Repository navigation
build: move the toolchain to bun 1.4 - #478
Merged
Merged
Conversation
bun-v1.4.0 shipped today, which is the one thing #443 was parked on. This is that branch rebuilt as TOOLCHAIN ONLY: 20 files, against 1229. Everything separable landed on main first as seven restructure slices and the TypeScript conversion, so what is left is the package manager and the CI that runs it. BYTE-IDENTICAL TO WHAT MAIN BUILDS TODAY, 1505 staged files, verified three ways because the release notes gave three reasons to doubt it: node + pnpm the baseline, main as it stands bun 1.4 + pnpm the runtime alone, on pnpm's dependency tree bun 1.4 + bun both, on bun's own resolutions 1.4 is a Zig-to-Rust rewrite and switches node:zlib to zlib-ng, and this repo's output is content-addressed and almost entirely compression-derived, so a single moved byte would re-mint /a/ and /i/ URLs and orphan every committed dictionary. Nothing moved. oxc-minify, lightningcss and minify-html resolve to the same pinned versions under both installers. THE CANARY MACHINERY IS GONE, and that is what a released tag buys. config/ bun-canary.json pinned a SHA-256 because the `canary` tag rolled daily, which would have swapped a compiler underneath the artifact graph. A released tag is immutable, so .github/actions/setup-bun reads the version from packageManager and installs it. One declaration, no digest to bump, no daily red CI. It still probes the capability first. Every bun before 1.4 ACCEPTS zstdCompressSync's `dictionary` option and silently ignores it: the frame decodes, nothing throws, and the only signal is a byte count that never shrank. The action runs that control before any build spends 40 seconds discovering it. TWO TESTS REPLACED RATHER THAN DELETED. The workflow-bootstrap test now asserts every workflow that RUNS bun reaches the shared action and that none names a version inline, and the allowBuilds test becomes trustedDependencies, whose entries must appear in bun.lock. Its first draft demanded a bootstrap from promote-production.yml, which only ECHOES "bun run deploy:promote" as advice, so detection reads commands rather than strings. Also measured rather than carried over: lens-reader needs NO --ignore-workspace. pnpm walked up to the workspace root and never created the local node_modules; bun installs the local package.json on its own. The comment says so, so nobody adds a flag looking for parity. routes:check stays on node deliberately: it boots workerd through wrangler's createTestHarness, which hangs under bun. Gates under bun 1.4: 325 tests, build, lint, typecheck, routes:check, pages:check, tools:check, perf-budget, photos:check, check-wrangler, the cal suite, lens-reader's 13, and a wrangler dry-run.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
aadhar-sh | 22aa0f1 | Aug 20 2026, 03:09 PM |
`bun test` with no path DISCOVERS every *.test.* in the tree. The CI step ran that, so it collected 367 tests across 10 files, including cal's vitest specs and lens-reader's, under a runner neither expects: 20 failures and 4 errors that say nothing about this change. It calls `bun run test` now, which names the one file the step means. And `node --test X` became `bun --test X`, which is not bun's spelling. The runner is `bun test X`; the flag form silently does something else. Both came from the same blunt substitution, and neither reproduced locally because I ran the suite by PATH rather than through the script and the CI step. Run the command CI runs, not the command that means the same thing. Also restores test:node, deleted by the same sweep. Running the suite under the other runtime occasionally is how gotcha 28 found a real defect: bun's spec-strict Response rejects a body that undici allows, and the test pushing the same case objects through twice was the bug. 325 tests under both runners, and the whole battery green under bun 1.4.
This job builds TWO commits in one run, and across a package-manager migration those two want different installers. It ran `bun install --frozen-lockfile` against the pnpm merge base, which wrote an untracked bun.lock and edited package.json, and the switch back to HEAD then aborted: error: Your local changes to the following files would be overwritten by checkout: package.json So it failed on git bookkeeping rather than on anything it measures. I called this a frozen-lockfile error when I first read it; the frozen-lockfile problem is real and upstream of this one, and the actual abort is the dirty tree. Both steps now pick the installer from the lockfile in the CHECKED-OUT tree, and the base tree is reset before switching back, so an install can no longer leak across the boundary. The helper is written to a file the steps SOURCE rather than exported through BASH_ENV: sourcing is visible where it is used and does not depend on how Actions invokes bash. It also watches pnpm-lock.yaml alongside bun.lock when deciding whether the lockfile moved, since during the swap the answer lives in whichever file exists. Both branches exercised locally against a real bun tree and a real pnpm tree. The job still gates nothing; this only stops it reporting a failure that says nothing about the diff.
Contributor
Wire-size diff vs merge base
Brotli q11, the quality the edge serves. Every number here is deterministic: Client assetsNo change. 22 files, 77.40 KiB Brotli total. PagesNo change. 48 files, 503.63 KiB Brotli total. Worker bundleNo change. 235.38 KiB gzip. Dictionary deltas (
|
perf-snapshot spawned "pnpm" as a quoted argument to run the dry-run. That is gotcha 29's blind spot exactly: every sweep for `pnpm exec` as a phrase missed it, so it survived the toolchain swap and then failed instantly on a tree with no pnpm. The runner has no bare pnpm either (only corepack), so it failed on BOTH sides of the comparison. The catch turned that into an empty snapshot, and comparing two empty snapshots renders "No change. 0 files" on every category. The check went green while measuring nothing, for a full run. It now runs wrangler's entry through process.execPath, naming no package manager at all, which this job needs anyway because it builds two commits that can disagree about which manager exists. The guard is the other half. A snapshot of nothing is never legitimate here, so record refuses to write one. It checks MODULES as well as files, because a failed dry-run in a tree still holding a .build from an earlier run counts 69 files off those stale bytes and reports 0 modules; measured both ways, and the file-count half alone would have missed it. Controls: broken wrangler path exits 1 and writes no file; the real path records 69 files, 85 modules.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
bun-v1.4.0shipped today, which is the one thing #443 was parked on. This is that branch rebuilt as toolchain-only: 20 files, against 1229.Everything separable landed on
mainfirst — seven restructure slices (#450-#457) and the TypeScript conversion (#459, #460) — so what remains is the package manager and the CI that runs it.Byte-identical to what main builds today
1505 staged files, verified three ways, because the release notes gave three reasons to doubt it:
main)1.4 is a Zig-to-Rust rewrite and switches
node:zlibto zlib-ng. This repo's output is content-addressed and almost entirely compression-derived, so one moved byte would re-mint/a/and/i/URLs and orphan every committed dictionary. Nothing moved, andoxc-minify,lightningcssandminify-htmlresolve to the same pinned versions under both installers.The canary machinery is gone
config/bun-canary.jsonpinned a SHA-256 because thecanarytag rolled daily — it rolled three times on 2026-08-18 alone — and swapping a compiler underneath the artifact graph is exactly what this repo cannot tolerate. A released tag is immutable, so the version string is the guarantee the digest used to provide..github/actions/setup-bunreads the version frompackageManagerand installs it: one declaration, no digest to bump, no daily red CI.It still probes the capability first. Every bun before 1.4 accepts
zstdCompressSync'sdictionaryoption and silently ignores it — the frame decodes, nothing throws, and the only signal is a byte count that never shrank. The action runs that control before a build spends 40 seconds discovering it.Two tests replaced rather than deleted
The workflow-bootstrap test now asserts every workflow that runs bun reaches the shared action and that none names a version inline. The
allowBuildstest becomestrustedDependencies, whose entries must appear inbun.lockso a dead approval cannot pose as a reviewed one.Its first draft demanded a bootstrap from
promote-production.yml, which only echoesbun run deploy:promoteas advice and runs no bun at all. Detection reads commands rather than strings now — the same sharpening every naive scanner in this repo has needed.Measured, not carried over
lens-readerneeds no--ignore-workspace. pnpm walked up to the workspace root and never created the localnode_modules; bun installs thatpackage.jsonon its own. The comment says so, so nobody adds a flag looking for parity with an era that ended.routes:checkstays on node deliberately: it boots workerd through wrangler'screateTestHarness(), which hangs under bun.Gates, under bun 1.4
325 tests, build, lint, typecheck,
routes:check,pages:check,tools:check,perf-budget,photos:check,check-wrangler, the cal suite, lens-reader's 13, and awrangler deploy --dry-run.Before this can merge
The dashboard's two deploy commands still say
pnpm exec wrangler …, and pnpm refuses to run on a tree whosepackageManagersays bun. #445 is the bridge that makes one command string work for both, and it should land first.bun/probe-14is also out there testing whether the build image can installbun@1.4.0while npm still shows1.3.14as latest — that log is worth reading before touching the dashboard.