diff --git a/.github/workflows/nightly-compat.yml b/.github/workflows/nightly-compat.yml index eabc2073b32..9515d6b2189 100644 --- a/.github/workflows/nightly-compat.yml +++ b/.github/workflows/nightly-compat.yml @@ -71,7 +71,20 @@ jobs: node-version: "26" cache: npm - uses: ./.github/actions/npm-ci-retry + # #8090 — this is the ONLY Node 26 build in the whole CI matrix (ci.yml pins + # CI_NODE_VERSION=24). It failed every nightly with the runner-reclaimed + # signature ("The runner has received a shutdown signal" / "The operation was + # canceled", no exit code) always at the same Turbopack compile phase — a + # classic OOM kill on the memory-constrained ubuntu-latest runner. Turbopack's + # native (Rust, off-V8-heap) allocation is NOT bounded by --max-old-space-size + # and peaks far higher than webpack on this large module graph (#6409), and is + # heavier still under Node 26. Use the documented webpack fallback here: it still + # validates that the app *builds* on Node 26 (the point of this compat job) at a + # much lower memory peak. Turbopack-on-Node-24 stays covered by ci.yml's build + # job. See docs/reference/ENVIRONMENT.md (OMNIROUTE_USE_TURBOPACK) and #6409. - run: npm run build + env: + OMNIROUTE_USE_TURBOPACK: "0" compat-tests: name: Node ${{ matrix.node }} Compat Tests (${{ matrix.shard }}/4) diff --git a/tests/unit/nightly-compat-node26-webpack-8090.test.ts b/tests/unit/nightly-compat-node26-webpack-8090.test.ts new file mode 100644 index 00000000000..5ae7d9caa04 --- /dev/null +++ b/tests/unit/nightly-compat-node26-webpack-8090.test.ts @@ -0,0 +1,64 @@ +/** + * Regression guard for #8090 — "nightly-compat: Node 24/26 build failures". + * + * Class 2 of the triage (the live item): the `compat-build-26` job in + * .github/workflows/nightly-compat.yml is the ONLY place in the entire CI matrix + * that runs `npm run build` on Node 26 (ci.yml pins CI_NODE_VERSION=24). Every + * nightly it failed with the runner-reclaimed signature — "The runner has received + * a shutdown signal" / "The operation was canceled", no exit code, always at the + * same Turbopack compile phase — the classic OOM-kill pattern on the + * memory-constrained ubuntu-latest runner. + * + * Turbopack's native (Rust, off-V8-heap) allocation is NOT bounded by + * --max-old-space-size and peaks far higher than webpack on OmniRoute's large module + * graph (#6409), heavier still under Node 26. The codebase's own documented escape + * hatch for RAM-constrained environments is the webpack fallback + * (OMNIROUTE_USE_TURBOPACK=0; see docs/reference/ENVIRONMENT.md). The fix wires that + * fallback into the Node 26 compat build so it still validates the app *builds* on + * Node 26 without OOMing the runner. + * + * This guard asserts the compat-build-26 job keeps the webpack fallback so a future + * edit cannot silently flip it back to the OOM-prone Turbopack path. + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); + +function extractJobBlock(yaml: string, jobName: string): string { + const lines = yaml.split("\n"); + // Job keys live at 2-space indentation under `jobs:`. + const startIdx = lines.findIndex((l) => new RegExp(`^ ${jobName}:\\s*$`).test(l)); + assert.ok(startIdx !== -1, `nightly-compat.yml must define the ${jobName} job`); + const block: string[] = []; + for (let i = startIdx + 1; i < lines.length; i++) { + const line = lines[i]; + // Next 2-space-indented key ends the block (but blank lines / deeper indent stay). + if (/^ \S/.test(line)) break; + block.push(line); + } + return block.join("\n"); +} + +test("#8090 compat-build-26 uses the webpack fallback (OMNIROUTE_USE_TURBOPACK=0) to avoid Node 26 OOM", () => { + const wf = fs.readFileSync( + path.join(repoRoot, ".github/workflows/nightly-compat.yml"), + "utf-8" + ); + const jobBlock = extractJobBlock(wf, "compat-build-26"); + + assert.match( + jobBlock, + /npm run build/, + "compat-build-26 must still run the production build on Node 26" + ); + assert.match( + jobBlock, + /OMNIROUTE_USE_TURBOPACK:\s*["']0["']/, + "compat-build-26 must set OMNIROUTE_USE_TURBOPACK=0 (webpack fallback) so the sole " + + "Node 26 build does not OOM the runner on Turbopack's unbounded native memory (#8090/#6409)" + ); +});