Skip to content

bun patch: refuse a file: folder or workspace package as the target - #43223

Open
robobun wants to merge 5 commits into
mainfrom
robobun/a2d0b5f3/patch-refuse-folder-of-installed-package
Open

robobun wants to merge 5 commits into
mainfrom
robobun/a2d0b5f3/patch-refuse-folder-of-installed-package

Conversation

@robobun

@robobun robobun commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • bun patch <name> on a file: dependency that a registry package ships reads the folder from the project root. With the bundled-file@2.0.0 fixture, bun patch bundled-file-dep exits 1 with error: error overwriting folder in node_modules: ENOENT after it deletes the installed folder. If the project has an unrelated folder at that path, it copies that folder over the installed package and exits 0. Fixes bun patch reads the file: dependency of a registry package from the project root #43200.
  • Cause: compute_cache_dir_and_subpath (src/install/PackageManager/PackageManagerDirectories.rs, Folder arm) joins every folder path onto the cwd. A registry manifest's path is stored as written (Package::from_npm), relative to the package that declares it.
  • A prepared patch of any folder dependency is a dead end. bun patch --commit fails for every file: directory and workspace member with failed to read from cache (readlink), and bun install never applies a patch to one (patched_package_missing_from_cache finds the folder itself). bun patch <workspace member> also replaces the node_modules symlink with a copy.

Fix

  • crash_if_folder_target (src/install/PackageManager/patchPackage.rs) runs at the three entry points (prepare_patch name and path form, do_patch_commit) before anything in node_modules is touched. It exits 1 for a Resolution::Folder or Resolution::Workspace target: error: cannot patch <name>: it is a file:<folder> dependency, and bun install never applies a patch to a file: folder.
  • The note names the remedy. For a folder a registry package ships: run bun patch <dependent> and edit <folder> inside that package. For a folder in the project (root, workspace, local file: package, root override) and for a workspace member: edit <folder> directly. The wording uses Lockfile::is_trusted_folder_dependency, the same row predicate the resolver and installer use. The refusal itself does not depend on it.
  • Correct because a patch of a folder dependency can never apply, so a refusal before the copy is the only outcome that leaves node_modules intact. The remedy works end to end: a patch of the package that ships the folder applies to the folder, and the nested install links the patched copy.
  • Verified: test/cli/install/bun-patch.test.ts, block "a folder dependency as the target" (8 tests, 7 fail on main). Also bun-patch.test.ts (45 pass), bun-install-patch.test.ts (31 pass), cargo clippy -p bun_install, cargo check -p bun_install --target x86_64-pc-windows-msvc.

Background

  • bun patch <pkg> copies the package's cache entry over node_modules/<pkg> so the user can edit it. bun patch --commit diffs the edited copy against the cache entry, and bun install applies that diff to a cache copy before it links the package.
  • A file: directory or workspace member has no cache entry. The installer links the folder in place (a symlink or per-file symlinks), so the lockfile resolution is the folder path itself, with one of two bases: the project root for paths the project writes, the declaring package for paths a registry manifest writes.
  • The lockfile tree places a registry package's file: dependency under that package's own node_modules, never hoisted.
Notes

