Skip to content

Commit

Permalink
Tag private packages when their version changes
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeGinnivan committed Feb 8, 2022
1 parent d02f2b8 commit 1dc6b87
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-jars-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/cli": minor
---

Private packages will now be tagged in the same way public packages do when they are published to npm
5 changes: 5 additions & 0 deletions .changeset/khaki-kangaroos-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/git": minor
---

Add tagExists git helper
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ git.tag.mockImplementation(() => Promise.resolve(true));

// @ts-ignore
publishPackages.mockImplementation(() =>
Promise.resolve([
{ name: "pkg-a", newVersion: "1.1.0", published: true },
{ name: "pkg-b", newVersion: "1.0.1", published: true }
])
Promise.resolve({
publishedPackages: [
{ name: "pkg-a", newVersion: "1.1.0", published: true },
{ name: "pkg-b", newVersion: "1.0.1", published: true }
],
untaggedPrivatePackages: [
{ name: "project-a", newVersion: "2.0.5", published: true }
]
})
);

describe("running release", () => {
Expand Down
40 changes: 31 additions & 9 deletions packages/cli/src/commands/publish/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,36 +66,58 @@ export default async function run(
tag: releaseTag
});

const successful = response.filter(p => p.published);
const unsuccessful = response.filter(p => !p.published);
const successfulNpmPublishes = response.publishedPackages.filter(
p => p.published
);
const unsuccessfulNpmPublishes = response.publishedPackages.filter(
p => !p.published
);
const untaggedPrivatePackages = response.untaggedPrivatePackages;

if (successful.length > 0) {
if (successfulNpmPublishes.length > 0) {
success("packages published successfully:");
logReleases(successful);
logReleases(successfulNpmPublishes);

if (gitTag) {
// We create the tags after the push above so that we know that HEAD won't change and that pushing
// won't suffer from a race condition if another merge happens in the mean time (pushing tags won't
// fail if we are behind the base branch).
log(`Creating git tag${successful.length > 1 ? "s" : ""}...`);
log(`Creating git tag${successfulNpmPublishes.length > 1 ? "s" : ""}...`);
if (tool !== "root") {
for (const pkg of successful) {
for (const pkg of successfulNpmPublishes) {
const tag = `${pkg.name}@${pkg.newVersion}`;
log("New tag: ", tag);
await git.tag(tag, cwd);
}
} else {
const tag = `v${successful[0].newVersion}`;
const tag = `v${successfulNpmPublishes[0].newVersion}`;
log("New tag: ", tag);
await git.tag(tag, cwd);
}
}
}

if (unsuccessful.length > 0) {
if (untaggedPrivatePackages.length > 0) {
success("found untagged projects:");
logReleases(untaggedPrivatePackages);

if (tool !== "root") {
for (const pkg of untaggedPrivatePackages) {
const tag = `${pkg.name}@${pkg.newVersion}`;
log("New tag: ", tag);
await git.tag(tag, cwd);
}
} else {
const tag = `v${untaggedPrivatePackages[0].newVersion}`;
log("New tag: ", tag);
await git.tag(tag, cwd);
}
}

if (unsuccessfulNpmPublishes.length > 0) {
error("packages failed to publish:");

logReleases(unsuccessful);
logReleases(unsuccessfulNpmPublishes);
throw new ExitError(1);
}
}
59 changes: 53 additions & 6 deletions packages/cli/src/commands/publish/publishPackages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { join } from "path";
import semver from "semver";
import chalk from "chalk";
import * as git from "@changesets/git";
import { AccessType } from "@changesets/types";
import { Package } from "@manypkg/get-packages";
import { info, warn } from "@changesets/logger";
Expand Down Expand Up @@ -85,9 +86,13 @@ export default async function publishPackages({
otp?: string;
preState: PreState | undefined;
tag?: string;
}) {
}): Promise<{
publishedPackages: PublishedResult[];
untaggedPrivatePackages: PublishedResult[];
}> {
const packagesByName = new Map(packages.map(x => [x.packageJson.name, x]));
const publicPackages = packages.filter(pkg => !pkg.packageJson.private);
const privatePackages = packages.filter(pkg => pkg.packageJson.private);
const twoFactorState: TwoFactorState = getTwoFactorState({
otp,
publicPackages
Expand All @@ -97,11 +102,7 @@ export default async function publishPackages({
preState
);

if (unpublishedPackagesInfo.length === 0) {
warn("No unpublished packages to publish");
}

return Promise.all(
const npmPackagePublish = Promise.all(
unpublishedPackagesInfo.map(pkgInfo => {
let pkg = packagesByName.get(pkgInfo.name)!;
return publishAPackage(
Expand All @@ -112,6 +113,52 @@ export default async function publishPackages({
);
})
);

const untaggedPrivatePackageReleases = getUntaggedPrivatePackages(
privatePackages
);

const result: {
publishedPackages: PublishedResult[];
untaggedPrivatePackages: PublishedResult[];
} = {
publishedPackages: await npmPackagePublish,
untaggedPrivatePackages: await untaggedPrivatePackageReleases
};

if (
result.publishedPackages.length === 0 &&
result.untaggedPrivatePackages.length === 0
) {
warn("No unpublished projects to publish");
}

return result;
}

async function getUntaggedPrivatePackages(privatePackages: Package[]) {
const packageWithTags = await Promise.all(
privatePackages.map(async privatePkg => {
const tagName = `${privatePkg.packageJson.name}@${privatePkg.packageJson.version}`;
const isMissingTag = !(await git.tagExists(tagName));

return { pkg: privatePkg, isMissingTag };
})
);

const untagged: PublishedResult[] = [];

for (const packageWithTag of packageWithTags) {
if (packageWithTag.isMissingTag) {
untagged.push({
name: packageWithTag.pkg.packageJson.name,
newVersion: packageWithTag.pkg.packageJson.version,
published: false
});
}
}

return untagged;
}

async function publishAPackage(
Expand Down
20 changes: 19 additions & 1 deletion packages/git/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
getDivergedCommit,
getChangedPackagesSinceRef,
getChangedChangesetFilesSinceRef,
getAllTags
getAllTags,
tagExists
} from "./";

const f = fixtures(__dirname);
Expand Down Expand Up @@ -226,6 +227,23 @@ describe("git", () => {
});
});

describe("tagExists", () => {
beforeEach(async () => {
await add("packages/pkg-a/package.json", cwd);
await commit("added packageA package.json", cwd);
});

it("returns false when no tag exists", async () => {
expect(await tagExists("tag_name")).toBe(false);
});

it("should create a tag for the current head", async () => {
await tag("tag_message", cwd);

expect(await tagExists("tag_name")).toBe(false);
});
});

describe("getCommitsThatAddFiles", () => {
it("should commit a file and get the hash of that commit", async () => {
await add("packages/pkg-b/package.json", cwd);
Expand Down
8 changes: 8 additions & 0 deletions packages/git/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,11 @@ export async function getChangedPackagesSinceRef({
.filter((pkg, idx, packages) => packages.indexOf(pkg) === idx)
);
}
async function tagExists(tagStr: string) {
const gitCmd = await spawn("git", ["tag", "-l", tagStr]);
const output = gitCmd.stdout.toString().trim();
const tagExists = !!output;
return tagExists;
}

tagExists,

0 comments on commit 1dc6b87

Please sign in to comment.