diff --git a/.github/workflows/label-pr-review-state.yml b/.github/workflows/label-pr-review-state.yml index 24646147f4..459dcf50bd 100644 --- a/.github/workflows/label-pr-review-state.yml +++ b/.github/workflows/label-pr-review-state.yml @@ -319,6 +319,7 @@ jobs: const communityApprovedLabel = 'community-approved'; const reviewGateName = 'Zoo Code / PR review gate'; const reconciliationCheckName = 'Zoo Code / reconcile PR review state'; + const workflowRunUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`; if (context.eventName === 'issue_comment' && (!context.payload.issue?.pull_request || @@ -656,8 +657,7 @@ jobs: } if (!lookupSucceeded && passed) return; if (latestGateStatus?.state === state && - latestGateStatus.description === description && - latestGateStatus.target_url === pr.html_url) { + latestGateStatus.description === description) { return; } const mustInvalidateSuccess = !passed && @@ -671,7 +671,7 @@ jobs: state, context: reviewGateName, description, - target_url: pr.html_url, + target_url: workflowRunUrl, }); } catch (error) { if (required || mustInvalidateSuccess) throw error; diff --git a/src/services/__tests__/pr-review-state-workflow.test.ts b/src/services/__tests__/pr-review-state-workflow.test.ts index e4be567420..af8ad70f3d 100644 --- a/src/services/__tests__/pr-review-state-workflow.test.ts +++ b/src/services/__tests__/pr-review-state-workflow.test.ts @@ -17,6 +17,7 @@ const resolveScript = workflow.jobs.resolve.steps[0].with.script as string const SHA = "a".repeat(40) const OLD_SHA = "b".repeat(40) const REVIEWED_AT = Date.parse("2026-08-29T15:02:00Z") +const WORKFLOW_RUN_URL = "https://github.com/Zoo-Code-Org/Zoo-Code/actions/runs/123456" type ReviewState = "APPROVED" | "CHANGES_REQUESTED" | "COMMENTED" | "DISMISSED" @@ -492,6 +493,8 @@ function buildReconcileHarness(options: HarnessOptions = {}) { const context = { eventName, repo: { owner: "Zoo-Code-Org", repo: "Zoo-Code" }, + runId: 123456, + serverUrl: "https://github.com", payload, runNumber: options.runNumber ?? 1, } @@ -1935,7 +1938,12 @@ describe("PR review-state workflow", () => { const result = await runWorkflow() expect(latestGateStatus(result)).toEqual( - expect.objectContaining({ context: "Zoo Code / PR review gate", sha: SHA, state: "pending" }), + expect.objectContaining({ + context: "Zoo Code / PR review gate", + sha: SHA, + state: "pending", + target_url: WORKFLOW_RUN_URL, + }), ) }) @@ -1955,6 +1963,27 @@ describe("PR review-state workflow", () => { expect(result.addLabels).toHaveBeenCalledWith(expect.objectContaining({ labels: ["awaiting-coderabbit"] })) }) + it("publishes a changed review gate status with the workflow run URL", async () => { + const result = await runWorkflow({ + gateStatuses: [ + { + context: "Zoo Code / PR review gate", + state: "pending", + description: "Wait for the required CI checks to finish.", + targetUrl: "https://github.com/Zoo-Code-Org/Zoo-Code/pull/1437", + }, + ], + }) + + expect(latestGateStatus(result)).toEqual( + expect.objectContaining({ + state: "pending", + description: "Required CI passed. Waiting for automated review of the latest commit.", + target_url: WORKFLOW_RUN_URL, + }), + ) + }) + it("continues metadata reconciliation when gate publication fails", async () => { const result = await runWorkflow({ createCommitStatusErrorStatus: 500 })