From bef726ee4c22e85458db3a4cf52f294a3569435d Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Tue, 14 Jul 2026 10:05:08 +0000 Subject: [PATCH] fix: refresh sibling release pull requests Change-Id: I73c1c03c598c92dfa91eea372fac1156f02c1a01 Signed-off-by: Thomas Kosiewski --- packages/release-please-ai/src/cli.ts | 42 ++++++++++++++----- .../release-please-ai/test/unit/cli.test.ts | 38 +++++++++++++++++ 2 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 packages/release-please-ai/test/unit/cli.test.ts diff --git a/packages/release-please-ai/src/cli.ts b/packages/release-please-ai/src/cli.ts index 2b91bdb..84128a1 100644 --- a/packages/release-please-ai/src/cli.ts +++ b/packages/release-please-ai/src/cli.ts @@ -33,12 +33,32 @@ async function main(): Promise { 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; + createPullRequests(): ReturnType; +} + +export async function runReleasePlease( + loadManifest: () => Promise, +): 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). */ @@ -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; + }); +} diff --git a/packages/release-please-ai/test/unit/cli.test.ts b/packages/release-please-ai/test/unit/cli.test.ts new file mode 100644 index 0000000..c046c6c --- /dev/null +++ b/packages/release-please-ai/test/unit/cli.test.ts @@ -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] }); + }); +});