Skip to content

Deduplicate package manager and CLI command helpers - #32022

Open
alii wants to merge 7 commits into
mainfrom
claude/split/install-cli
Open

Deduplicate package manager and CLI command helpers#32022
alii wants to merge 7 commits into
mainfrom
claude/split/install-cli

Conversation

@alii

@alii alii commented Jun 9, 2026

Copy link
Copy Markdown
Member

What this does

Consolidates duplicated blocks across the package manager and CLI commands (bun install/add/update/link/unlink/outdated/update --interactive/pack/bunx/create/run --filter|--parallel|--sequential/repl): shared workspace enumeration and lockfile loading (src/runtime/cli/workspace_helpers.rs), shared run-process plumbing (src/runtime/cli/run_processes_shared.rs), the two PackageManager init paths, the workspace package.json load-or-exit helper, the trusted-dependency lifecycle enqueue, the tarball error dispatch, and the hosted_git_info/npm/yarn dedups. Net -1.05k lines (+2251/-3303).

Split from #31912 (whole-repo simplification pass, closed in favor of module-scoped splits). This PR only moves and removes code; zero intended behavior change. It was originally stacked on #32000, which has since been closed (its deletions landed on main separately), so it now targets main directly.

Behavioral equivalence

Audited hunk by hunk against main: error messages, log-flush order, and exit semantics are byte-identical at every call site, with three reviewed edge-path exceptions:

  1. bunx: the second cached-bin probe (the package.json bin-name retry) now builds its probe path with the platform separator, matching the first probe; it previously used literal /. On Windows the probe string changes bytes but names the same file: which_win takes the absolute-path branch and every consumer (exists check, stat, CreateProcessW) accepts both separators.
  2. The shared get_with_path_or_exit and load_lockfile_or_crash helpers ignore a failed Log::print to stderr where two of the replaced sites propagated it with ?. That error path is unreachable: the writer behind Output::error_writer() discards fd write failures and always returns Ok (adapter_write_all in src/sys/lib.rs), so the old ? was dead code.
  3. bun update --interactive with --filter now routes WorkspaceFilter::init allocation failure through bun_core::handle_oom (matching bun outdated) instead of .expect("OOM").

Where the original duplicates genuinely differed (trusted-deps lockfile gating in PackageInstaller, the per-backend Windows walkers, the two PackageManager init paths, gitlab's distinct URL extractor, pack's own package.json error wording), the helpers parameterize the difference instead of collapsing it.

write_shared_default_fields was re-derived against the current PackageManager struct: both init paths still write all 70 fields exactly once (58 shared + 12 per path), with every value identical to main's inline writes.

Rebase onto main

Main moved ~780 commits since this was written. Besides textual conflicts, the following drift was found and folded in:

  • runTasks.rs: install: don't re-download a tarball that already failed #34103 replaced network_dedupe_map.remove with mark_network_task_failed at both tarball error sites; the shared dispatch_tarball_error does the same.
  • PackageInstaller.rs: should_ignore_lifecycle_scripts lost its tree_id argument; the shared enqueue_lifecycle_scripts_for_trusted follows. Main's new has_trusted_dependency check sits outside the consolidated block and is unchanged.
  • PackageManager.rs: main removed the cpu_count, ci_mode, default_features, and progress_name_buf_dynamic fields, renamed cache_directory_, and added updating_catalogs; the shared field list reflects that.
  • filter_run.rs / multi_run.rs: main fixed the Windows uninstall to pass the handler address to SetConsoleCtrlHandler; the shared AbortHandler carries the fix.
  • hosted_git_info.rs: main deleted the URL formatters this PR was also consolidating, so only the extract consolidation (build_result + the shared bitbucket/gist/sourcehut tail) remains, using strings::split per strings: route all byte search through highway, deny the paths around it #37052.
  • run_command.rs / repl_command.rs: wire_install_options uses main's install_preference field instead of the old prefer-offline/prefer-latest booleans.
  • PackageJSONEditor.rs: main's new edit_catalogs_after_update contained a third copy of the version-literal block this PR deduplicates; it now uses replacement_version_literal too.
  • Dropped the parts superseded on main (get_with_source, the FilterRun/MultiRun aliases, PackCommand::exec), and matched main's narrowed visibility (fn finalize, pub(crate) helpers).

