Skip to content

fix(bun): accept bun.lock lockfileVersion 2 — bun 1.4 re-versioned an unchanged grammar - #224

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/bun-lockfile-version-2
Open

fix(bun): accept bun.lock lockfileVersion 2 — bun 1.4 re-versioned an unchanged grammar#224
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/bun-lockfile-version-2

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Why main is red

The required hosted-e2e check fails on every push (main run 32378974818 and the post-#222 run; every open PR too). The job installs floating npm install -g bun@1, which since bun 1.4.0's release (~2026-08-20) resolves to 1.4.0 — and bun 1.4.0 writes bun.lock with "lockfileVersion": 2. Both of our bun lock version gates refused anything but 1, so the bun_hosted_install_proof leg fails at the rewrite step (redirect_bun_lock_unsupported: "bun.lock lockfileVersion is not 1; re-lock with bun >= 1.3"). 15/16 other legs stayed green; the bun crawler was never affected — only the version gates refuse.

What lockfileVersion 2 actually is

Empirically (docker oven/bun:1.3.14 vs oven/bun:1.4.0, same minimist fixture): the two locks are byte-identical except the version integer"configVersion": 1 is present in both, so it is not new in 1.4.

Bun's 1.4 breaking-changes tracker (oven-sh/bun#28792, PR #31539) confirms the bump gates stricter parse-time checks over an unchanged emitted grammar:

"bun.lock default lockfileVersion is now 2. v2 lockfiles require integrity hashes for off-registry npm tarballs and reject unsafe git .bun-tag values at parse time. Existing v0/v1 lockfiles continue to load. Older Bun versions cannot read v2 lockfiles."

Both new constraints are compat-neutral for us: our hosted/vendored URL 3-tuples always carry a sha512 (satisfying the off-registry-integrity rule by construction), and we never emit or edit git entries.

The fix

One shared gate, widened from {1} to {1, 2} (fail-closed on everything else, message updated to "not 1 or 2"):

  • crates/socket-patch-core/src/vendor/bun_lock_text.rsSUPPORTED_LOCK_VERSIONS / check_lock_version, the single production gate, with three call sites:
    • crates/socket-patch-core/src/patch/redirect/mod.rs (rewrite_bun_lockredirect_bun_lock_unsupported)
    • crates/socket-patch-core/src/vendor/bun_lock.rs (vendor backend → vendor_lockfile_version_unsupported)
    • crates/socket-patch-core/src/vendor/lock_inventory.rs (inventory_bun — auto-benefits; now test-covered)
  • Plus a fourth v1-only assumption found while proving the fix: e2e_vendor_bun_build.rs's fixture precondition asserted lockfileVersion: 1 and failed under real bun 1.4.0 — now accepts 1 or 2.
  • Docs: CLI_CONTRACT.md + docs/ecosystems.md bun paragraphs.

Tests (RED-first, verified by reverting the gate to v1-only)

With the production gate temporarily re-pinned to [1], all the new legs fail (v2 refused): the bun_lock_text gate unit, bun_lock::lock_v2_vendors_like_v1, the lock-inventory v2 leg, redirect::tests::bun_lock_warning_branches, and redirect_golden on the new fixture. With the widened gate, everything is green:

  • Gate unit: 1 and 2 accepted; 0/3/99 and missing/non-integer/string heads refused with version + remedy named.
  • Redirect: unit v2 accept + v3 refusal ("not 1 or 2"); in_process_redirect adds scan_redirect_rewrites_bun_lock_v2 (version line preserved verbatim) and scan_redirect_refuses_bun_lock_v3 (fail-closed: lock byte-identical, redirected: 0, warning in the --json envelope).
  • Vendor: lock_v2_vendors_like_v1 + the existing refusal test re-pinned to version 3; inventory reads v1/v2 identically and yields nothing on v3.
  • Golden fixtures: new npm/bun/lock-v2 case; the existing lock-version-unsupported input re-pinned 2 → 3 (2 is now a supported version, the refusal case needs a future one). No existing expected/ bytes were re-blessed. Loud note: these fixtures are the shared cross-language contract — the new lock-v2 case was authored on the Rust side, so the depscan TS twin (golden.test.ts + its bun rewriter gate) needs the matching v2 acceptance and a fixture sync, same as the gem.ts port.

Proof against the real thing

With real bun 1.4.0 on PATH (locally installed, same version CI's bun@1 resolves):

  • e2e_redirect_bun_build: all legs run un-skipped — the main leg now exercises a native v2 lock end-to-end (real bun install, hosted rewrite, fresh-checkout bun install --frozen-lockfile materializes the patched bytes; tampered-tarball twin still refuses). New lock-v2 leg pins the forced-version path and asserts the version line survives the rewrite verbatim.
  • e2e_vendor_bun_build: full vendor → frozen install → revert capstone green on a native v2 lock.
  • The exact red CI leg: cargo test -p socket-patch-cli --test e2e_hosted_production bun_hosted_install_proof -- --ignored against real production — green (3.12s).

Gates: cargo test -p socket-patch-core --lib (2540), redirect_golden, in_process_redirect (43), cargo clippy --workspace --all-features -- -D warnings clean, touched files fmt --check clean (the repo has pre-existing fmt drift in untouched files; CI has no fmt gate).

Deliberately NOT changed

CI's floating npm install -g bun@1 stays floating: it caught this drift within a day of the release — that is precisely the hosted suite's job.

🤖 Generated with Claude Code


Note

Medium Risk
Touches lockfile surgery and integrity-pinning for bun hosted/vendor paths, but only widens an existing fail-closed version gate over an empirically unchanged grammar. Unsupported versions still refuse with the lock left byte-identical.

Overview
Accepts bun 1.4's "lockfileVersion": 2 for hosted redirect, vendor, and lock inventory. Bun 1.4 re-versions the same emitted grammar as v1 (stricter parse checks only); the shared check_lock_version gate now allows {1, 2} and still fail-closes on anything else, preserving the version line verbatim.

This unblocks CI that installs floating bun@1 (now 1.4) and was refusing rewrites with redirect_bun_lock_unsupported. Tests cover v2 rewrite/vendor/inventory, v3 refusal, a new lock-v2 golden, and e2e fresh-checkout install of patched bytes. Docs (CLI_CONTRACT.md, ecosystems.md) match.

Reviewed by Cursor Bugbot for commit 42a4b55. Configure here.

… unchanged grammar

bun 1.4.0 bumped the default bun.lock lockfileVersion to 2 (oven-sh/bun
PR #31539). The bump gates stricter PARSE checks — integrity hashes
required for off-registry npm tarballs, unsafe git .bun-tag values
rejected — behind an UNCHANGED emitted grammar: a 1.3.14 and a 1.4.0
lock of the same fixture are byte-identical except the integer
(verified empirically in docker). Our hosted/vendored tuples always
carry a sha512, satisfying the new off-registry-integrity rule by
construction.

CI's hosted-e2e job installs floating bun@1, which now resolves to
1.4.0 — the shared version gate (check_lock_version in bun_lock_text)
refused everything but 1, so bun_hosted_install_proof went red on every
push to main. Widen the gate to {1, 2}; refuse anything else with the
updated fail-closed message. The floating bun@1 is deliberately kept:
catching exactly this drift is the hosted suite's job.

Covered call sites: redirect rewriter (redirect_bun_lock_unsupported),
vendor backend (vendor_lockfile_version_unsupported), lock inventory,
plus a v1-only fixture assertion in the vendor bun e2e capstone.

New golden fixture npm/bun/lock-v2 (Rust-authored — the depscan TS twin
needs the matching acceptance + fixture sync); the existing
lock-version-unsupported input re-pinned from 2 to 3 (2 is now
supported). No existing expected/ bytes re-blessed.

Proofs, all with real bun 1.4.0 on PATH: redirect + vendor e2e
capstones green on NATIVE v2 locks (fresh-checkout frozen installs of
patched bytes, tamper refusal), and the exact red CI leg
bun_hosted_install_proof green against real production.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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