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
42 changes: 32 additions & 10 deletions packages/release-please-ai/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,32 @@ async function main(): Promise<void> {
registerAiChangelogNotes();

const github = await GitHub.create({ owner, repo, token, defaultBranch: targetBranch });
const manifest = await Manifest.fromManifest(github, targetBranch, configFile, manifestFile);
const result = await runReleasePlease(() =>
Manifest.fromManifest(github, targetBranch, configFile, manifestFile),
);

const pullRequests = (await manifest.createPullRequests()).filter(Boolean);
const releases = (await manifest.createReleases()).filter((r): r is CreatedRelease => Boolean(r));
emitOutputs(result.pullRequestCount, result.releases);
}

emitOutputs(pullRequests.length, releases);
interface ReleasePleaseManifest {
createReleases(): ReturnType<Manifest["createReleases"]>;
createPullRequests(): ReturnType<Manifest["createPullRequests"]>;
}

export async function runReleasePlease(
loadManifest: () => Promise<ReleasePleaseManifest>,
): Promise<{ pullRequestCount: number; releases: CreatedRelease[] }> {
const releaseManifest = await loadManifest();
const releases = (await releaseManifest.createReleases()).filter(
(release): release is CreatedRelease => Boolean(release),
);

// Reload after tagging merged releases. Otherwise createPullRequests sees the
// just-merged release PR as untagged and aborts before refreshing sibling PRs.
const pullRequestManifest = await loadManifest();
const pullRequests = (await pullRequestManifest.createPullRequests()).filter(Boolean);

return { pullRequestCount: pullRequests.length, releases };
}

/** Write GitHub Actions step outputs (mirrors googleapis/release-please-action). */
Expand All @@ -62,9 +82,11 @@ function emitOutputs(prCount: number, releases: CreatedRelease[]): void {
);
}

main().catch((error: unknown) => {
process.stderr.write(
`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`,
);
process.exitCode = 1;
});
if (process.env.NODE_ENV !== "test") {
main().catch((error: unknown) => {
process.stderr.write(
`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`,
);
process.exitCode = 1;
});
}
38 changes: 38 additions & 0 deletions packages/release-please-ai/test/unit/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { CreatedRelease } from "release-please";
import { describe, expect, it, vi } from "vitest";
import { runReleasePlease } from "../../src/cli.js";

describe("runReleasePlease", () => {
it("creates releases before reloading and refreshing release pull requests", async () => {
const calls: string[] = [];
const release = { path: "packages/agent" } as CreatedRelease;
const createReleases = vi.fn(async () => {
calls.push("createReleases");
return [release, undefined];
});
const createPullRequests = vi.fn(async () => {
calls.push("createPullRequests");
return [{ number: 27 }, undefined];
});
const loadManifest = vi
.fn()
.mockImplementationOnce(async () => ({
createReleases,
createPullRequests: vi.fn(() => {
throw new Error("first manifest must not create pull requests");
}),
}))
.mockImplementationOnce(async () => ({
createReleases: vi.fn(() => {
throw new Error("second manifest must not create releases");
}),
createPullRequests,
}));

const result = await runReleasePlease(loadManifest);

expect(calls).toEqual(["createReleases", "createPullRequests"]);
expect(loadManifest).toHaveBeenCalledTimes(2);
expect(result).toEqual({ pullRequestCount: 1, releases: [release] });
});
});