Tests

  • Existing tests added with the consolidation pin the shared error exits: package.json name validation shared by bun link/bun unlink (bun-link.test.ts), the pack package.json parse failure (bun-pack.test.ts), and the missing-lockfile error shared by bun outdated and bun update --interactive (bun-install-registry.test.ts, update_interactive_install.test.ts).
  • New in this rebase: fromUrl cases for the per-host differences the shared extractor is parameterized on (rejected aux segment, missing user/project, gist's optional user, and undecodable segments returning null for gist/sourcehut but throwing for bitbucket), and a bun update --latest case for aliased catalog entries. Both new sets pass against a release build of main as well, i.e. they describe the pre-existing behavior.

Verification

  • cargo check --workspace on all 10 CI targets (bun run rust:check-all), clippy on bun_install/bun_runtime, rustfmt, prettier, and test/internal/source-lints are clean.
  • Debug-build runs of the touched suites pass: hosted-git-info, catalogs, bun-update, bun-link, bun-pack, update_interactive (all three files), the outdated and bundledDependencies/optionalDependencies/tarball groups of bun-install-registry, filter-workspace, multi-run, repl, run-autoinstall, bunx, bun-create, bun-run, bun-patch, bun-install-patch, yarn-lock-migration, bun-install, isolated-install, bun-install-retry, bun-install-tarball-integrity, bun-add, bun-remove, bun-workspaces, bun-install-lifecycle-scripts. The remaining local failures reproduce identically with the released binary or are debug-build artifacts (bitbucket/gitlab clones needing network, the debug install-failure trace dump in bun-link, 5s timeouts on chains of debug binaries, Bun.version being -debug in the bunx user-agent test, and the shared /tmp/bun-node-debug wipe racing under describe.concurrent, which is tracked separately).

no test proof · iteration 15 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/install/bun-install-registry.test.ts test/cli/install/bun-link.test.ts

@alii

alii commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@robobun adopt

@robobun

robobun commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Audit done: the diff is behavior-preserving (edge-path notes in the PR body) and hermetic tests pin the consolidated error exits. Rebased directly onto main and retargeted; nothing here has landed on main separately. All 15 CodeRabbit threads are resolved (two comment fixes and six test cases in ca15187; the rest pre-existing behavior kept on purpose, with the two real ones filed separately). rust:check-all is clean on all 10 targets. CI: build 92351 is final at 195 passed, 1 failed. The failing job is one darwin 14 aarch64 agent whose environment is broken (no default rustup toolchain, so grpc-js/test-tonic fails; its system resolver times out on bogus hostnames, so two resolve-dns cases fail); both are reported for triage and neither is near this diff, which has now passed every other lane on two full runs. Ready for a maintainer pass.

@robobun

robobun commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator
Updated 3:20 PM PT - Aug 11th, 2026

@robobun, your commit b5b9aaa has 2 failures in Build #92351 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 32022

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

bun-32022 --bun

@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.

I didn't find any bugs, but this is a large refactor (35 files, -1.2k net) that reorganizes unsafe placement-init in PackageManager.rs, raw-pointer aliasing in install_with_manager.rs/patchPackage.rs, Windows FFI walkers in PackageInstall.rs, and the bunx cached-bin TOCTOU probe — worth a human pass on the equivalence claims before merging.

Extended reasoning...

Overview

This PR deduplicates ~1.2k lines across the package manager core (src/install/PackageInstall.rs, PackageInstaller.rs, PackageManager.rs, PackageManagerEnqueue.rs, install_with_manager.rs, patchPackage.rs, runTasks.rs, hosted_git_info.rs, npm.rs, yarn.rs, isolated_install.rs) and the CLI commands (bunx, create, link/unlink, outdated, update --interactive, pack, repl, filter_run/multi_run, install/add). It introduces two new shared modules (workspace_helpers.rs, run_processes_shared.rs) and a handful of factored helpers (write_shared_default_fields, walk_install_dir_windows, parse_root_package, load_path_package, replacement_version_literal, format_template/user_project_committish, etc.). New hermetic tests pin the consolidated error-exit paths. The description documents three intentional edge-path byte-level differences and asserts the rest is behavior-identical.

Security risks

The bunx change touches the post-install cached-binary probe in a world-writable temp dir, which is the TOCTOU-hardened path that gates execution on is_trusted_cached_binary. The refactor preserves the uid check and the exec call shape, but it does change the second probe's path-separator bytes on Windows (called out in the description). No new attack surface is introduced, but given this is an exec gate I'd want a human to confirm the consolidation is byte-equivalent on all platforms.

Level of scrutiny

High. This is production-critical: it reshapes the core install loop, the lockfile load/parse path, the lifecycle-script trust gating, and signal handling for multi-process runners. A substantial fraction of the moved code is unsafe — per-field placement writes into the PackageManager singleton (now split between write_shared_default_fields and two callers that must together cover every field), raw-pointer disjoint-borrow splits in parse_root_package/load_path_package, and Windows FFI buffer slicing in walk_install_dir_windows. The equivalence argument for each is plausible and well-commented, but verifying that the two init paths still write every PackageManager field exactly once, and that the aliasing reshapes preserve the original disjointness invariants, is the kind of review a human should sign off on.

Other factors

The bug-hunting pass found nothing, the touched test suites are reported green against the base, and the PR adds tests for the consolidated error paths. It is also stacked on #32000. None of the touched files have a CODEOWNER. Given the breadth (35 files), the density of unsafe, and the criticality of the install path, I'm deferring rather than approving.

@robobun

robobun commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Agreed this should get a human look. For whoever does the pass, here's where each flagged area was verified during the adopt audit:

  • write_shared_default_fields (PackageManager.rs): the two init paths (init and init_with_runtime_once) were compared against shared+remainder by a normalized field-name multiset diff; every field is written exactly once in both paths, with the genuinely divergent fields (options, log, root_dir, env, cpu_count, thread_pool, root_package_json_file, event_loop, original_package_json_path, subcommand, workspace_name_hash, workspace_package_json_cache, root_package_json_name_at_time_of_init) left at their call sites with their original per-path values.
  • parse_root_package / load_path_package aliasing: the disjoint-storage invariants are the same as the originals and are spelled out in the SAFETY comments at the helper and each call site (lockfile is either a stack local or the Box heap allocation, disjoint from *manager; log_mut() is the decoupled backref; the lockfile is always restored before any fallible return).
  • walk_install_dir_windows (PackageInstall.rs): the helper body matches both deleted walkers; only the per-File action differs and that is exactly what the closure parameterizes. The queue-based hardlink walker was intentionally left untouched.
  • bunx cached-bin probe: is_trusted_cached_binary(destination, uid) gating and the Run::run_binary call are unchanged; the only byte-level delta is the second probe's separator on Windows (note 1 in the PR body), which resolves to the same file via which_win's absolute-path branch.

Happy to expand on any of these.

@robobun
robobun force-pushed the claude/split/install-cli branch from a1c67b5 to 2f5dd36 Compare July 12, 2026 20:48

@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.

No bugs found, but this is a large consolidation (35 files, +2.2k/-3.4k) across the package-manager core, bunx's cached-bin security probe, and several unsafe raw-pointer aliasing sites — worth a human pass.

What was reviewed:

  • write_shared_default_fields field coverage vs. both init paths; parse_root_package/load_path_package disjoint-storage invariants preserved.
  • walk_install_dir_windows matches both deleted per-backend walkers; the non-Windows copy() fn is not dead (still called on the #[cfg(not(windows))] path).
  • bunx try_run_cached_bin keeps the is_trusted_cached_binary gate and Run::run_binary args; only the second probe's path separator changes on Windows.
  • hosted_git_info user_project_committish collapse checked against bitbucket/gist/sourcehut originals — aux-segment reject, user-optional, and error-as-none flags reproduce each variant's behavior.
Extended reasoning...

Overview

This PR consolidates ~1.2k net lines of duplicated code across the package manager (src/install/) and CLI commands (src/runtime/cli/) into shared helpers: workspace_helpers.rs, run_processes_shared.rs, walk_install_dir_windows, write_shared_default_fields, format_template/user_project_committish in hosted_git_info, and about a dozen smaller extractions. It touches 35 files across install, add/update, link/unlink, outdated, pack, bunx, create, filter/multi-run, and repl. Four new hermetic tests pin the consolidated error exits.

Security risks

The bunx try_run_cached_bin helper wraps the post-install cached-binary probe, which includes the is_trusted_cached_binary TOCTOU hardening against attacker-planted symlinks in a world-writable temp dir. The consolidation preserves the uid check, the Run::run_binary argument list, and the untrusted-binary log-and-skip fallthrough; the only byte-level change is the second probe's path separator on Windows (documented as edge-path exception 1). No new inputs reach security-sensitive parsing, and no validation was loosened.

Level of scrutiny

High. This is production-critical code (the install/resolve/lockfile hot paths, process spawning, and a security-gated exec path) with extensive unsafe blocks whose correctness depends on subtle aliasing and provenance reasoning: write_shared_default_fields does per-field placement writes into a partially-initialized singleton, parse_root_package/load_path_package split &mut PackageManager and &mut Lockfile through raw pointers, and analyze_dependencies_and_install stores raw *mut ContextData across a re-entrant bundler callback. Each site's SAFETY comments were carried over and appear sound, but this is exactly the class of refactor where a missed field-write or a widened aliasing window is easy to overlook and hard to test.

Other factors

  • The PR body documents three intentional edge-path deviations and where the original duplicates genuinely differed (parameterized rather than collapsed). robobun's adopt audit already left a per-helper verification breakdown on the thread.
  • The filter_run/multi_run AbortHandler merge collapses two previously-separate SHOULD_ABORT statics into one shared static; both commands are mutually exclusive CLI entry points, so the sharing is safe, but it's a design-shape change a maintainer should be aware of.
  • CI is reported green modulo a known-flaky bunx test tracked separately, and rust:check-all covers the Windows-gated code, but the Windows install-backend walkers and the pack PackQueueOpenMode fd-ownership shuffle (raw Fd + CloseOnDropFile::from_fd + into_raw()) are the kind of thing where a maintainer familiar with the original Zig should confirm the ownership transfer is identical.

Given the breadth, the density of unsafe, and the security-adjacent bunx path, I'm deferring rather than approving.

@robobun

robobun commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

On the two additional items from this pass:

  • Shared SHOULD_ABORT static (run_processes_shared.rs): bun run --filter and bun run --parallel/--sequential are mutually exclusive CLI entry points; exactly one runner executes per process and neither resets the flag, so merging the two per-file atomics into one is equivalent. Both originals and the shared version store and load with SeqCst.
  • pack PackQueueOpenMode fd ownership: the change is CloseOnDrop(fd) -> owning File, plus into_raw() where the fd is handed off. File::from_fd and into_raw are transparent (no side effects), both Drop impls call the same close(), and File::drop's INVALID/stdio guard is vacuous for these freshly-opened fds. Drop timing at the end of each loop iteration (including the early ? return) is unchanged.

The earlier comment covers write_shared_default_fields, the aliasing reshapes, walk_install_dir_windows, and the bunx probe.

alii and others added 5 commits August 10, 2026 23:14
… extractor edge cases

edit_catalogs_after_update carried a third copy of the alias-prefix and
pin-style logic that replacement_version_literal already covers for direct
dependencies; use the helper there too and cover aliased catalog entries.

Add fromUrl cases for the per-host differences the shared bitbucket, gist,
and sourcehut extractor is parameterized on: the rejected aux segment,
missing user or project, gist's optional user, and whether an undecodable
segment is reported as "not hosted" or as an invalid URL.
…ents

The helper has no callers outside bun_install. The pack and repl comments
described the helpers in terms of code that no longer exists.
@robobun
robobun changed the base branch from claude/split/foundations to main August 11, 2026 02:08
@alii

alii commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@robobun get this mergeable — or close it if the changes are already covered on main / too stale to salvage.

@robobun
robobun force-pushed the claude/split/install-cli branch from 2f5dd36 to 9ae3ff5 Compare August 11, 2026 02:09
@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Rebased onto current main and retargeted from the closed #32000 to main (head is now 9ae3ff5). The PR description lists the drift that had to be folded into the helpers (mark_network_task_failed in the tarball error dispatch, the should_ignore_lifecycle_scripts signature, the changed PackageManager field set, the Windows ctrl-handler fix, install_preference, and main deleting the hosted_git_info formatters outright) plus the parts of the original diff that main had already superseded. Two small additions on top: main's new edit_catalogs_after_update now also uses replacement_version_literal, and there are new tests for aliased catalog entries and for the per-host edge cases of the shared extractor; both new test sets also pass against a release build of main.

rust:check-all is clean on all 10 targets and the touched install/run/repl suites pass locally on a debug build (details in the description).

@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.

I reviewed this PR and the bug-hunting pass found no issues. Given the scope — 37 files across the package manager core (unsafe placement-init of the PackageManager singleton, raw-pointer aliasing reshapes in install_with_manager/patchPackage, cfg-gated Windows install walkers, fd-ownership changes in pack) — a human pass is still warranted before merge.

What was reviewed:

  • write_shared_default_fields: shared vs. per-path field sets cover the singleton with no gaps or double-writes.
  • replacement_version_literal: alias-prefix + pin-style ordering matches all three original call sites, including the exact-versions branch.
  • user_project_committish (hosted_git_info): per-host reject-aux / optional-user / error-as-none flags reproduce the original bitbucket/gist/sourcehut extractors; new tests pin the edges.
  • archive_pack_queue fd lifetime: File drop timing matches the old CloseOnDrop on both success and ? paths.
Extended reasoning...

Overview

Large-scale deduplication across src/install/ and src/runtime/cli/: introduces shared helpers (workspace_helpers.rs, run_processes_shared.rs, write_shared_default_fields, walk_install_dir_windows, replacement_version_literal, user_project_committish, try_run_cached_bin, load_package_for_link, parse_root_package, load_path_package, archive_pack_queue, analyze_dependencies_and_install, evaluate_to_value, etc.) and rewires ~two dozen call sites to them. Net −1.2k lines. Adds hermetic tests pinning the consolidated error exits and hosted-git-info extractor edges.

Security risks

The bunx try_run_cached_bin helper still gates execution on is_trusted_cached_binary(destination, uid) before Run::run_binary, and the only byte delta is the platform separator in the second probe path (documented, resolves identically via which_win). No new untrusted-input parsing surface. create_command template-dir probing preserves the has_any_illegal_chars guard on every candidate. No auth/crypto touched.

Level of scrutiny

High. The PR restructures memory-safety-sensitive code: (a) PackageManager init is per-field placement writes into an uninitialized singleton — a missed field is UB; (b) parse_root_package and load_path_package split &mut PackageManager / &mut Lockfile via raw pointers with disjointness argued in SAFETY comments; (c) walk_install_dir_windows and buffered_stdio are Windows-only paths not exercised on the review host; (d) archive_pack_queue changes fd ownership from CloseOnDrop to owning File. These are behavior-preserving on inspection and the robobun audit comments document the field-multiset diff and aliasing invariants, but they are exactly the class of change where a maintainer sign-off is appropriate.

Other factors

  • The PR is stacked on #32000 and has been rebased twice through large upstream refactors (#33909, #33032); a maintainer should confirm the base has landed and the branch is current.
  • Robobun's prior comment on this PR already agreed it should get a human look.
  • CI build 72329 has two failures documented as unrelated (worker-terminate SIGABRT, grpc-tonic toolchain), and alii's most recent comment asks to get it mergeable or close it — a human needs to make that call regardless.
  • No prior claude[bot] review on this PR.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Two facts in this pass are stale now:

  • The PR is no longer stacked. Remove dead code across foundation crates #32000 was closed and this branch was rebased directly onto main (merge base 827475e, 5 commits behind at the time of writing) and retargeted; GitHub reports it mergeable with no conflicts.
  • The current CI run is build 91835 on 9ae3ff5, not 72329.

The two newest commits (catalog updates routed through replacement_version_literal, extractor edge-case tests, and the pub(crate) narrowing) were re-verified locally on the rebased branch along with the hosted-git-info, catalogs, patch, yarn-migration, pack, link, bun-run, repl, and filter/multi-run suites. I will refresh the PR description once the cross-target check finishes.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Summary

The pull request consolidates package installation, dependency management, workspace handling, process execution, and CLI command logic into shared helpers. It also adds regression tests for lockfile, package metadata, alias, hosted Git, and link command behavior.

Changes

Installation and package management

Layer / File(s) Summary
Platform-specific installation traversal
src/install/PackageInstall.rs
Unix copy and symlink paths retain platform-specific handling. Windows copy and symlink installation use a shared directory walker with callback-based file handling.
Trusted dependencies and manifest parsing
src/install/PackageInstaller.rs, src/install/hosted_git_info.rs, src/install/isolated_install.rs, src/install/npm.rs, src/install/yarn.rs
Trusted lifecycle-script recording, hosted Git parsing, enqueue failure handling, bundled dependency parsing, and Yarn dependency construction use shared logic.
Package manager initialization and loading
src/install/PackageManager.rs, src/install/PackageManager/WorkspacePackageJSONCache.rs, src/install/PackageManager/install_with_manager.rs, src/install/PackageManager/patchPackage.rs, src/install/PackageManager/updatePackageJSONAndInstall.rs
Shared initialization, fatal manifest loading, root package parsing, patch package loading, and cache error handling replace duplicated flows.
Dependency updates and download errors
src/install/PackageManager/PackageJSONEditor.rs, src/install/PackageManager/PackageManagerEnqueue.rs, src/install/PackageManager/runTasks.rs
Version formatting, dependency callbacks, workspace resolution, update-request cleanup, and tarball error dispatch use centralized helpers.

CLI runtime

Layer / File(s) Summary
Shared process execution
src/runtime/cli/run_processes_shared.rs, src/runtime/cli/filter_run.rs, src/runtime/cli/multi_run.rs, src/runtime/cli/mod.rs
Process commands share abort handlers, process monitoring, exit-code aggregation, buffered standard I/O, and module declarations.
Shared CLI and workspace helpers
src/runtime/cli/run_command.rs, src/runtime/cli/repl_command.rs, src/runtime/cli/install_command.rs, src/runtime/cli/pm_update_package_json.rs, src/runtime/cli/bunx_command.rs, src/runtime/cli/create_command.rs, src/runtime/cli/link_command.rs, src/runtime/cli/unlink_command.rs, src/runtime/cli/outdated_command.rs, src/runtime/cli/update_interactive_command.rs, src/runtime/cli/workspace_helpers.rs
CLI commands reuse installation wiring, dependency analysis, cached binary probing, template discovery, link setup, lockfile loading, and workspace filtering.
Pack and REPL flows
src/runtime/cli/pack_command.rs, src/runtime/cli/repl.rs
Package JSON loading, archive queue processing, and transformed REPL evaluation are centralized.

Suggested reviewers: robobun, dylan-conway

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: deduplicating package manager and CLI command helpers.
Description check ✅ Passed The description explains the changes, behavioral differences, rebase context, tests, and verification results in sufficient detail.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/cli/repl.rs (1)

1355-1361: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The two interrupt paths disagree on the newline.

The execution_forbidden path prints "\n" unconditionally. The Pending path at Line 1382 prints "\n" only when mode == ReportMode::Print. Both paths represent an interrupted wait, so the newline behavior should match.

The ReportMode::Print doc at Line 587 describes only the pending case, which suggests the gate was applied to one path and not the other.

Confirm the intended behavior, then align the two paths. If the newline is always wanted after an interrupt, remove the gate at Line 1384. If it is print-only, add the gate here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/cli/repl.rs` around lines 1355 - 1361, Align newline handling
between the execution_forbidden branch and the Pending interrupt path in the
REPL wait logic: choose whether interrupted waits always print a newline or only
do so for ReportMode::Print, then apply that same condition to both paths.
Update the relevant print call near the execution_forbidden handling and the
Pending branch while preserving their existing interruption behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/install/PackageInstall.rs`:
- Around line 2417-2423: Update the fallback in the CreateDirectoryExW failure
branch to propagate the Result from bun_sys::MakePath::make_path_u16 instead of
discarding it. Ensure PackageInstall’s installation flow reports the filesystem
error and cannot succeed when both directory-creation attempts fail.
- Around line 1390-1399: Update the Unix metadata handling in the install flow
around fstat and fchmod so both failures are propagated as install errors rather
than skipped or ignored. Replace the fstat continue path and the discarded
fchmod result while preserving successful metadata application before the
install completes.
- Around line 2397-2411: Update the capacity checks around the destination and
source path construction to reserve one byte for each NUL terminator, rejecting
lengths equal to the remaining buffer capacity. Validate the
offset-plus-path-length arithmetic before slicing or indexing, and return
ENAMETOOLONG for overflow or insufficient capacity so head1/head2 writes in the
surrounding install flow cannot panic.

In `@src/install/PackageManager/PackageManagerEnqueue.rs`:
- Around line 1240-1243: Replace the four `.expect("unreachable")` calls on
`task_callback_list` and `push_dependency_task_callback` within
`enqueue_dependency_with_main_and_success_fn` with `?` propagation. Preserve the
existing callback logic and rely on the function’s `crate::Result<()>` return
type so allocation failures reach the designated error handler instead of
panicking.

In `@src/install/PackageManager/updatePackageJSONAndInstall.rs`:
- Around line 740-755: Replace the direct
WorkspacePackageJSONCache::get_with_path match in the root_package_json_entry
initialization with the existing get_with_path_or_exit helper, passing the same
logger, path bytes, and GetJSONOptions. Preserve the returned entry assignment
and let the shared helper print pending diagnostics and terminate consistently
on read or parse failure.

In `@src/install/yarn.rs`:
- Around line 1726-1734: Update the dep_groups construction in process_deps to
preserve the matched entry’s workspace state when creating each
dependency::Behavior, rather than using only PROD, OPTIONAL, PEER, or DEV.
Ensure entries with workspace: or * versions retain the WORKSPACE flag while
preserving the existing dependency-category flags.

In `@src/runtime/cli/bunx_command.rs`:
- Around line 693-708: Update the write! call constructing absolute_in_cache_dir
so buffer exhaustion is mapped to crate::Error::PathTooLong and propagated from
the surrounding function instead of using expect("unreachable"). Match the
existing error-handling pattern at the other path construction site, while
preserving the successful path construction behavior.

In `@src/runtime/cli/create_command.rs`:
- Around line 1367-1371: Update the HOME lookup in the environment search around
env_loader.map.get to use bun_core::env_var::HOME.key() instead of the hardcoded
b"HOME", preserving the existing mapping behavior and enabling the
platform-specific key.

In `@src/runtime/cli/pack_command.rs`:
- Around line 3090-3092: Update the documentation comment near the loop in pack
to include the `?` propagation from `add_archive_entry` as an additional
early-exit path, while preserving the existing explanation that
`node.complete_one()` is explicitly called on normal loop-body exits and that
`Global::crash()` does not return. Do not change the implementation.
- Around line 1899-1902: Add a SAFETY-style documentation note to
load_package_json_or_exit stating that callers must not use the returned
MapEntry reference after mutating manager_ptr’s workspace_package_json_cache,
including removing or replacing entries. Keep the function signature and
behavior unchanged.
- Around line 3161-3183: Update the failed-stat error handling in the stat
operation to always report item.path, including the PlainFile branch. Remove the
uv_owned_fd-specific Output::err formatting branch and use one shared error
message with item.path for both stat implementations.

In `@src/runtime/cli/pm_update_package_json.rs`:
- Around line 138-141: Validate that the entry-point slice derived from
cli.positionals contains at least one item before constructing entry_points or
DependenciesScanner. Update the existing empty-entry-point check to reject
cli.positionals equal to [b"install"] and return the missing-script error first,
while preserving normal scanning for non-empty entry points.

In `@src/runtime/cli/run_processes_shared.rs`:
- Around line 34-72: Align the platform gating for the abort handler across
install(), uninstall(), and windows_ctrl_handler, using the same
Windows-specific cfg predicate so unsupported non-Unix, non-Windows targets do
not reference unavailable symbols. Also update the buffered_stdio cfg site if
needed to preserve the existing Windows-only libuv type assumptions.

In `@src/runtime/cli/workspace_helpers.rs`:
- Around line 18-26: Update load_lockfile_or_crash to accept a command-specific
wording parameter, following the verb pattern used by load_package_for_link, and
use it when constructing the missing-lockfile message instead of hardcoding
“nothing outdated.” Update each caller, including bun outdated and bun update
--interactive, to pass the appropriate wording.

In `@test/cli/install/hosted-git-info/from-url.test.ts`:
- Around line 24-38: Add negative-control cases to the hosted-git URL
parameterized test proving reject_aux is host-specific: include valid foreign
aux segments such as Bitbucket’s raw URL and SourceHut’s get URL, and assert
they are parsed rather than rejected. Keep the existing host-specific rejection
cases unchanged.

---

Outside diff comments:
In `@src/runtime/cli/repl.rs`:
- Around line 1355-1361: Align newline handling between the execution_forbidden
branch and the Pending interrupt path in the REPL wait logic: choose whether
interrupted waits always print a newline or only do so for ReportMode::Print,
then apply that same condition to both paths. Update the relevant print call
near the execution_forbidden handling and the Pending branch while preserving
their existing interruption behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d5917919-8378-4040-b6dd-13998de8f85a

📥 Commits

Reviewing files that changed from the base of the PR and between 9fcdea8 and 9ae3ff5.

📒 Files selected for processing (37)
  • src/install/PackageInstall.rs
  • src/install/PackageInstaller.rs
  • src/install/PackageManager.rs
  • src/install/PackageManager/PackageJSONEditor.rs
  • src/install/PackageManager/PackageManagerEnqueue.rs
  • src/install/PackageManager/WorkspacePackageJSONCache.rs
  • src/install/PackageManager/install_with_manager.rs
  • src/install/PackageManager/patchPackage.rs
  • src/install/PackageManager/runTasks.rs
  • src/install/PackageManager/updatePackageJSONAndInstall.rs
  • src/install/hosted_git_info.rs
  • src/install/isolated_install.rs
  • src/install/npm.rs
  • src/install/yarn.rs
  • src/runtime/cli/bunx_command.rs
  • src/runtime/cli/create_command.rs
  • src/runtime/cli/filter_run.rs
  • src/runtime/cli/install_command.rs
  • src/runtime/cli/link_command.rs
  • src/runtime/cli/mod.rs
  • src/runtime/cli/multi_run.rs
  • src/runtime/cli/outdated_command.rs
  • src/runtime/cli/pack_command.rs
  • src/runtime/cli/pm_update_package_json.rs
  • src/runtime/cli/repl.rs
  • src/runtime/cli/repl_command.rs
  • src/runtime/cli/run_command.rs
  • src/runtime/cli/run_processes_shared.rs
  • src/runtime/cli/unlink_command.rs
  • src/runtime/cli/update_interactive_command.rs
  • src/runtime/cli/workspace_helpers.rs
  • test/cli/install/bun-install-registry.test.ts
  • test/cli/install/bun-link.test.ts
  • test/cli/install/bun-pack.test.ts
  • test/cli/install/catalogs.test.ts
  • test/cli/install/hosted-git-info/from-url.test.ts
  • test/cli/update_interactive_install.test.ts

Comment thread src/install/PackageInstall.rs
Comment thread src/install/PackageInstall.rs
Comment thread src/install/PackageInstall.rs
Comment thread src/install/PackageManager/PackageManagerEnqueue.rs
Comment thread src/install/PackageManager/updatePackageJSONAndInstall.rs
Comment thread src/runtime/cli/pack_command.rs
Comment thread src/runtime/cli/pm_update_package_json.rs
Comment thread src/runtime/cli/run_processes_shared.rs
Comment thread src/runtime/cli/workspace_helpers.rs
Comment thread test/cli/install/hosted-git-info/from-url.test.ts
@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

@alii summary of where this stands:

So it just needs the remaining lanes to finish and a human pass on the equivalence notes; nothing further is pending from my side.

…age_json_or_exit's lifetime; pin per-host aux segments in fromUrl tests

@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.

I reviewed this PR and the automated bug hunt found no issues. Given the scope — ~5.5k lines across 37 files in the package manager and CLI command paths, with hand-verified field-by-field equivalence for the two PackageManager init paths and several parameterized extractions — a human pass on the equivalence notes (as robobun's summary also flags) is still warranted before merge.

Checked: the write_shared_default_fields split covers the same field set as both original init blocks; replacement_version_literal preserves the alias-prefix fallthrough at all three call sites; the hosted_git_info user_project_committish parameterization matches each host's original aux-reject / user-optional / error-as-none behavior (pinned by the new tests); the TrustedDepRecord split preserves the first call site's package_json == lockfile coupling and the second's independent add_to_lockfile.

Extended reasoning...

Overview

This PR consolidates ~1k net lines of duplicated code across the package manager (src/install/) and CLI commands (src/runtime/cli/). It introduces shared helpers for: workspace enumeration and lockfile loading, multi-process run plumbing (signal handling, exit-code aggregation, stdio buffering), the two PackageManager placement-init paths, workspace package.json load-or-exit, trusted-dependency lifecycle enqueue, tarball error dispatch, the yarn dependency-group loop, npm bundleDependencies extraction, hosted_git_info URL extractors, the bunx cached-bin probe, the pack archive queue, the patch path-package loader, the repl evaluate pipeline, and the version-literal replacement in PackageJSONEditor. It also adds tests pinning the shared error exits and the per-host extractor differences.

Security risks

No new attack surface. The bunx try_run_cached_bin helper preserves the existing is_trusted_cached_binary TOCTOU check. The unsafe blocks are refactored placements of pre-existing raw-pointer field projections and placement writes with the same SAFETY invariants restated; no new unsafe patterns are introduced. The PR description documents three intentional edge-path deltas (bunx separator, discarded infallible log.print result, handle_oom vs .expect), all of which are benign or improvements.

Level of scrutiny

High. This touches the core install/add/update/link/outdated/pack/bunx dispatch paths, the PackageManager singleton's per-field placement initialization (where a missed field is UB), Windows-gated install walkers, and several borrow-checker workarounds routed through raw pointers. The claim is byte-identical behavior, which requires line-by-line comparison against main — the kind of verification a human maintainer familiar with the original duplicates is best positioned to spot-check. Robobun's own status update explicitly calls for a human pass on the equivalence notes.

Other factors

CI build 91835 was green on 190 lanes with 6 still queued at last report. The touched test suites pass on debug. Several unresolved CodeRabbit comments remain (the shared "nothing outdated" message wording, the bunx PathTooLong vs .expect, a get_with_path_or_exit reuse suggestion) — these are minor and pre-existing in spirit, but the author may want to address them before merge. Given the size, the criticality of the paths, and the explicit ask for a human equivalence review, I'm not auto-approving.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

The "other factors" here are a step behind: the three CodeRabbit items named (the shared missing-lockfile wording, the bunx expect, the get_with_path_or_exit reuse) were each answered as pre-existing behavior this PR keeps on purpose and have been resolved, as have the other twelve; the bunx one is filed separately. There are no open threads. The current CI run is build 91858 on ca15187, at 189 passed and 0 failed with 7 lanes left.

@alii

alii commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@robobun retry ci — darwin lanes expired during the outage, runners are back now.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Retriggered: build 92351 is running on b5b9aaa (an empty commit; the Buildkite token here is read-only, so the expired jobs could not be retried in place). https://buildkite.com/bun/bun/builds/92351

@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.

I reviewed this PR and the bug-hunting pass found no issues. Given the scope — a ~1k-line net refactor across the install/link/pack/bunx/run command paths, including the unsafe per-field placement init of the PackageManager singleton and several raw-pointer aliasing reshapes — a human pass on the equivalence claims is still warranted.

What was reviewed:

  • write_shared_default_fields vs. both init paths — the 58 shared + 12 per-path fields cover the struct with no double-write.
  • replacement_version_literal's alias handling — the edit_update_no_args site's distinct dep_literal (workspace_dep vs. original_version_literal) is preserved via the separate parameter.
  • The hosted_git_info shared extractor's per-host flags (reject_aux, user_optional, error_as_none) against each deleted per-host body, matched by the new from-url tests.
  • The bunx try_run_cached_bin helper keeps the is_trusted_cached_binary TOCTOU gate on both probes; only the second probe's path separator changes (documented).
Extended reasoning...

Overview

This PR consolidates ~1k net lines of duplicated logic across 31 Rust files in src/install/ and src/runtime/cli/, plus 6 test files. It extracts shared helpers for: workspace enumeration and lockfile loading, multi-process run plumbing (AbortHandler, exit-code aggregation, buffered_stdio), the two PackageManager init paths' 58 shared field writes, workspace package.json load-or-exit, trusted-dep lifecycle enqueue, tarball-error dispatch, the hosted-git-info bitbucket/gist/sourcehut extractors, the yarn.lock dependency-group loop, npm bundled-deps extraction, the pack archive queue drain, the bunx post-install cached-bin probes, and the --analyze bundler-then-install flow. The PR body documents three intentional edge-path deltas and asserts byte-identical error text/exit semantics everywhere else.

Security risks

The bunx change touches the security-sensitive is_trusted_cached_binary TOCTOU check that guards execution of binaries from a world-writable temp cache. The extracted try_run_cached_bin helper keeps that gate intact on both probes. The PackageManager singleton init uses unsafe per-field placement writes; a missed field would be an uninitialized-memory read. The parse_root_package / load_path_package reshapes involve raw-pointer disjoint-field projections with SAFETY comments. None of these introduce new attack surface, but each depends on the equivalence audit being correct.

Level of scrutiny

High. This is production-critical package-manager and CLI-dispatch code exercised on every bun install/add/update/link/pack/bunx invocation, with substantial unsafe Rust. The claim is zero behavior change across ~3.3k deleted lines rebased over ~780 commits of drift; verifying that the 58-field shared-default list exactly matches both init paths, and that each parameterized helper preserves the per-site variations it replaces, requires the kind of hunk-by-hunk comparison a maintainer should sign off on rather than an automated approval.

Other factors

All 15 CodeRabbit threads are resolved (mostly as pre-existing behavior intentionally kept). robobun's audit notes on the thread already flag the specific areas for a human pass. Test coverage was added for the shared error exits and the hosted-git-info per-host parameters, and the description states the new tests pass on a release build of main (i.e., they pin pre-existing behavior). CI on the current head was retriggered after a darwin runner outage; the prior run was 192/192 with only expired lanes.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Retry result (build 92351): same shape as before. 192 lanes passed, nothing failed, and the darwin 26 lanes picked up and passed this time, but the four darwin 14 (aarch64 and x64) jobs expired again without an agent, so that pool specifically still looks down. Everything that has run on this diff has now passed twice; whether to wait on darwin 14 is your call.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Correction to the above: the darwin 14 agents did pick up in the end, just slowly. Build 92351 is final at 195 passed with a single failure, grpc-js/test-tonic.test.ts on one darwin 14 aarch64 shard, which fails because that agent has no default rustup toolchain (same failure and same agent as build 72329 in July, so it is an agent provisioning problem, now reported for triage separately). Nothing this PR touches failed on either run.

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.

2 participants