Skip to content

ci: move the CI test toolchain to Bun 1.4.2 - #3265

Merged
larryro merged 5 commits into
mainfrom
ci/bun-1-4-2-test-toolchain
Sep 6, 2026
Merged

ci: move the CI test toolchain to Bun 1.4.2#3265
larryro merged 5 commits into
mainfrom
ci/bun-1-4-2-test-toolchain

Conversation

@larryro

@larryro larryro commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Why

The docs Playwright job (E2E workflow, Playwright (docs)) times out in Playwright's webServer on roughly one run in fifteen — 3 of the last 40 E2E runs, and twice in a row on #3262 (runs 34011390460 attempts 1+2, and 34007884968 on another branch). The failing logs all stop at the same line: the client build prints the PWA plugin's files generated, then nothing follows for the remaining ~3m50s of the 240 s budget. vite preview never prints its banner, so zero tests run.

The PWA file list is printed from inside rolldown's native close, so the rest of that close, build() returning, the explicit process.exit(0) in services/docs/scripts/build-client.ts, the shell && and vite preview's start all sit in the unobserved window (0.8 s in a passing run). The evidence places the stall at the exit: the natural-exit vite build path that runs the same bundle.close() is green across the sampled Build history, web's identical vite preview never stalled, and Bun 1.4.1 names an exit-time hang in exactly this addon class.

Root cause

Bun v1.4.1's release notes (2026-09-04):

On the main thread, process.exit() previously ran addon finalizers and cleanup hooks, and these could crash. It now skips them, as Node does.
process.exit() with calls still queued on a threadsafe function no longer passes a null env to the addon's callback.

rolldown — what Vite 8 builds with — is an N-API addon whose JS plugin bridge runs on threadsafe functions. CI ran Bun 1.3.12 through the setup-turbo composite action's default, so every docs build exited through that path; under runner contention it occasionally never came back. The pre-#2982 vite build CLI "never exits after a successful build" hang is the same class (natural exit runs the same teardown).

Change

  • .github/actions/setup-turbo/action.yml: default bun-version 1.3.121.4.2 (current release, 2026-09-05), with the floor documented in place. This is the pin behind Checks, E2E, Commitlint and the two Build jobs that call the action without a version.
  • services/docs/scripts/build-client.ts, services/docs/playwright.config.ts: the comments that explained the hang as a dangling event-loop handle now state the real dependency.

Deliberately left alone (release-engineering decisions)

  • build.yml / release.yml (1.3.12) and cli.yml (1.3.13) — they compile the shipped CLI binaries.
  • The Dockerfiles (oven/bun:1.3.12 in web, docs, sandbox, plop template) — runtime images; the Docs container test job shows no failures in the last 25 Build runs.
  • security.yml's 1.4.0 audit pin (ci: harden the bun audit gate (bun 1.4.0, registry retries, timeout) #3211) and package.json's packageManager: bun@1.3.10.

Verification

  • Bun 1.4.2, this branch: bun install --frozen-lockfile --dry-run resolves the lockfile; CI=1 bun run --filter @tale/docs test:e2e → build, ➜ Local: http://localhost:3002/, 3 passed.
  • Bun 1.3.12 and 1.4.0 locally never reproduced the hang (36 contended builds, 0 hangs) — the failure needs the runner's timing; the CI runs of this PR are the observation that counts, and the docs job's pass rate over the next runs is the proof.
  • oxfmt --check clean on the changed files.

Review follow-ups (adversarial review: approve, advisory only) — 1034184

  • Turbo cache (turbo.json): the task hash ignored the Bun version, so this PR's first CI run replayed the CLI unit task from a 1.3.12 cache (bun test v1.3.12 in the log) and never executed it under 1.4.2. .github/actions/setup-turbo/action.yml is now in globalDependencies, so a pin change reruns every cached task. (First attempt added a second top-level globalDependencies key that the parser dropped, so 1034184 replayed the same 1.3.12 log again; 3abb75b puts the file in the existing list and Turbo's dry run shows it among the global files — that push is the first run where the CLI tests execute under 1.4.2 in CI.) (The reviewer ran tools/cli under 1.4.2 locally meanwhile: 334 pass / 18 skip / 0 fail.)

  • Audit gate (security.yml): its 1.4.0 pin, documented as "deliberately newer than the build jobs", was now the older one; aligned to 1.4.2 with the comment corrected.

  • Wording: comments, this body and the follow-up commit state that the evidence places the stall at the exit instead of asserting the site. build-client.ts now logs [build-client] build resolved, exiting 0 after build() resolves, so any future timeout lands on one side of the exit.

  • Not addressed here: Storybook (also a Vite/rolldown build under --bun) is path-filtered off this PR and gets its first 1.4.2 observation with the next UI change; the repo-wide dev floor stays "Bun >= 1.3" (local runs skip the build chain via reuseExistingServer).

  • Fifth occurrence while this PR was open: run 34016343826 on fix(platform): claim message slots until a deadline, not a fixed count #3267 (a platform-only change, no docs content touched) — bun-version: 1.3.12, files generated, then the 240 s timeout. Same shape, unrelated content.

  • Observed on 3abb75b: the Checks Unit job ran with zero cache replays@tale/cli:test was a cache miss and executed under bun test v1.4.2, as did every other Bun-native suite.

  • Sixth occurrence: run 34017745677 on fix(platform): claim message slots until a deadline, not a fixed count #3267's polish head (platform-only again) — bun-version: 1.3.12, files generated, 240 s timeout. Under 1.3.12 the docs job has now stalled on 6 of today's runs; on this PR's three 1.4.2 heads it passed 3 of 3.

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.
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.
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.
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.
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.
@larryro
larryro merged commit 4df487e into main Sep 6, 2026
50 checks passed
@larryro
larryro deleted the ci/bun-1-4-2-test-toolchain branch September 6, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant