From c2a35e574a1d889172ac7d3f3b51a2cc0721a09c Mon Sep 17 00:00:00 2001 From: larryro <371767072@qq.com> Date: Sun, 6 Sep 2026 13:48:06 +0800 Subject: [PATCH 1/5] ci: move the CI test toolchain to Bun 1.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs Playwright job timed out in the webServer on roughly one E2E run in fifteen (3 of the last 40, twice in a row on #3262): the client build printed the PWA plugin's "files generated" and then nothing followed for the rest of the 240 s budget — `vite preview` never started, so zero tests ran. The build's own work was complete; the process hung inside the explicit `process.exit(0)` that build-client.ts fires after `build()`. Bun 1.4.1 names the cause: on the main thread, `process.exit()` used to run N-API addon finalizers and cleanup hooks and could hand a still-queued threadsafe-function call a null env. rolldown, which Vite 8 builds with, is exactly such an addon. 1.4.1 skips them, as Node does. CI pinned 1.3.12 through the setup-turbo composite action; the pin moves to 1.4.2. The two comments that explained the hang as a dangling event-loop handle now state the real dependency. Under Bun 1.4.2, `bun install --frozen-lockfile --dry-run` resolves the lockfile and the docs Playwright suite passes 3/3 with CI=1. Left alone on purpose: the release/build/cli workflow pins and the Dockerfiles (they ship binaries and runtime images), security.yml's 1.4.0 audit pin, and package.json's packageManager field. --- .github/actions/setup-turbo/action.yml | 7 ++++++- services/docs/playwright.config.ts | 3 ++- services/docs/scripts/build-client.ts | 15 ++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/actions/setup-turbo/action.yml b/.github/actions/setup-turbo/action.yml index 2caf55ea47..6a6bfd47a2 100644 --- a/.github/actions/setup-turbo/action.yml +++ b/.github/actions/setup-turbo/action.yml @@ -4,7 +4,12 @@ description: Bun + deps with cache, optional Turbo remote cache server inputs: bun-version: description: Bun version - default: '1.3.12' + # Floor 1.4.1. Earlier Bun ran N-API addon finalizers and cleanup hooks + # inside process.exit() and could hand a still-queued threadsafe-function + # call a null env; rolldown (Vite 8) is such an addon, so the docs client + # build hung at its explicit exit on roughly one CI run in fifteen and the + # docs Playwright webServer timed out with zero tests run. + default: '1.4.2' start-turbo-cache: description: Start the Turborepo GH artifacts cache server default: 'true' diff --git a/services/docs/playwright.config.ts b/services/docs/playwright.config.ts index 845d8fde6c..338322a0aa 100644 --- a/services/docs/playwright.config.ts +++ b/services/docs/playwright.config.ts @@ -23,7 +23,8 @@ export default createPlaywrightConfig({ // `scripts/build-client.ts` (vite's JS API + an explicit exit), not the // `vite build` CLI: the CLI process occasionally never exits after a // successful build, and the `&&` chain then starves silently until this - // webServer timeout with zero tests run. + // webServer timeout with zero tests run. The explicit exit is only safe + // on Bun ≥ 1.4.1 — see build-client.ts for why. command: `bun --bun scripts/build-search-index.ts && ` + `bun --bun scripts/build-client.ts && ` + diff --git a/services/docs/scripts/build-client.ts b/services/docs/scripts/build-client.ts index 4c25675153..82eacb1e9a 100644 --- a/services/docs/scripts/build-client.ts +++ b/services/docs/scripts/build-client.ts @@ -1,12 +1,13 @@ // The Playwright webServer chains client build → preview with `&&`, so the // chain advances only when the build PROCESS exits. `bun --bun vite build` -// occasionally never exits after a successful build — a dangling handle in -// the rolldown/PWA-plugin pipeline keeps the event loop alive — and the -// silent hang starves the chain until the webServer timeout with zero tests -// run. Building through the JS API and exiting explicitly makes process -// exit a certainty instead of an event-loop accident: `build()` resolves -// only after every plugin's `closeBundle`, so the PWA artifacts are already -// on disk when the exit fires. +// occasionally never exits after a successful build, and the silent hang +// starves the chain until the webServer timeout with zero tests run. +// Building through the JS API and exiting explicitly removes the implicit +// exit from the chain: `build()` resolves only after every plugin's +// `closeBundle`, so the PWA artifacts are already on disk when the exit +// fires. The exit itself needs Bun ≥ 1.4.1 — earlier Bun ran rolldown's +// N-API finalizers and cleanup hooks inside `process.exit()` and could hang +// there just the same (pinned in `.github/actions/setup-turbo/action.yml`). import { build } from 'vite'; From 302480f391deb36fae090d6140cb723b878a22b4 Mon Sep 17 00:00:00 2001 From: larryro <371767072@qq.com> Date: Sun, 6 Sep 2026 14:28:36 +0800 Subject: [PATCH 2/5] ci: hash the toolchain pin into turbo's cache key Turbo's task hash ignored the Bun version, so a pin bump could be masked by replayed logs: on #3265's first run the CLI unit task was a cache hit from a 1.3.12 run and never executed under 1.4.2. The setup-turbo action file is now a global dependency, so changing its pin reruns every cached task under the new toolchain. --- turbo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/turbo.json b/turbo.json index 5527fbebe2..bc945ce6b2 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,6 @@ { "$schema": "https://turbo.build/schema.v2.json", + "globalDependencies": [".github/actions/setup-turbo/action.yml"], "globalPassThroughEnv": [ "DO_NOT_TRACK", "TURBO_TELEMETRY_DISABLED", From 358b3ab558c6dfdc03f138ed01486bcb798919d7 Mon Sep 17 00:00:00 2001 From: larryro <371767072@qq.com> Date: Sun, 6 Sep 2026 14:28:36 +0800 Subject: [PATCH 3/5] ci: align the audit gate's Bun pin with the test toolchain The audit gate pinned 1.4.0 as 'deliberately newer than the build jobs'; with the test toolchain at 1.4.2 that pin was the older one. Same pin as setup-turbo now; the audit's own floor (1.4, for bun audit --prod on a workspace root) still holds. --- .github/workflows/security.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index a43eff686d..49cf3fd6fa 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -62,15 +62,16 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: - # Deliberately newer than the 1.3.12 the build jobs pin: before 1.4, - # `bun audit --prod` run from a workspace root reported workspace + # The test toolchain's pin (`.github/actions/setup-turbo`); the audit + # itself needs 1.4 or newer: before 1.4, `bun audit --prod` run from + # a workspace root reported workspace # members' devDependencies as production (oven-sh/bun#26675, fixed # in 1.4 by oven-sh/bun#38333). That made the gate below fail on # @faker-js/faker 5.5.3, reachable only through the dev-only # @stoplight/prism-http mock server (postman-collection pins it and # no upstream release lifts the pin). 1.4.0 keeps reporting real # production findings (verified against the fast-uri/mysql2 lock). - bun-version: '1.4.0' + bun-version: '1.4.2' - name: Run bun audit (report) # Full advisory list for visibility; does not fail the build itself. From 10341847d76bb853500fc33f49b2f7ac23dba089 Mon Sep 17 00:00:00 2001 From: larryro <371767072@qq.com> Date: Sun, 6 Sep 2026 14:28:37 +0800 Subject: [PATCH 4/5] ci(docs): mark the client build's exit and soften the hang wording The failing logs stop at the PWA file list, which rolldown prints from inside its native close; the rest of that close, build() returning, process.exit(0) and the preview's start all sit in the unobserved 0.8 s window. The comments now say the evidence places the stall at the exit rather than stating it, and build-client.ts logs one line after build() resolves so any future stall lands on one side of the exit. --- .github/actions/setup-turbo/action.yml | 9 ++++++--- services/docs/scripts/build-client.ts | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-turbo/action.yml b/.github/actions/setup-turbo/action.yml index 6a6bfd47a2..2e96679040 100644 --- a/.github/actions/setup-turbo/action.yml +++ b/.github/actions/setup-turbo/action.yml @@ -6,9 +6,12 @@ inputs: description: Bun version # Floor 1.4.1. Earlier Bun ran N-API addon finalizers and cleanup hooks # inside process.exit() and could hand a still-queued threadsafe-function - # call a null env; rolldown (Vite 8) is such an addon, so the docs client - # build hung at its explicit exit on roughly one CI run in fifteen and the - # docs Playwright webServer timed out with zero tests run. + # call a null env; rolldown (Vite 8) is such an addon. The docs client + # build stalled on roughly one CI run in fifteen after its last build + # output and before `vite preview` started, and the evidence places that + # stall at the build's explicit exit; the docs Playwright webServer then + # timed out with zero tests run. The pin is hashed into every Turbo task + # (`globalDependencies`), so a bump reruns the cached tasks under it. default: '1.4.2' start-turbo-cache: description: Start the Turborepo GH artifacts cache server diff --git a/services/docs/scripts/build-client.ts b/services/docs/scripts/build-client.ts index 82eacb1e9a..4665ff7f6f 100644 --- a/services/docs/scripts/build-client.ts +++ b/services/docs/scripts/build-client.ts @@ -6,8 +6,10 @@ // exit from the chain: `build()` resolves only after every plugin's // `closeBundle`, so the PWA artifacts are already on disk when the exit // fires. The exit itself needs Bun ≥ 1.4.1 — earlier Bun ran rolldown's -// N-API finalizers and cleanup hooks inside `process.exit()` and could hang -// there just the same (pinned in `.github/actions/setup-turbo/action.yml`). +// N-API finalizers and cleanup hooks inside `process.exit()`, and the +// evidence places CI's remaining stalls there (pinned in +// `.github/actions/setup-turbo/action.yml`). The line logged after `build()` +// resolves puts any future stall on one side of the exit or the other. import { build } from 'vite'; @@ -17,4 +19,5 @@ try { console.error(error); process.exit(1); } +console.info('[build-client] build resolved, exiting 0'); process.exit(0); From 3abb75b058a9f459018ac34ff9828f4bc746d130 Mon Sep 17 00:00:00 2001 From: larryro <371767072@qq.com> Date: Sun, 6 Sep 2026 14:53:27 +0800 Subject: [PATCH 5/5] ci: put the toolchain pin in the existing global dependency list turbo.json already carried a globalDependencies list further down; the key added at the top was a duplicate the parser dropped (last key wins), so the CLI test task kept main's hash and replayed a 1.3.12 log a second time. The action file now sits in the one list, and Turbo's dry run shows it among the global files. --- turbo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turbo.json b/turbo.json index bc945ce6b2..6ab8beea9c 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,5 @@ { "$schema": "https://turbo.build/schema.v2.json", - "globalDependencies": [".github/actions/setup-turbo/action.yml"], "globalPassThroughEnv": [ "DO_NOT_TRACK", "TURBO_TELEMETRY_DISABLED", @@ -110,6 +109,7 @@ } }, "globalDependencies": [ + ".github/actions/setup-turbo/action.yml", ".oxlintrc.json", ".oxfmtrc.json", "tsconfig.base.json"