From ef2c0a2b7bbc4b731168017cdc515f62a8cf2229 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 17 Jun 2026 14:24:57 -0400 Subject: [PATCH] fix(#1389): exempt auto-backmerge PRs from the Require Issue Link gate (#1391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The required "Issue link required" check failed on automated back-merge PRs (chore/backmerge-main-to-next-), which legitimately map to no issue — a `Closes #N` would pollute the released CHANGELOG. When such a PR parks for manual review (needs_review), the maintainer could only merge via --admin. Carve them out at the failing step's `if:` (step-level, so the required check still reports SUCCESS rather than a branch-protection-blocking "skipped"), keyed on the workflow-authored branch name AND same-repo identity so a fork PR cannot forge the exemption. Co-authored-by: Claude Opus 4.8 --- .github/workflows/require-issue-link.yml | 11 ++++++++++- tests/workflow-maintainer-skip.test.cjs | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/require-issue-link.yml b/.github/workflows/require-issue-link.yml index 129093da0..146f5e9f1 100644 --- a/.github/workflows/require-issue-link.yml +++ b/.github/workflows/require-issue-link.yml @@ -29,7 +29,16 @@ jobs: fi - name: Comment and fail if no issue link - if: steps.check.outputs.found == 'false' + # Exempt auto-backmerge PRs (chore/backmerge-main-to-next-*): they map to + # no issue and a `Closes #N` would pollute the released CHANGELOG. Keyed on + # the workflow-authored branch name AND same-repo identity so a fork PR + # cannot forge the exemption. Step-level (not job-level) so the required + # "Issue link required" check still reports SUCCESS rather than a + # branch-protection-blocking "skipped". See #1389. + if: >- + steps.check.outputs.found == 'false' && + !(startsWith(github.head_ref, 'chore/backmerge-main-to-next-') && + github.event.pull_request.head.repo.full_name == github.repository) uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: # Uses GitHub API SDK — no shell string interpolation of untrusted input diff --git a/tests/workflow-maintainer-skip.test.cjs b/tests/workflow-maintainer-skip.test.cjs index 3f91a6c6d..8141e7855 100644 --- a/tests/workflow-maintainer-skip.test.cjs +++ b/tests/workflow-maintainer-skip.test.cjs @@ -79,3 +79,27 @@ describe('PR policy workflow maintainer carve-outs', () => { assert.match(workflow, /CONTRIBUTING\.md#pull-request-guidelines/); }); }); + +describe('Require Issue Link back-merge automation carve-out', () => { + test('the fail step is skipped for same-repo auto-backmerge PRs', () => { + const workflow = readWorkflow('.github/workflows/require-issue-link.yml'); + + // Auto-backmerge PRs (chore/backmerge-main-to-next-*) map to no issue, and a + // `Closes #N` would pollute the released CHANGELOG. The fail step must carve + // them out — keyed on the workflow-authored branch name AND same-repo + // identity so a fork PR cannot forge the exemption (#1389). + assert.match( + workflow, + /startsWith\(github\.head_ref, 'chore\/backmerge-main-to-next-'\)/ + ); + assert.match( + workflow, + /github\.event\.pull_request\.head\.repo\.full_name == github\.repository/ + ); + + // The carve-out must live on the failing step's `if:` alongside the + // found=='false' check (step-level, so the required check still reports + // SUCCESS rather than a branch-protection-blocking "skipped"). + assert.match(workflow, /steps\.check\.outputs\.found == 'false'/); + }); +});