Found while working on a bundled file: dependency (#42884). Present on main and on 1.4.3-canary.1 (b52d513).

Manual checks with the debug build, both linkers left at the hoisted default:

  • A root "dep": "file:./dep" and a workspace:* member: bun patch dep and bun patch ws exit 0 on main and print the edit folder. bun patch --commit node_modules/dep then fails with ENOENT: No such file or directory: failed to read from cache (readlink). node_modules/ws is a real directory afterwards, no longer the workspace symlink. Both are refused now, with the edit <folder> directly note.
  • The remedy: bun patch bundled-file, edit node_modules/bundled-file/vendor/bundled-file-dep/index.js, bun patch --commit node_modules/bundled-file, rm -rf node_modules, bun install. node_modules/bundled-file/node_modules/bundled-file-dep/index.js has the edit. The last test in the block pins that round trip.

Shape: a first version refused only the folder of a registry package, with a new lockfile predicate layered on the base directory rule that #38856, #38994 and #38816 change. A self-review found that the reason for the refusal (a patch of a folder never applies) holds for every folder target, so the change now refuses every Folder and Workspace resolution and uses no new lockfile predicate. The declaring package only picks the note text. Self-reviewed: 5 concerns raised, 5 addressed.

Related: #43160 stops the delete on the ENOENT path for other errors. #43199 refuses a bundled dependency at the same three call sites. #43178 keeps nested dependencies when the patched package itself is prepared. This change is independent of them in code and can merge in any order.


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

@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: e7e39f10-9f51-4f7c-9170-0487b25a3feb

📥 Commits

Reviewing files that changed from the base of the PR and between 663508d and cb0ba74.

📒 Files selected for processing (2)
  • src/install/PackageManager/patchPackage.rs
  • test/cli/install/bun-patch.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.


Walkthrough

The patch flow now rejects workspace, link:, and file: folder targets before cache access. CLI tests verify errors, unchanged files, missing patch metadata, symlinks, and successful edits through the owning package.

Changes

Folder target validation

Layer / File(s) Summary
Validation helper and patch-flow integration
src/install/PackageManager/patchPackage.rs
crash_if_folder_target rejects unsupported folder resolutions. Path-based and name/version-based preparation, plus commit, invoke the validation before cache access.
Folder dependency test coverage
test/cli/install/bun-patch.test.ts
Tests cover file:, workspace, link:, and shipped folder targets, including diagnostics, unchanged content, symlinks, patch metadata, and reinstall behavior.

Suggested reviewers: jarred-sumner

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to cb0ba

Some folder-target patch attempts may receive guidance for the wrong parent package or suggest direct editing when the target is registry-shipped. The command still refuses the unsupported patch safely, so this is bounded but should be corrected.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: bun patch refuses file: folder and workspace package targets. It is concise and specific.
Description check ✅ Passed The description explains the problem, fix, expected behavior, user remedies, testing, and verification results. It does not use the template headings ### What does this PR do? and `### How did you v…
Linked Issues check ✅ Passed Issue #43200 requires bun patch to refuse file: folder targets that cannot be applied by install or committed from cache. The change adds crash_if_folder_target checks before patch preparation a…
Out of Scope Changes check ✅ Passed The changes stay within the same patch-safety scope as issue #43200. The workspace and link: refusals enforce the same rule that bun install does not apply patches to directly linked folder target…

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

The automated review is paused by a plan limit, so there are no review findings to address yet. CI is running on 459c3ad. The diff is ready for a maintainer review.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

4 verified lower-impact observations (convention, logging or cleanup points) were not posted.

Comment thread src/install/PackageManager/patchPackage.rs
Comment thread src/install/PackageManager/patchPackage.rs
Comment thread src/install/PackageManager/patchPackage.rs Outdated
Comment thread src/install/PackageManager/patchPackage.rs Outdated
Comment thread src/install/PackageManager/patchPackage.rs Outdated
@robobun

robobun commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

On the merge risk note about the note text: the refusal itself does not depend on which package declares the folder. The note names the declaring package only when the path is not user authored (Lockfile::is_trusted_folder_dependency is false for the row), which is the case where the folder lives inside that package. A root override with the same name makes the path project relative, so 'edit directly' is right there. When two registry packages share one folder row, the note names the first of them. Both ship the folder, so the remedy holds.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

One verified lower-impact observation (a convention, logging or cleanup point) was not posted.

@robobun

robobun commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Both automated reviews report nothing further on cb0ba74 and every review thread is resolved. The one red CI lane is test/bake/deinitialization.test.ts on alpine aarch64, which also fails on main and is unrelated to bun patch. The rest of the build is green so far.

@robobun

robobun commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 9:23 PM PT - Sep 17th, 2026

✅ @robobun, your commit d6155a5543a6fd9c4d56301a3ecfee4b3919921f passed in Build #117541! 🎉


🧪   To try this PR locally:

bunx bun-pr 43223

That installs a local version of the PR into your bun-43223 executable, so you can run:

bun-43223 --bun

@robobun

robobun commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

One overlap with #43199, for whichever PR merges second.

Both PRs add a guard at the same line of the path arm in prepare_patch, right after let name = lockfile.str(&package.name).to_vec();. Git will report a conflict there.

For the bundled file: dependency of bundled-file@1.0.0 both guards fire, with different messages. #43199 has a test that expects cannot patch bundled-file-dep: it is a bundled dependency of bundled-file, which ships it in its own tarball for that target. The tests here use bundled-file@2.0.0, where the dependency is not bundled, so only crash_if_folder_target fires.

With crash_if_only_bundled above crash_if_folder_target, both test blocks pass. I checked the logic, not a merged build: in 2.0.0 the dependency is reachable without a bundled edge, so the bundled check lets it through to the folder check. If a maintainer prefers the folder message for the bundled file: case, the test in #43199 is the one to change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bun patch reads the file: dependency of a registry package from the project root

1 participant