Conversation
bun run <script> was not setting INIT_CWD in the script's environment, while npm sets it to the directory the command was invoked from and bun's own install lifecycle scripts already do the same. This broke monorepo idioms like 'cd $INIT_CWD' and tools that locate the caller's directory. Set INIT_CWD in configure_env_for_run_impl alongside the other npm_* env vars, using the same put_default semantics as the install path (PackageManager.rs) so an explicitly-set INIT_CWD is preserved.
WalkthroughThe CLI runner now sets ChangesINIT_CWD runtime behavior
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Found 1 issue this PR may fix:
🤖 Generated with Claude Code |
- put_default -> put so nested bun run resets INIT_CWD to its own invocation cwd (matches npm and the npm_lifecycle_event precedent) - use without_trailing_slash_windows_path so C:\ stays C:\ instead of becoming drive-relative C: - add nested-run test and make both tests assert the overwrite
There was a problem hiding this comment.
LGTM — all three earlier nits are addressed (unconditional .put(), Windows-safe without_trailing_slash_windows_path, and the bun pm pack claim dropped from the description).
What was reviewed:
.put()matches thenpm_lifecycle_eventprecedent at run_command.rs:275/2482; new nested-run test proves the overwrite and seeds a sentinelINIT_CWDto catch leak-through.without_trailing_slash_windows_pathdelegates to plainwithout_trailing_slashon unix and preservesC:\\on Windows — same helperprocess.cwd()uses on this field.- Tests drain stdout/stderr/exited concurrently, use
realpathSyncto avoid macOS/privatesymlink mismatches, and assert exit code last.
Extended reasoning...
Overview
Single 6-line addition to configure_env_for_run_impl in src/runtime/cli/run_command.rs that writes INIT_CWD (the invocation cwd) into the script env, plus two new tests in test/cli/run/run-process-env.test.ts. This is the second review pass — commit 6f9897d9 addressed all three nits from the prior round: put_default → unconditional .put(), strings::without_trailing_slash → the Windows-drive-root-safe without_trailing_slash_windows_path, and the PR description no longer claims bun pm pack coverage.
Security risks
None. This exports a directory path the invoking process already knows (its own cwd) into a child process it is about to spawn. No untrusted-input parsing, no privilege boundary crossed.
Level of scrutiny
Low-to-moderate. It's a small Node/npm-compat env-var addition that mirrors an existing pattern in the same function (npm_lifecycle_event uses .put() a few lines away) and an existing behavior in the install path. The helper used for trailing-slash trimming is the established in-tree one that process.cwd() already applies to this exact top_level_dir field.
Other factors
- The nested-run test is modeled directly on the existing #3589
npm_lifecycle_eventnested test two entries above it, and the sentinelINIT_CWD: "/should/be/overwritten"in the spawn env is a stronger assertion than the previousundefinedscrub — it would fail if the write were stillput_default. fs.realpathSyncon both the invoke dir and the package root guards against macOS tmpdir symlink normalization flaking the equality check.bun_pathsis already imported in this file; the fully-qualified helper path resolves without new imports.- The remaining known limitation (
bun pm pack/ install lifecycle scripts see the package root, not the true invocation cwd) is pre-existing, now explicitly called out in the description as out of scope, and unchanged by this PR.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@test/cli/run/run-process-env.test.ts`:
- Around line 30-85: Extend the INIT_CWD tests alongside the existing bun run
cases to cover both bunx and bun run --filter entry points. Add one assertion
per path verifying INIT_CWD equals the directory from which the command was
invoked, including the expected environment override behavior, while preserving
the existing run and nested-run coverage.
🪄 Autofix (Beta)
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: 7b0c9e58-24dc-4278-bed7-ece8b0661beb
📒 Files selected for processing (2)
src/runtime/cli/run_command.rstest/cli/run/run-process-env.test.ts
There was a problem hiding this comment.
LGTM — all three earlier review points are addressed in 6f9897d.
What was reviewed:
.put()(unconditional) so nestedbun runresetsINIT_CWD; nested-run test locks it in.without_trailing_slash_windows_pathontop_level_dir— Windows drive-rootC:\preserved; delegates to plain trim on unix.top_level_dirat run_command.rs:613 is the invocation cwd on thebun run/bunx/--filterpaths;bun pm packlimitation now correctly noted in the description.- Tests: pipes drained concurrently,
fs.realpathSyncfor symlinked tmpdirs, seedINIT_CWD: "/should/be/overwritten"proves overwrite semantics.
Extended reasoning...
Overview
Adds a single env_loader.map.put(b"INIT_CWD", without_trailing_slash_windows_path(top_level_dir)) call in configure_env_for_run_impl (src/runtime/cli/run_command.rs:679-685), plus two tests in test/cli/run/run-process-env.test.ts. This exports INIT_CWD to package.json scripts under bun run, matching npm's process.env.INIT_CWD = process.cwd() at CLI entry.
Prior review resolved
I left three inline comments on the previous revision; all are addressed in commit 6f9897d and reflected in the current diff:
put_default→.put()so nestedbun runresets to its own invocation cwd (matching thenpm_lifecycle_eventprecedent at lines 275/278). A dedicated nested-run test now asserts this.strings::without_trailing_slash→bun_paths::string_paths::without_trailing_slash_windows_path, which preservesC:\on Windows and delegates to the plain helper on unix (verified at src/paths/string_paths.rs:461-469).- PR description no longer claims
bun pm packcoverage; thePackageManager::initchdir limitation is called out as pre-existing and out of scope.
Security risks
None. This only writes an additional env var (the process's own cwd) into the script environment; no untrusted input parsing, no new I/O, no privilege boundaries crossed.
Level of scrutiny
Low-to-moderate. 8 lines of Rust in a well-understood env-seeding function, following the exact pattern of adjacent npm_config_local_prefix / npm_lifecycle_event writes. The value source (top_level_dir = (*this_transpiler.fs).top_level_dir, set from getcwd() via FileSystem::init) is the same field process.cwd() reads. Type signature matches (&[u8] in, &[u8] out).
Other factors
Tests follow harness conventions: tempDirWithFiles, bunEnv spread, await using for the subprocess, Promise.all over stdout/stderr/exited, stderr asserted empty before stdout, exit code last. fs.realpathSync handles macOS /private/var symlinks. The INIT_CWD: "/should/be/overwritten" seed makes the overwrite semantics load-bearing (test would fail on canary with the old put_default). Evidence block shows both tests fail on canary and pass with the fix under both ASAN debug and release.
|
CI status: the diff is green. The gate ( The only hard failure across both builds is Ready for review/merge. |
|
Closing as part of a cleanup of stale pull requests. This PR has had no new commits since 2026-07-27, it conflicts with main, and its last CI run failed. This is not a judgment on the fix itself. If the problem still reproduces on a current build, reopen this PR after a rebase or open a new one against main. |
What
bun run <script>now exportsINIT_CWD(the directory the command was invoked from) to the script's environment, matching npm and matching whatbun installlifecycle scripts already do.Part of #21088 (the
INIT_CWDportion;npm_commandandnpm_config_local_prefixleft for follow-up).Repro
Before:
IC=[]After:
IC=[/.../p/sub/deep](same asnpm run --silent envcheck)Cause
configure_env_for_run_implseedsnpm_lifecycle_event,npm_config_local_prefix,npm_package_*, etc., but neverINIT_CWD. The install path (PackageManager::configure_env_for_scripts_run) sets it separately after the fact, which is whybun installlifecycle scripts already had it butbun rundid not.Fix
Set
INIT_CWDinconfigure_env_for_run_implunconditionally (.put()), sourced fromtop_level_dirwhich at that point is the invocation cwd. Unconditional write matches npm (which doesprocess.env.INIT_CWD = process.cwd()at CLI entry) and thenpm_lifecycle_eventprecedent in the same function, so a nestedbun runresetsINIT_CWDto its own invocation directory rather than inheriting the outer value. The trailing-slash trim useswithout_trailing_slash_windows_pathso a Windows drive-root cwdC:\is preserved instead of becoming drive-relativeC:.This covers
bun run,bunx, and--filtersince they share this env setup. (bun pm packand install lifecycle hooks go throughPackageManager::initwhichchdirs to the package root before this runs, so they continue to see the package root asINIT_CWD; that is a pre-existing limitation tracked separately.)Verification
Both fail on canary, pass with the fix.
Not in this PR
The other two discrepancies from #21088, left for follow-up:
npm_commandisrun-script(npm usesrun)npm_config_local_prefixis the invocation cwd rather than the package root[review] gate passed · iteration 1 · 2 files touched
fails on main (without fix)
passes on PR (with fix)
diff hotspot
gate history · 3 passed · 0 rejected · iteration 1
evidence per changed file