From 49d682430ac8cbbba3f5627eb8b148b9db831d46 Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Tue, 30 Jun 2026 04:44:39 -0700 Subject: [PATCH] fix(orb): preserve actionable relay PR events --- src/github/webhook-coalesce.ts | 2 -- test/integration/orb-relay.test.ts | 38 +++++++++++++++++++++++ test/unit/github-webhook-coalesce.test.ts | 18 ++++++----- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/github/webhook-coalesce.ts b/src/github/webhook-coalesce.ts index 519b00867..d6ec0ddd0 100644 --- a/src/github/webhook-coalesce.ts +++ b/src/github/webhook-coalesce.ts @@ -5,8 +5,6 @@ const COALESCABLE_PULL_REQUEST_ACTIONS = new Set([ "synchronize", "edited", "ready_for_review", - "labeled", - "unlabeled", ]); export function githubWebhookCoalesceKey( diff --git a/test/integration/orb-relay.test.ts b/test/integration/orb-relay.test.ts index ad7a9b6f9..3093d52cf 100644 --- a/test/integration/orb-relay.test.ts +++ b/test/integration/orb-relay.test.ts @@ -551,6 +551,44 @@ describe("enqueueRelayPending", () => { expect(JSON.parse(rows.results[0]?.raw_body ?? "{}")).toMatchObject({ marker: "new" }); }); + it("does not let label-only PR events coalesce away pending gate-triggering PR events", async () => { + const e = brokeredEnv(); + const prBody = (action: string, marker: string) => + JSON.stringify({ + action, + repository: { full_name: "JSONbored/Gittensory" }, + pull_request: { number: 1629, head: { sha: "a".repeat(40) } }, + marker, + }); + + await enqueueRelayPending(e, { + deliveryId: "pr-opened-actionable", + installationId: 9608, + eventName: "pull_request", + rawBody: prBody("opened", "actionable"), + }); + await enqueueRelayPending(e, { + deliveryId: "pr-labeled-not-actionable", + installationId: 9608, + eventName: "pull_request", + rawBody: prBody("labeled", "label-only"), + }); + + const events = await pullRelayPending(e, 9608); + expect(events.map((event) => event.deliveryId).sort()).toEqual([ + "pr-labeled-not-actionable", + "pr-opened-actionable", + ]); + expect( + Object.fromEntries( + events.map((event) => [event.deliveryId, JSON.parse(event.rawBody).action]), + ), + ).toEqual({ + "pr-labeled-not-actionable": "labeled", + "pr-opened-actionable": "opened", + }); + }); + it("keeps exact duplicate coalescible delivery IDs idempotent", async () => { const e = brokeredEnv(); const body = (marker: string) => diff --git a/test/unit/github-webhook-coalesce.test.ts b/test/unit/github-webhook-coalesce.test.ts index 5501d9067..04d8935d3 100644 --- a/test/unit/github-webhook-coalesce.test.ts +++ b/test/unit/github-webhook-coalesce.test.ts @@ -24,7 +24,7 @@ describe("githubWebhookCoalesceKey", () => { ).toBe("github-webhook:ci-completed:jsonbored/gittensory@def5678"); }); - it("coalesces refresh-like pull request events and ignores terminal actions", () => { + it("coalesces gate-triggering pull request events and ignores non-actionable or terminal actions", () => { expect( githubWebhookCoalesceKey("pull_request", { action: "synchronize", @@ -40,13 +40,15 @@ describe("githubWebhookCoalesceKey", () => { pull_request: { number: 100, head: { sha: "BEEF123" } }, } as GitHubWebhookPayload), ).toBe("github-webhook:pr-refresh:jsonbored/gittensory#100@beef123"); - expect( - githubWebhookCoalesceKey("pull_request", { - action: "closed", - repository: { full_name: "JSONbored/Gittensory" }, - pull_request: { number: 100, head: { sha: "BEEF123" } }, - } as GitHubWebhookPayload), - ).toBeNull(); + for (const action of ["labeled", "unlabeled", "closed"]) { + expect( + githubWebhookCoalesceKey("pull_request", { + action, + repository: { full_name: "JSONbored/Gittensory" }, + pull_request: { number: 100, head: { sha: "BEEF123" } }, + } as GitHubWebhookPayload), + ).toBeNull(); + } }); it("returns null for malformed or non-coalescible webhook shapes", () => {