From 49ae2b2a4bc7002b7e37ae89afa863cf8dd229af Mon Sep 17 00:00:00 2001 From: wenshao Date: Tue, 18 Aug 2026 08:14:42 +0800 Subject: [PATCH] fix(ci): gate the Windows lane's checkout verification per trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first thing the revived triggers hit was not a test failure but the lane's own plumbing. `test_windows` verifies its checkout with `verify-checkout-head`, and the input was written when this lane ran in the merge queue alone: `expected_sha: github.event.merge_group.head_sha`, with no event gate. On a pull request that expression is empty, the action refuses an empty SHA, and the lane went red in 63 seconds without running a test — the first Windows run in six weeks, failing on the trigger rather than on the code. Give it the event-aware shape the Ubuntu gate already uses, and skip it where there is nothing to verify: the scheduled and dispatch runs check out a branch by name, not a head commit. Pinned generally rather than by name: for both lanes, any step whose inputs read a `github.event.` context must be gated to that event, in the step's own `if` or in the expression itself. Restoring the old spelling turns that test red. --- .github/workflows/ci.yml | 10 +++++++-- scripts/tests/ci-platform-lanes.test.js | 29 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1221f8802..2da6f7d310 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1012,11 +1012,17 @@ jobs: # Same stale-checkout guard as the Ubuntu gate: this job now runs on ECS, # so fail loud if the checkout lacks the merge-queue head rather than # silently testing the wrong tree into a merge. + # Written when this lane ran in the merge queue alone, so its expected + # SHA named only the queue's event: on any other trigger the input is + # empty and the step fails the whole lane before a single test runs. + # That is what the revived triggers hit first. Same event-aware shape as + # the Ubuntu gate now, and skipped where there is no head to verify — + # the scheduled and dispatch runs check out a branch by name. - name: 'Verify checkout includes expected head commit' - if: "${{ needs.classify_pr.outputs.skip_ci != 'true' }}" + if: "${{ needs.classify_pr.outputs.skip_ci != 'true' && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }}" uses: './.github/actions/verify-checkout-head' with: - expected_sha: '${{ github.event.merge_group.head_sha }}' + expected_sha: "${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || github.event.pull_request.head.sha }}" # Avoid setup-node downloads on ECS, where nodejs.org may be unreachable # through the egress proxy; reuse the machine's Node instead. diff --git a/scripts/tests/ci-platform-lanes.test.js b/scripts/tests/ci-platform-lanes.test.js index 0d2af40c56..4ebe415d12 100644 --- a/scripts/tests/ci-platform-lanes.test.js +++ b/scripts/tests/ci-platform-lanes.test.js @@ -64,6 +64,35 @@ describe('platform lanes — triggers', () => { }); } + for (const lane of LANES) { + it(`${lane}'s steps are gated for every trigger it now has`, () => { + // The first thing the revived triggers hit was not a test failure but + // the lane's own plumbing: a `verify-checkout-head` step written when + // this lane ran in the merge queue alone, with `expected_sha` naming + // only `github.event.merge_group.head_sha`. On a pull request that + // input is empty and the step fails the lane before a single test + // runs. A step whose inputs name one event must be gated to that + // event — for every step in a job that now runs on four. + for (const step of ci.jobs[lane].steps ?? []) { + const inputs = JSON.stringify(step.with ?? {}); + const gate = String(step.if ?? ''); + for (const [context, event] of [ + ['github.event.merge_group', "'merge_group'"], + ['github.event.pull_request', "'pull_request'"], + ]) { + if (!inputs.includes(context)) continue; + const guarded = + inputs.includes(`github.event_name == ${event}`) || + gate.includes(`github.event_name == ${event}`); + expect( + guarded, + `${lane} step "${step.name}" reads ${context} on every trigger`, + ).toBe(true); + } + } + }); + } + it('keeps a nightly run to exactly the two lanes', () => { // A `schedule:` trigger fires the whole workflow. Every other job must // therefore either exclude `schedule` outright or gate on an event