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/calm-files-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/action": patch
---

Allow custom publish scripts to complete without a Changesets output file, warning that GitHub releases and git tags cannot be created when that file is missing.
62 changes: 60 additions & 2 deletions src/run.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import path from "node:path";
import * as core from "@actions/core";
import type { Changeset } from "@changesets/types";
import { writeChangeset } from "@changesets/write";
import { createFixture } from "fs-fixture";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { GitHub } from "./github.ts";
import { runVersion } from "./run.ts";
import { runPublish, runVersion } from "./run.ts";

vi.mock("@actions/github", () => ({
context: {
Expand All @@ -20,6 +21,10 @@ vi.mock("@actions/github", () => ({
graphql: mockedGraphql,
}),
}));
vi.mock("@actions/core", async (importOriginal) => ({
...(await importOriginal<typeof import("@actions/core")>()),
warning: vi.fn(),
}));
Comment on lines +24 to +27

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could do this instead, which is cleaner imo

vi.mock("@actions/core", { spy: true })
const mockedWarning = vi.mocked(core.warning)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, i’ll update this

vi.mock("@changesets/ghcommit");

let mockedGithubMethods = {
Expand Down Expand Up @@ -102,6 +107,59 @@ beforeEach(() => {
vi.clearAllMocks();
});

afterEach(() => {
vi.unstubAllEnvs();
});

describe("publish", () => {
it("warns when a custom publish script does not create the output file", async () => {
await using fixture = await createSimpleProjectFixture();
const cwd = fixture.path;
vi.stubEnv("RUNNER_TEMP", cwd);

const result = await runPublish({
script: 'node -e "void 0"',
github: createGithub(cwd),
createGithubReleases: true,
pushGitTags: true,
cwd,
});

expect(result).toEqual({ published: false, exitCode: 0 });
expect(core.warning).toHaveBeenCalledWith(
expect.stringContaining(
"GitHub releases and git tags cannot be created without this output",
),
);
});

it("throws when the built-in publish command does not create the output file", async () => {
await using fixture = await createFixture({
"node_modules/@changesets/cli/package.json": JSON.stringify({
name: "@changesets/cli",
type: "module",
}),
"node_modules/@changesets/cli/bin.js": "",
"package.json": JSON.stringify({
name: "simple-project",
version: "1.0.0",
}),
"package-lock.json": "",
});
const cwd = fixture.path;
vi.stubEnv("RUNNER_TEMP", cwd);

await expect(
runPublish({
github: createGithub(cwd),
createGithubReleases: true,
pushGitTags: true,
cwd,
}),
).rejects.toThrow("Failed to read changesets output at");
});
});

describe("version", () => {
it("creates simple PR", async () => {
await using fixture = await createSimpleProjectFixture();
Expand Down
22 changes: 18 additions & 4 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ type ChangesetsOutputEvent = {
packageName: string;
};

class ChangesetsOutputReadError extends Error {}

type PublishResult =
| {
published: true;
Expand Down Expand Up @@ -113,9 +115,10 @@ async function readChangesetsOutput(outputPath: string) {
try {
rawOutput = await fs.readFile(outputPath, "utf8");
} catch (err) {
throw new Error(`Failed to read changesets output at ${outputPath}`, {
cause: err,
});
throw new ChangesetsOutputReadError(
`Failed to read changesets output at ${outputPath}`,
{ cause: err },
);
}

const events: ChangesetsOutputEvent[] = [];
Expand Down Expand Up @@ -195,7 +198,18 @@ export async function runPublish({

let { packages, tool } = await getPackages(cwd);
let packagesByName = new Map(packages.map((x) => [x.packageJson.name, x]));
let output = await readChangesetsOutput(outputFile);
let output: ChangesetsOutputEvent[];
try {
output = await readChangesetsOutput(outputFile);
} catch (err) {
if (!script || !(err instanceof ChangesetsOutputReadError)) {
throw err;
}
core.warning(
`${err.message}. GitHub releases and git tags cannot be created without this output. Ensure the custom publish script passes CHANGESETS_OUTPUT to the Changesets CLI.`,
);
output = [];
}
let releases = output.map((event) => {
let pkg = packagesByName.get(event.packageName);
if (pkg === undefined) {
Expand Down