install: fix bun patch --commit cache path for git, github, and tarball dependencies
Repro
printf '{"name":"r","dependencies":{"@types/betterdiscord":"github:zerthox/betterdiscord-types"}}' > package.json
bun install
bun patch @types/betterdiscord
echo '// x' >> node_modules/@types/betterdiscord/index.d.ts
bun patch --commit 'node_modules/@types/betterdiscord'
error: failed to make diff error: Could not access '/root/.bun/install/cache/@GH@@@@1'
exit code 2 and no patch is written. The pristine copy actually lives at @GH@Zerthox-betterdiscord-types-7ee79af@@@1. The same failure hits git dependencies (Could not access '.../@G@') and tarball dependencies (Could not access '.../@T@0000000000000000@@@1'). npm registry dependencies patch fine.
Cause
do_patch_commit loads its own copy of the lockfile (it runs before the install flow populates manager.lockfile), then hands the package's Resolution to compute_cache_dir_and_subpath, which resolved the resolution's strings against manager.lockfile's string buffer. That buffer is empty at this point. Lockfile strings longer than 7 bytes are stored as offsets into their lockfile's string buffer, so repository.resolved / tarball URLs sliced against the wrong buffer come back empty, and the computed cache folder name collapses to just its prefix and cache-version suffix (@GH@ + `` + @@@1). npm resolutions were unaffected because the version is stored inline in the Resolution value.
This mismatch predates the Rust port: the same flow segfaulted in v1.1.42 (#18792), where manager.lockfile was an undefined pointer rather than an empty lockfile.
Fix
compute_cache_dir_and_subpath now takes the string buffer that owns the resolution's strings (resolution_string_bytes: Option<&[u8]>). The two bun patch --commit call sites pass the locally loaded lockfile's buffer; bun patch and the patch-apply task keep resolving against manager.lockfile (their resolutions come from it). All non-npm arms (git, github, local/remote tarball, folder, workspace, symlink) go through the new parameter.
Windows: patch filename escaping
With the cache path fixed, the same flow on Windows hit a second failure: the patch filename is {name}@{resolution}.patch, and git/github resolution labels contain : (pkg@github:owner/repo#sha.patch), which NTFS rejects in filenames (STATUS_OBJECT_NAME_INVALID when renaming the patch into patches/). escape_patch_filename now escapes the NTFS-reserved printable characters (: ? * " < > |) the same way it already escapes /. This applies on every OS so a patches/ directory committed from macOS or Linux stays checkoutable on Windows.
Known limitation: workspaces with the isolated linker
In a workspace using the isolated linker (the default for new projects), the reinstall that bun patch --commit runs after writing the patch hangs. That hang is a pre-existing bun install bug, reproducible on released bun with no patch commands involved: a workspace with an existing patchedDependencies entry for a github: dependency hangs the same way on a plain bun install (it completes with --linker hoisted). With this fix, bun patch --commit --linker hoisted completes end to end in workspaces; the isolated-linker install hang is tracked separately.
Verification
Three new tests in test/cli/install/bun-patch.test.ts run the full install, bun patch, edit, bun patch --commit flow offline and assert the patch file contents, the patchedDependencies key, and the reinstalled patched package:
github:dependency served by a local tarball server viaGITHUB_API_URL- git dependency cloned from a local server over git's dumb HTTP protocol
- local
file:tarball, committed by package name to also cover the name-based lookup path
All three fail on bun 1.4.0-canary.1 with the Could not access errors above and pass with this change, on Linux and Windows. The rest of bun-patch.test.ts (34 tests) and bun-install-patch.test.ts (18 tests) pass.
no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/install/bun-patch.test.ts