diff --git a/.github/workflows/dev-version-bump.yml b/.github/workflows/dev-version-bump.yml index b884b04ace..a29b29d067 100644 --- a/.github/workflows/dev-version-bump.yml +++ b/.github/workflows/dev-version-bump.yml @@ -119,7 +119,17 @@ jobs: # leaves the branch check passing, so the job would recreate the branch and then # fail on `gh pr create` with "already exists" — turning a successful release red # for a repair that was already queued. - open_prs="$(gh pr list --base dev --head "${branch}" --state open --json number --jq 'length')" + # Apply the repository owner and branch filter on the server. Filtering a + # paginated `gh pr list` result locally can miss this repository's pull request + # when newer same-named fork pull requests fill the fetched page. + open_prs="$( + gh api --method GET "repos/${GITHUB_REPOSITORY}/pulls" \ + -f state=open \ + -f base=dev \ + -f "head=${GITHUB_REPOSITORY_OWNER}:${branch}" \ + -F per_page=1 \ + --jq 'length' + )" if [ "${open_prs}" != "0" ]; then echo "::notice::a bump pull request for ${branch} is already open; nothing to do" exit 0 diff --git a/tests/bump-dev-version.test.ts b/tests/bump-dev-version.test.ts index 79d5388496..9e9630a373 100644 --- a/tests/bump-dev-version.test.ts +++ b/tests/bump-dev-version.test.ts @@ -17,6 +17,7 @@ import { decideDevVersion } from "../scripts/bump-dev-version"; // which bun cannot open, so every CLI case exited 1 before reaching the code under test — // and the malformed-input case read that same load failure as a correct rejection. const CLI = fileURLToPath(new URL("../scripts/bump-dev-version.ts", import.meta.url)); +const WORKFLOW = fileURLToPath(new URL("../.github/workflows/dev-version-bump.yml", import.meta.url)); function runCli(...args: string[]) { const proc = Bun.spawnSync([process.execPath, CLI, ...args]); @@ -41,6 +42,20 @@ function tempPackageJson(version: string): string { } describe("dev version bump rule", () => { + test("the idempotency check filters the repository-owned head before pagination", () => { + const workflow = readFileSync(WORKFLOW, "utf8"); + const block = workflow.match(/open_prs="\$\(([\s\S]*?)\n\s*\)"/)?.[1]; + expect(block).toBeDefined(); + expect(block).toContain('gh api --method GET "repos/${GITHUB_REPOSITORY}/pulls"'); + expect(block).toContain("-f state=open"); + expect(block).toContain("-f base=dev"); + expect(block).toContain('-f "head=${GITHUB_REPOSITORY_OWNER}:${branch}"'); + expect(block).toContain("-F per_page=1"); + expect(block).toContain("--jq 'length'"); + expect(block).not.toContain("gh pr list"); + expect(block).not.toContain("isCrossRepository"); + }); + test("a stable release moves dev to the next minor", () => { // e4a85d134 (2.33.0 -> 2.34.0) and 076ad3036 (2.34.0 -> 2.35.0). expect(decideDevVersion("2.36.0", "2.36.0")).toMatchObject({ changed: true, version: "2.37.0" });