turbo
ea08facf - fix(bun): Preserve overrides objects, trustedDependencies, workspace bins and git integrity through prune; accept lockfileVersion 3 (#13740)

Commit
2 days ago
fix(bun): Preserve overrides objects, trustedDependencies, workspace bins and git integrity through prune; accept lockfileVersion 3 (#13740) ### Description Bun's next release changes a few things about `bun.lock` that `crates/turborepo-lockfiles/src/bun/` does not handle yet, and while going through the format we found a handful of existing fields that `turbo prune` drops or rewrites into a shape Bun does not read. This PR fixes all of them in one pass over the Bun parser/emitter; every item is independent and small. Background on the Bun side: - `lockfileVersion: 2` shipped in oven-sh/bun#31539 and turbo accepts it since #13119 (2.10.3). oven-sh/bun#38333 (the upcoming package-manager work) does not change the text format further. - The next Bun release adds *nested overrides*: rules scoped to the dependencies of one parent package. They are stored inside the existing `overrides` section as object values, and the file is stamped `lockfileVersion: 3` only when at least one such rule exists (it goes back to 2 when they are removed). Objects are accepted by Bun's reader at every version. The shape is: ```jsonc "overrides": { "lodash": "4.17.21", // flat rule, unchanged "micromatch": { ".": "4.0.5", "picomatch": "2.3.2" }, // "." = flat rule for micromatch itself "webpack@^4": { "terser": "4.8.1" }, // only applies under webpack matching ^4 }, ``` Today either the object value (`invalid type: map, expected a string`) or the version stamp makes `BunLockfile::from_str` fail, so the lockfile is treated as absent and `turbo prune` errors with `Cannot prune without parsed lockfile` as soon as a repo adds one nested rule. #### 1. Object values in `overrides`, `lockfileVersion: 3` `overrides` is now `Map<String, OverrideValue>` with an untagged string-or-object value. Prune keeps copying the whole section verbatim, exactly as it already does for flat overrides (`subgraph.rs`): Bun's `--frozen-lockfile` diffs the complete override set in the lockfile against the root package.json, which prune also copies verbatim, so trimming any rule (flat or nested) would fail pruned installs. `apply_overrides` applies string rules and an object's `"."` entry; keys carrying a parent range (`"webpack@^4"`) never match a bare name, and nested child rules need no resolution logic in turbo because Bun materializes them as nested lockfile keys (`webpack/terser`), which resolution already follows. `LockfileVersion::V3` is added. Versions above the newest known one now parse with a `tracing::warn!` instead of an error, since every Bun revision so far has only added to the schema and the npm/pnpm parsers in this crate already behave that way; negative versions are still rejected. Adding or removing the first nested rule flips 2 <-> 3 and therefore registers as a global lockfile change once each; that seems correct and is left as is. #### 2. `trustedDependencies` copied verbatim on prune Prune currently emits the section empty. Bun diffs the lockfile's `trustedDependencies` against the set declared in the (verbatim-copied) package.json files on every install, so an empty section registers every declared entry as newly added. On released Bun versions this is only bookkeeping (`--frozen-lockfile` does not compare it), but Bun's next release uses "did the manifest diff report anything" to decide how aggressively to clean the tree before the frozen comparison, which can turn that spurious diff into a visible difference; that signal is expected to be tightened on the Bun side as well, but copying the section is the consistent choice either way and matches how `overrides`/`catalog(s)` are handled. Trade-off worth knowing: if the only declarer of a trusted name was a workspace that got pruned away (the `bun-v1-issue-12744` fixture declares it in `apps/bot`), the copied entry is now reported as removed instead of nothing being reported. Bun's frozen check compares the resolved tree, not this section, so neither variant should affect `bun install --frozen-lockfile` on released Bun versions, and the root package.json is the documented place for the field. #### 3. Workspace-level `bin` / `binDir` Bun writes a workspace's `bin`/`binDir` into its `workspaces` entry and the installer links workspace bins from there. `WorkspaceEntry` did not have the fields, so a pruned install linked no bins for workspace packages. They are now carried through (`bin` is a string or an object). #### 4. git/github integrity element git/github entries are `[ident, INFO, bun-tag, integrity?]`; the deserializer stopped after the bun-tag and the emitter always wrote 3 elements, silently removing the content pin from every git dependency in the pruned lockfile. `PackageEntry` gains an `integrity` field that is only read/written for git/github entries. #### 5. Root workspace `name` is optional Bun omits `name` from the `""` workspace entry when the root package.json has no name; `WorkspaceEntry.name` was required, so such repos failed to parse entirely. It now defaults to empty and is skipped on output, which is also how Bun represents it internally. #### 6. Local tarball and `name@root:` entries `PackageIdent::Tarball` only matched the literal string `tarball` (a misreading of the schema comment), so `["bar@./bar-0.0.2.tgz", INFO, integrity]` was classified as a registry package and re-emitted as `[ident, "", INFO, integrity]`, which Bun rejects with `Expected an object`. Tarballs are now recognized the way Bun does it (by `.tgz`/`.tar.gz` suffix) and use the existing `[ident, INFO, integrity?]` path. Similarly `name@root:` entries were parsed into `RootInfo` but the emitter never consulted it, and `RootInfo.bin` could not hold an object bin; they are now written as `[ident, { bin, binDir }]`. #### 7. `turborepo-devtools` watcher `RELEVANT_FILES` listed `bun.lockb` but not `bun.lock` (the package watcher has both), so the devtools graph never rebuilt on text lockfile changes. Not included: a Bun-generated `lockfile-tests` fixture. All existing Bun fixtures there pin `packageManagerVersion` <= 1.3.x, and the v3 shape needs a Bun that is not released yet; happy to add a `bun-v2-*` fixture now and a `bun-v3-nested-overrides` one once that release is out, if you want them in this PR or a follow-up. ### Testing Instructions - `cargo test -p turborepo-lockfiles` (312 passed). New unit tests in `bun/test.rs`: v3 file with object overrides (parse, `"."` applied, ranged/nested rules not applied, verbatim through prune, reparse), object overrides at v1, unknown newer version accepted / negative rejected, `trustedDependencies` through prune, workspace `bin`/`binDir` (string, object, `binDir`) through prune, root workspace without a name, git/github 4-element entries through prune, local tarball / remote tarball / `@root:` entries (with string, object and no bins) through prune. `de.rs`/`ser.rs` gain matching `test_case`s, `types.rs` gains tarball/root ident tests. - `cargo test -p turborepo-devtools watcher`, `cargo test -p turborepo-repository bun`. - `cargo fmt`, `cargo clippy -p turborepo-lockfiles --all-targets` clean. - Not run: the `lockfile-tests` e2e harness (see above; the encoded shapes asserted in the new tests are copied from what Bun's writer produces). This PR was written by Claude on behalf of the Bun team; a Bun maintainer is sponsoring it and will respond to review.
Author
Parents
Loading