From 0670584f78e1ade501b82eabae9726fe994ba4e1 Mon Sep 17 00:00:00 2001 From: JUN Date: Wed, 21 Oct 2026 23:08:38 +0900 Subject: [PATCH 1/2] ci(windows): run the Windows shards on Windows-sensitive pull requests again #7052 dropped the pull_request clause from platform-windows, so the changes job's windows=true verdict was computed and ignored; with dev absent from the push trigger, Windows ran only nightly. #7468 landed green that way and broke windows 9/9 on dev. Restore the clause, mirror it in the aggregate, add a spawnsync marker, and correct the comment. Ordinary PRs keep the Ubuntu subset. --- .github/workflows/ci.yml | 16 ++++++++++++---- scripts/ci/windows-sensitive-diff.sh | 1 + tests/ci-workflows/ci-review-lanes.test.ts | 15 ++++++++++----- tests/ci-workflows/ci-workflows.test.ts | 9 +++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2df7ddab928..5c26c7147ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -999,15 +999,20 @@ jobs: # # Dispatch-only proved too late. #6639, #6556, #6605 and #6606 each landed with # green pull-request CI while breaking Windows, and the breakage surfaced days - # later on a manual dispatch. So a pull request whose changes job reports + # later on a manual dispatch. #6670 made a pull request whose changes job reports # windows=true (a Windows path, or an added src/tests line naming a Windows - # marker; see scripts/ci/windows-sensitive-diff.sh) now runs the nine shards, - # and the daily schedule catches whatever that filter misses. + # marker; see scripts/ci/windows-sensitive-diff.sh) run the nine shards. #7052 + # then dropped that clause to keep pull requests on the fast Ubuntu subset, and + # since dev is absent from the push trigger, Windows ran only nightly or on a + # lane=all dispatch. #7468 landed green that way and broke windows 9/9 on dev. + # The clause is back for Windows-sensitive pull requests only: every other pull + # request still runs the Ubuntu subset, and the nightly schedule still catches + # whatever the selector misses. The aggregate below mirrors this condition. platform-windows: name: windows ${{ matrix.shard }}/9 needs: [changes, select-windows-runner] if: >- - github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') + github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') || github.event_name == 'pull_request' && needs.changes.outputs.windows == 'true' runs-on: ${{ fromJSON(needs.select-windows-runner.outputs.runner) }} # Sharded like the Linux legs. The single-leg run reached 30 minutes on a # green suite and was killed in cleanup; four shards put each leg inside the @@ -1805,6 +1810,9 @@ jobs: if [ -z "$LANE" ] || [ "$LANE" = "all" ]; then windows=requested fi + elif [ "$EVENT_NAME" = "pull_request" ] && [ "${CHANGES_WINDOWS:-}" = "true" ]; then + # A Windows-sensitive pull request runs the nine shards (platform-windows if:). + windows=requested fi # One line per job, mirroring that job's own `if:`. Adding a job to this workflow diff --git a/scripts/ci/windows-sensitive-diff.sh b/scripts/ci/windows-sensitive-diff.sh index 05bcaaba27e..b0a9f025008 100644 --- a/scripts/ci/windows-sensitive-diff.sh +++ b/scripts/ci/windows-sensitive-diff.sh @@ -79,6 +79,7 @@ get-ciminstance process.platform localappdata userprofile +spawnsync MARKERS status=0 diff --git a/tests/ci-workflows/ci-review-lanes.test.ts b/tests/ci-workflows/ci-review-lanes.test.ts index 99d0aaf7164..6253544f53a 100644 --- a/tests/ci-workflows/ci-review-lanes.test.ts +++ b/tests/ci-workflows/ci-review-lanes.test.ts @@ -231,8 +231,11 @@ describe("CI review lanes", () => { expect(enabled("npm-global-smoke", event, lane, "true", "false")).toBe(false); } for (const windows of ["true", "false", ""]) { - // Even a Windows-sensitive pull request runs only the fast Ubuntu subset. - expect(enabled("platform-windows", "pull_request", "", "true", "true", "true", windows)).toBe(false); + // Only a Windows-sensitive pull request (changes.outputs.windows == 'true') runs the + // nine shards; every other pull request stays on the fast Ubuntu subset (#7052). + expect(enabled("platform-windows", "pull_request", "", "true", "true", "true", windows)).toBe(windows === "true"); + // A push never selects Windows, whatever the selector reported. + expect(enabled("platform-windows", "push", "", "true", "true", "true", windows)).toBe(false); } expect(enabled("platform-windows", "schedule", "", "false", "false", "false")).toBe(true); expect(enabled("macos-control", "schedule", "", "false", "false", "false")).toBe(true); @@ -362,7 +365,7 @@ describe("Windows and nightly CI contracts", () => { expect(checkouts).toBe(17); }); - test.skipIf(process.platform === "win32" || !Bun.which("jq"))("aggregate never requests Windows on a pull request and requests both diagnostic jobs nightly", async () => { + test.skipIf(process.platform === "win32" || !Bun.which("jq"))("aggregate requests Windows on a pull request only when it is Windows-sensitive, and both diagnostic jobs nightly", async () => { const ci = Bun.YAML.parse(await readText(".github/workflows/ci.yml")) as { jobs: Record; run?: string }> }>; }; @@ -381,8 +384,8 @@ describe("Windows and nightly CI contracts", () => { const slice = script.slice(start, last + "RESULTS_EOF".length); for (const [event, lane, windows, expectWindows, expectControl] of [ ["schedule", "", undefined, "requested", "requested"], - // Pull requests run only the fast Ubuntu subset, even when the change is Windows-sensitive. - ["pull_request", "", "true", "not-requested", "not-requested"], + // A Windows-sensitive pull request runs the nine shards; any other stays on Ubuntu. + ["pull_request", "", "true", "requested", "not-requested"], ["pull_request", "", "false", "not-requested", "not-requested"], ["pull_request", "", undefined, "not-requested", "not-requested"], ["push", "", "true", "not-requested", "not-requested"], @@ -408,6 +411,8 @@ describe("Windows and nightly CI contracts", () => { test.skipIf(process.platform === "win32" || !Bun.which("git"))("Windows diff scan considers only added src/tests markers and fails closed without a parent", () => { const scenarios = [ { path: "src/example.ts", before: "plain\n", after: "WIN32\n", expected: "true" }, + // An added spawnSync line is Windows-sensitive by content, outside the path filter too (#7468). + { path: "tests/update/example-host-guard.test.ts", before: "plain\n", after: "const original = Bun.spawnSync;\n", expected: "true" }, { path: "tests/example.ts", before: "plain\n", after: "ordinary\n", expected: "false" }, { path: "src/example.ts", before: "powershell\nplain\n", after: "plain\n", expected: "false" }, { path: "docs/example.md", before: "plain\n", after: "pwsh\n", expected: "false" }, diff --git a/tests/ci-workflows/ci-workflows.test.ts b/tests/ci-workflows/ci-workflows.test.ts index eaea2720078..2b5c1577440 100644 --- a/tests/ci-workflows/ci-workflows.test.ts +++ b/tests/ci-workflows/ci-workflows.test.ts @@ -313,12 +313,13 @@ describe("GitHub Actions hardening", () => { expect(macosControlSteps.some(step => step.run?.includes("--shard"))).toBe(false); expect(macosControlSteps.some(step => step.run?.includes("coreutils") && step.run.includes("GITHUB_PATH"))).toBe(true); - // Dispatch and the daily schedule request Windows; pull requests (which run - // only the fast Ubuntu subset) and pushes do not. Keep branch-ref shortcuts - // off this condition. + // Dispatch and the daily schedule request Windows, and so does a Windows-sensitive + // pull request (changes.outputs.windows == 'true'); every other pull request runs + // only the fast Ubuntu subset, and pushes never do. Keep branch-ref shortcuts off + // this condition. const windowsIf = String((ci.jobs?.["platform-windows"] as { if?: string })?.if ?? ""); expect(windowsIf).toBe( - "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all')", + "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') || github.event_name == 'pull_request' && needs.changes.outputs.windows == 'true'", ); expect(windowsIf).not.toContain("refs/heads/main"); expect(windowsIf).not.toContain("refs/heads/preview"); From 69146a3a7619b13bf59ec2752db64ba8d6b100cd Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 22 Oct 2026 02:42:43 +0900 Subject: [PATCH 2/2] ci(windows): keep PRs Ubuntu-only, but flag Windows-sensitive ones for a lane=all dispatch Drop the pull_request clause and aggregate change: AGENTS.md's CI policy keeps Windows off PR CI. A Windows-sensitive PR now gets a non-blocking ::notice and step-summary line to dispatch lane=all before merge, the selector flags added spawnSync lines, and the platform-windows comment states the real policy. --- .github/workflows/ci.yml | 25 +++++++++------------- scripts/ci/windows-sensitive-diff.sh | 8 +++++++ tests/ci-workflows/ci-review-lanes.test.ts | 23 +++++++++++--------- tests/ci-workflows/ci-workflows.test.ts | 9 ++++---- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c26c7147ca..028c28ca318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -997,22 +997,20 @@ jobs: # now means Linux + macOS + the gates; Windows re-enters the gate when the # tracked failures are fixed, not before. # - # Dispatch-only proved too late. #6639, #6556, #6605 and #6606 each landed with - # green pull-request CI while breaking Windows, and the breakage surfaced days - # later on a manual dispatch. #6670 made a pull request whose changes job reports - # windows=true (a Windows path, or an added src/tests line naming a Windows - # marker; see scripts/ci/windows-sensitive-diff.sh) run the nine shards. #7052 - # then dropped that clause to keep pull requests on the fast Ubuntu subset, and - # since dev is absent from the push trigger, Windows ran only nightly or on a - # lane=all dispatch. #7468 landed green that way and broke windows 9/9 on dev. - # The clause is back for Windows-sensitive pull requests only: every other pull - # request still runs the Ubuntu subset, and the nightly schedule still catches - # whatever the selector misses. The aggregate below mirrors this condition. + # Pull requests never run these shards (AGENTS.md "CI policy", a maintainer + # decision): Windows runs on the nightly schedule and on a lane=all dispatch. + # The changes job still computes windows=true for a Windows-sensitive pull + # request (a Windows path, or an added src/tests line naming a Windows marker; + # see scripts/ci/windows-sensitive-diff.sh). That verdict does not schedule + # these jobs; it posts a non-blocking notice, and a maintainer dispatches + # lane=all on the branch before merging. #6639, #6556, #6605, #6606 and #7468 + # each landed with green pull-request CI while breaking Windows, which is why + # that dispatch is part of merging a Windows-sensitive pull request. platform-windows: name: windows ${{ matrix.shard }}/9 needs: [changes, select-windows-runner] if: >- - github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') || github.event_name == 'pull_request' && needs.changes.outputs.windows == 'true' + github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') runs-on: ${{ fromJSON(needs.select-windows-runner.outputs.runner) }} # Sharded like the Linux legs. The single-leg run reached 30 minutes on a # green suite and was killed in cleanup; four shards put each leg inside the @@ -1810,9 +1808,6 @@ jobs: if [ -z "$LANE" ] || [ "$LANE" = "all" ]; then windows=requested fi - elif [ "$EVENT_NAME" = "pull_request" ] && [ "${CHANGES_WINDOWS:-}" = "true" ]; then - # A Windows-sensitive pull request runs the nine shards (platform-windows if:). - windows=requested fi # One line per job, mirroring that job's own `if:`. Adding a job to this workflow diff --git a/scripts/ci/windows-sensitive-diff.sh b/scripts/ci/windows-sensitive-diff.sh index b0a9f025008..e642788ccba 100644 --- a/scripts/ci/windows-sensitive-diff.sh +++ b/scripts/ci/windows-sensitive-diff.sh @@ -26,6 +26,14 @@ selected="${PATH_SELECTED:-}" emit() { printf 'windows=%s\n' "$1" >> "$GITHUB_OUTPUT" printf 'windows-sensitive: %s (%s)\n' "$1" "$2" + # Pull requests never run the Windows shards (AGENTS.md CI policy), so a sensitive one only + # gets a non-blocking notice: a maintainer dispatches lane=all on its branch before merging. + if [ "$event" = "pull_request" ] && [ "$1" = "true" ]; then + printf '::notice title=Windows-sensitive::Windows-sensitive (%s): dispatch Cross-platform CI lane=all on this branch before merge\n' "$2" + if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then + printf '**Windows-sensitive** (%s): pull requests do not run the Windows shards; dispatch Cross-platform CI `lane=all` on this branch before merge.\n' "$2" >> "$GITHUB_STEP_SUMMARY" + fi + fi } case "$selected" in diff --git a/tests/ci-workflows/ci-review-lanes.test.ts b/tests/ci-workflows/ci-review-lanes.test.ts index 6253544f53a..c7dfd629de7 100644 --- a/tests/ci-workflows/ci-review-lanes.test.ts +++ b/tests/ci-workflows/ci-review-lanes.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { spawnSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { repoPath } from "../helpers/repo-root"; @@ -231,11 +231,8 @@ describe("CI review lanes", () => { expect(enabled("npm-global-smoke", event, lane, "true", "false")).toBe(false); } for (const windows of ["true", "false", ""]) { - // Only a Windows-sensitive pull request (changes.outputs.windows == 'true') runs the - // nine shards; every other pull request stays on the fast Ubuntu subset (#7052). - expect(enabled("platform-windows", "pull_request", "", "true", "true", "true", windows)).toBe(windows === "true"); - // A push never selects Windows, whatever the selector reported. - expect(enabled("platform-windows", "push", "", "true", "true", "true", windows)).toBe(false); + // Even a Windows-sensitive pull request runs only the fast Ubuntu subset. + expect(enabled("platform-windows", "pull_request", "", "true", "true", "true", windows)).toBe(false); } expect(enabled("platform-windows", "schedule", "", "false", "false", "false")).toBe(true); expect(enabled("macos-control", "schedule", "", "false", "false", "false")).toBe(true); @@ -365,7 +362,7 @@ describe("Windows and nightly CI contracts", () => { expect(checkouts).toBe(17); }); - test.skipIf(process.platform === "win32" || !Bun.which("jq"))("aggregate requests Windows on a pull request only when it is Windows-sensitive, and both diagnostic jobs nightly", async () => { + test.skipIf(process.platform === "win32" || !Bun.which("jq"))("aggregate never requests Windows on a pull request and requests both diagnostic jobs nightly", async () => { const ci = Bun.YAML.parse(await readText(".github/workflows/ci.yml")) as { jobs: Record; run?: string }> }>; }; @@ -384,8 +381,8 @@ describe("Windows and nightly CI contracts", () => { const slice = script.slice(start, last + "RESULTS_EOF".length); for (const [event, lane, windows, expectWindows, expectControl] of [ ["schedule", "", undefined, "requested", "requested"], - // A Windows-sensitive pull request runs the nine shards; any other stays on Ubuntu. - ["pull_request", "", "true", "requested", "not-requested"], + // Pull requests run only the fast Ubuntu subset, even when the change is Windows-sensitive. + ["pull_request", "", "true", "not-requested", "not-requested"], ["pull_request", "", "false", "not-requested", "not-requested"], ["pull_request", "", undefined, "not-requested", "not-requested"], ["push", "", "true", "not-requested", "not-requested"], @@ -442,13 +439,19 @@ describe("Windows and nightly CI contracts", () => { commit(); if (!scenario.orphan) { writeFileSync(path, scenario.after ?? scenario.before); commit(); } const output = join(directory, "output"); + const summary = join(directory, "summary"); const result = spawnSync("bash", [repoPath("scripts", "ci", "windows-sensitive-diff.sh")], { cwd: directory, encoding: "utf8", timeout: 5_000, - env: { ...process.env, EVENT_NAME: scenario.event ?? "pull_request", PATH_SELECTED: scenario.selected ?? "false", GITHUB_OUTPUT: output }, + env: { ...process.env, EVENT_NAME: scenario.event ?? "pull_request", PATH_SELECTED: scenario.selected ?? "false", GITHUB_OUTPUT: output, GITHUB_STEP_SUMMARY: summary }, }); expect(result.error).toBeUndefined(); expect(result.status, result.stderr).toBe(scenario.status ?? 0); if (scenario.expected) expect(readFileSync(output, "utf8")).toBe(`windows=${scenario.expected}\n`); + // A Windows-sensitive pull request gets a non-blocking notice and summary line, and nothing + // else does; the PR still never runs the Windows shards (AGENTS.md CI policy). + const notice = (scenario.event ?? "pull_request") === "pull_request" && scenario.expected === "true"; + expect(result.stdout.includes("::notice title=Windows-sensitive::")).toBe(notice); + expect(existsSync(summary) && readFileSync(summary, "utf8").includes("lane=all")).toBe(notice); } finally { rmSync(directory, { recursive: true, force: true }); } } }, 30_000); diff --git a/tests/ci-workflows/ci-workflows.test.ts b/tests/ci-workflows/ci-workflows.test.ts index 2b5c1577440..eaea2720078 100644 --- a/tests/ci-workflows/ci-workflows.test.ts +++ b/tests/ci-workflows/ci-workflows.test.ts @@ -313,13 +313,12 @@ describe("GitHub Actions hardening", () => { expect(macosControlSteps.some(step => step.run?.includes("--shard"))).toBe(false); expect(macosControlSteps.some(step => step.run?.includes("coreutils") && step.run.includes("GITHUB_PATH"))).toBe(true); - // Dispatch and the daily schedule request Windows, and so does a Windows-sensitive - // pull request (changes.outputs.windows == 'true'); every other pull request runs - // only the fast Ubuntu subset, and pushes never do. Keep branch-ref shortcuts off - // this condition. + // Dispatch and the daily schedule request Windows; pull requests (which run + // only the fast Ubuntu subset) and pushes do not. Keep branch-ref shortcuts + // off this condition. const windowsIf = String((ci.jobs?.["platform-windows"] as { if?: string })?.if ?? ""); expect(windowsIf).toBe( - "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all') || github.event_name == 'pull_request' && needs.changes.outputs.windows == 'true'", + "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane == '' || github.event.inputs.lane == 'all')", ); expect(windowsIf).not.toContain("refs/heads/main"); expect(windowsIf).not.toContain("refs/heads/preview");