Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-lions-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---

Validate that projects use Changesets CLI v3 and direct Changesets CLI v2 users to `changesets/action@v1`.
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import {
getOptionalInput,
getRequiredInput,
throwOnRenamedInputs,
validateChangesetsCliVersion,
} from "./utils.ts";

(async () => {
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
await validateChangesetsCliVersion(cwd);

throwOnRenamedInputs({
publish: "publish-script",
version: "version-script",
Expand All @@ -32,9 +37,6 @@ import {
);
}

// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();

const commitMode = getOptionalInput("commit-mode") ?? "git-cli";
const prDraft = getOptionalInput("pr-draft");
if (commitMode !== "git-cli" && commitMode !== "github-api") {
Expand Down
7 changes: 5 additions & 2 deletions src/pack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
downloadArtifact,
execChangesetsCli,
getOptionalInput,
validateChangesetsCliVersion,
} from "../utils.ts";

try {
Expand All @@ -16,10 +17,12 @@ try {
}

async function main() {
const publishPlanArtifactId = getOptionalInput("publish-plan-artifact-id");

// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
await validateChangesetsCliVersion(cwd);

const publishPlanArtifactId = getOptionalInput("publish-plan-artifact-id");

const tmpDir = process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir()));
const outDir = path.join(tmpDir, `changeset-pack-${Date.now()}`);

Expand Down
8 changes: 5 additions & 3 deletions src/publish/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
downloadArtifact,
getOptionalInput,
getRequiredInput,
validateChangesetsCliVersion,
} from "../utils.ts";

try {
Expand All @@ -16,6 +17,10 @@ try {
}

async function main() {
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
await validateChangesetsCliVersion(cwd);

const githubToken = getRequiredInput("github-token");
const script = getOptionalInput("script");
const packDirArtifactId = getOptionalInput("pack-dir-artifact-id");
Expand All @@ -30,9 +35,6 @@ async function main() {
);
}

// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();

// NOTE: Always use API mode here as publish does not need a commit-mode.
const github = new GitHub({ cwd, githubToken, commitMode: "github-api" });

Expand Down
8 changes: 3 additions & 5 deletions src/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ vi.mock("@actions/github", () => ({
graphql: mockedGraphql,
}),
}));
vi.mock("@actions/core", async (importOriginal) => ({
...(await importOriginal<typeof import("@actions/core")>()),
warning: vi.fn(),
}));
vi.mock("@actions/core", { spy: true });
const mockedWarning = vi.mocked(core.warning);
vi.mock("@changesets/ghcommit");

let mockedGithubMethods = {
Expand Down Expand Up @@ -126,7 +124,7 @@ describe("publish", () => {
});

expect(result).toEqual({ published: false, exitCode: 0 });
expect(core.warning).toHaveBeenCalledWith(
expect(mockedWarning).toHaveBeenCalledWith(
expect.stringContaining(
"GitHub releases and git tags cannot be created without this output",
),
Expand Down
12 changes: 7 additions & 5 deletions src/select-mode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from "node:path";
import artifact from "@actions/artifact";
import * as core from "@actions/core";
import readChangesetState from "../readChangesetState.ts";
import { execChangesetsCli } from "../utils.ts";
import { execChangesetsCli, validateChangesetsCliVersion } from "../utils.ts";

type ModeResult =
| {
Expand All @@ -27,7 +27,10 @@ try {
}

async function main() {
const result = await getMode();
const cwd = process.cwd();
await validateChangesetsCliVersion(cwd);

const result = await getMode(cwd);
core.setOutput("mode", result.mode);
if (result.mode === "publish") {
const publishPlanArtifact = await artifact.uploadArtifact(
Expand All @@ -48,8 +51,8 @@ async function main() {
}
}

async function getMode(): Promise<ModeResult> {
const { changesets } = await readChangesetState();
async function getMode(cwd: string): Promise<ModeResult> {
const { changesets } = await readChangesetState(cwd);

if (changesets.length > 0) {
const hasNonEmptyChangesets = changesets.some(
Expand All @@ -61,7 +64,6 @@ async function getMode(): Promise<ModeResult> {
return { mode: "none" };
}

const cwd = process.cwd();
const publishPlanPath = path.join(
process.env.RUNNER_TEMP ?? (await fs.realpath(os.tmpdir())),
`changeset-publish-plan-${Date.now()}`,
Expand Down
54 changes: 53 additions & 1 deletion src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { createFixture } from "fs-fixture";
import { expect, test } from "vitest";
import { BumpLevels, getChangelogEntry, sortTheThings } from "./utils.ts";
import {
BumpLevels,
getChangelogEntry,
sortTheThings,
validateChangesetsCliVersion,
} from "./utils.ts";

let changelog = `# @keystone-alpha/email

Expand Down Expand Up @@ -100,3 +106,49 @@ test("it sorts the things right", () => {
];
expect(things.sort(sortTheThings)).toMatchSnapshot();
});

test("throws when the project declares Changesets CLI v2", async () => {
await using fixture = await createFixture({
"package.json": JSON.stringify({
name: "project",
devDependencies: { "@changesets/cli": "^2.29.7" },
}),
"package-lock.json": "",
});

await expect(validateChangesetsCliVersion(fixture.path)).rejects.toThrow(
"This version of the Changesets action is designed to work with Changesets CLI v3. Changesets CLI v2 is not supported; use Changesets action v1 instead, which is compatible with CLI v2.",
);
});

test("accepts a Changesets CLI v3 contract without an installed CLI", async () => {
await using fixture = await createFixture({
"package.json": JSON.stringify({
name: "project",
devDependencies: { "@changesets/cli": "^3.0.0" },
}),
"package-lock.json": "",
});

await expect(
validateChangesetsCliVersion(fixture.path),
).resolves.toBeUndefined();
});

test("throws when the project has Changesets CLI v2 installed", async () => {
await using fixture = await createFixture({
"package.json": JSON.stringify({
name: "project",
devDependencies: { "@changesets/cli": "^3.0.0" },
}),
"package-lock.json": "",
"node_modules/@changesets/cli/package.json": JSON.stringify({
name: "@changesets/cli",
version: "2.29.7",
}),
});

await expect(validateChangesetsCliVersion(fixture.path)).rejects.toThrow(
"This version of the Changesets action is designed to work with Changesets CLI v3. Changesets CLI v2 is not supported; use Changesets action v1 instead, which is compatible with CLI v2.",
);
});
51 changes: 51 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
type ExecOutput,
} from "@actions/exec";
import { getPackages, type Package } from "@manypkg/get-packages";
import major from "semver/functions/major.js";
import subset from "semver/ranges/subset.js";

const require = createRequire(import.meta.url);

Expand Down Expand Up @@ -154,6 +156,55 @@ export function throwOnRenamedInputs(renames: Record<string, string>) {
}
}

const changesetsCliCompatibilityError =
"This version of the Changesets action is designed to work with Changesets CLI v3. " +
"Changesets CLI v2 is not supported; use Changesets action v1 instead, which is compatible with CLI v2.";

export async function validateChangesetsCliVersion(cwd: string) {
const { rootPackage } = await getPackages(cwd);
const packageJson = rootPackage?.packageJson;
const declaredVersion =
packageJson?.devDependencies?.["@changesets/cli"] ??
packageJson?.dependencies?.["@changesets/cli"];

if (typeof declaredVersion === "string") {
const range = declaredVersion.startsWith("workspace:")
? declaredVersion.slice("workspace:".length)
: declaredVersion;

let isV2 = false;

try {
isV2 = subset(range, ">=2.0.0-0 <3.0.0-0", {
includePrerelease: true,
});
} catch {
// it could be a non-semver protocol
}

if (isV2) {
throw new Error(changesetsCliCompatibilityError);
}
}

let cliPackageJson;

try {
cliPackageJson = require(
require.resolve("@changesets/cli/package.json", { paths: [cwd] }),
);
Comment thread
beeequeue marked this conversation as resolved.
} catch {
return;
}

if (
typeof cliPackageJson.version === "string" &&
major(cliPackageJson.version) === 2
) {
throw new Error(changesetsCliCompatibilityError);
}
}

function resolveChangesetsCli(cwd: string) {
return require.resolve("@changesets/cli/bin.js", {
paths: [cwd],
Expand Down
13 changes: 9 additions & 4 deletions src/version/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import * as core from "@actions/core";
import { GitHub } from "../github.ts";
import { runVersion } from "../run.ts";
import { getOptionalInput, getRequiredInput } from "../utils.ts";
import {
getOptionalInput,
getRequiredInput,
validateChangesetsCliVersion,
} from "../utils.ts";

try {
await main();
Expand All @@ -10,6 +14,10 @@ try {
}

async function main() {
// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();
await validateChangesetsCliVersion(cwd);

const githubToken = getRequiredInput("github-token");
const script = getOptionalInput("script");
const commitMessage = getRequiredInput("commit-message");
Expand All @@ -27,9 +35,6 @@ async function main() {
throw new Error(`Invalid commit-mode input: ${commitMode}`);
}

// If the user needs to change the cwd, set `working-directory` in the step instead
const cwd = process.cwd();

const github = new GitHub({
cwd,
githubToken,
Expand Down
Loading