Skip to content

fix(npm-globals): purge bun npm shim that breaks @railway/cli install - #2022

Merged
shunkakinoki merged 1 commit into
mainfrom
fix/railway-cli-install
Jul 7, 2026
Merged

fix(npm-globals): purge bun npm shim that breaks @railway/cli install#2022
shunkakinoki merged 1 commit into
mainfrom
fix/railway-cli-install

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • The npm bun wrapper package gets pulled in as a transitive dependency during global installs
  • Its postinstall is skipped by bun, leaving a broken stub at .bin/bun that shadows the real system bun
  • This causes every subsequent postinstall that shells out to bun to fail (e.g. @railway/cli, and likely the other 12 failing packages)
  • Added purge_bun_npm_shim() that removes the broken bun npm package and its .bin shims after every bun add/bun install operation

Test plan

  • All 54 shellspec tests pass (4 new tests for the purge function)
  • Run install-npm-globals.sh and verify @railway/cli installs successfully

Summary by cubic

Fixes global installs failing due to a broken npm bun shim that shadowed the system bun, blocking @railway/cli installs. We now remove the npm bun wrapper and its .bin shims after each global install step.

  • Bug Fixes
    • Added purge_bun_npm_shim() to delete the npm bun wrapper and .bin/bun/bunx shims from the global install.
    • Call purge after the initial global install, after each bun add --global, and after applying global overrides.
    • Added shellspec tests to verify the purge function, its removal targets, and that it’s invoked post-install.

Written for commit 86389fb. Summary will update on new commits.

Review in cubic

The npm 'bun' wrapper package gets pulled in as a transitive dependency.
When its postinstall is skipped, it leaves a broken stub at
.bin/bun that shadows the real system bun, causing every subsequent
postinstall that shells out to bun to fail (e.g. @railway/cli).

Add purge_bun_npm_shim() that removes the bun npm package and its
.bin shims after every bun add/install operation.
@indent-zero

indent-zero Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Fixes a bootstrap regression where a transitive npm bun wrapper package left a broken .bin/bun in the global node_modules, shadowing the real system bun and cascading postinstall failures (notably @railway/cli). Adds a purge_bun_npm_shim helper that deletes the wrapper package plus its .bin/{bun,bunx} shims and calls it after every mutating bun operation, restoring the Nix-provided bun on PATH.

  • Add purge_bun_npm_shim() in install-npm-globals.sh that removes ~/.bun/install/global/node_modules/bun and its .bin/bun/.bin/bunx shims when present.
  • Invoke the purge after stale removal, after each per-package bun add --global, after the overrides bun install, and after each optional platform-native bun add --global.
  • Add four grep-based spec cases in spec/npm_globals_spec.sh verifying the function's presence, its removal targets, and the post-install call site.

Issues

3 potential issues found:

  • New spec cases only grep the source for literals — they'd pass even if the purge were refactored into a no-op or reordered incorrectly; consider an integration test that seeds a fake bun package under a mocked $HOME and asserts it's gone after the script runs (mirroring the mocked-bun blocks at lines 251+ and 318+). → Autofix
  • Purge misses the ~/.bun/bin/bun symlink for the overrides and optional-native phases; the dangling-shim sweep only runs once at line 224, so re-materializations from bun install (line 241) or the optional loop (line 339) leave a dangling shim on PATH until the next run. → Autofix
  • First purge_bun_npm_shim call happens at line 171, after bun pm -g trust (line 140) and bun remove --global (line 162); if a prior run left the wrapper on disk those steps silently no-op via their || true / || echo guards until the purge lands. Moving the initial purge above line 140 would let post-corruption recovery complete in a single run. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@shunkakinoki, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8771e6d-6560-4766-a3d8-d65d7d7a94db

📥 Commits

Reviewing files that changed from the base of the PR and between ab9ec22 and 86389fb.

📒 Files selected for processing (2)
  • home-manager/modules/npm-globals/install-npm-globals.sh
  • spec/npm_globals_spec.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/railway-cli-install

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@shunkakinoki
shunkakinoki merged commit 6756d0c into main Jul 7, 2026
23 of 29 checks passed
@shunkakinoki
shunkakinoki deleted the fix/railway-cli-install branch July 7, 2026 16:42

@gemini-code-assist gemini-code-assist 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

This pull request introduces a purge_bun_npm_shim function in install-npm-globals.sh to remove the npm "bun" wrapper package and its shims from global node_modules, preventing them from shadowing the system bun. It also adds corresponding tests in npm_globals_spec.sh. Feedback was provided to make the purge check more robust by also checking for dangling symlinks (-L or -e) in case the main directory is missing but the shims remain.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

# subsequent postinstall that shells out to bun (e.g. @railway/cli).
purge_bun_npm_shim() {
local gm="${HOME}/.bun/install/global/node_modules"
if [ -d "${gm}/bun" ]; then

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.

high

If a previous run was interrupted, or if the ${gm}/bun directory was deleted/cleaned up but the shims in ${gm}/.bin were left behind, they will become dangling symlinks.

In Unix-like systems, a dangling symlink in a directory that is in the PATH (like node_modules/.bin during postinstall scripts) will still shadow the command and cause execution to fail with a No such file or directory error, rather than falling back to the real system bun.

Since the current check only looks for the directory [ -d "${gm}/bun" ], it will skip purging the shims if the directory is gone but the dangling symlinks remain.

To make this robust against dangling symlinks, we should also check for the existence of the symlinks/files themselves using [ -L ... ] and [ -e ... ].

Suggested change
if [ -d "${gm}/bun" ]; then
if [ -d "${gm}/bun" ] || [ -L "${gm}/.bin/bun" ] || [ -L "${gm}/.bin/bunx" ] || [ -e "${gm}/.bin/bun" ] || [ -e "${gm}/.bin/bunx" ]; then

rm -f "${gm}/.bin/bun" "${gm}/.bin/bunx"
echo "Removed broken bun npm shim from global node_modules"
fi
}

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.

Purge doesn't cover ~/.bun/bin for later phases: home-manager/modules/npm-globals/default.nix puts $HOME/.bun/bin on home.sessionPath, and bun add -g on the wrapper package creates ~/.bun/bin/bun as a symlink into install/global/node_modules/bun/.... This function only deletes the target under install/global/node_modules, so ~/.bun/bin/bun becomes a dangling symlink. The dangling-shim sweep at lines 224–232 catches it, but it runs once — before the purges at lines 242 and 340. If either the overrides bun install or the optional-native bun add --global re-materializes the wrapper, ~/.bun/bin/bun is left dangling until the next script run.

Today this is not immediately harmful because bash's test -x returns false on dangling symlinks and PATH resolution falls through to the Nix bun. But it relies on bash-specific PATH semantics; interactive shells with a cached hash bun, or non-bash callers, may still resolve to the dead symlink and reproduce the original shadow. Consider extending the purge to also rm -f "${HOME}/.bun/bin/bun" "${HOME}/.bun/bin/bunx" (or moving the dangling sweep to run after every purge).

Comment thread spec/npm_globals_spec.sh
When run bash -c "grep -A 1 'bun add --global.*dep.*2>/dev/null' '$SCRIPT' | grep 'purge_bun_npm_shim'"
The output should include 'purge_bun_npm_shim'
End
End

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.

Behavior not exercised by tests: These four cases only grep the source file for literal strings (purge_bun_npm_shim(), rm -rf.*gm.*bun, rm -f.*\.bin/bun, bun add --global.*dep.*purge_bun_npm_shim). They pass whenever those tokens appear in the file, regardless of whether the purge actually fires or removes the wrapper.

The file already has good precedent for real integration tests — see native-binary reinstall integration (line 251) and stale global package pruning (line 318). A similar block that seeds $TEMP_HOME/.bun/install/global/node_modules/bun/package.json and asserts it's gone after running the script would actually guard the fix from regressing.

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="spec/npm_globals_spec.sh">

<violation number="1" location="spec/npm_globals_spec.sh:191">
P3: These tests only verify that certain string patterns exist in the source file (via `grep`). They'll pass as long as the tokens are present, regardless of whether the purge logic actually removes the wrapper correctly. The spec file already has good integration-style precedents (e.g. `native-binary reinstall integration` at line 251) — a test that seeds a fake `bun/package.json` under a temp global modules dir and asserts it's gone after sourcing/running the function would provide real regression protection.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread spec/npm_globals_spec.sh
End

Describe 'bun npm shim purge'
It 'defines a purge_bun_npm_shim function'

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.

P3: These tests only verify that certain string patterns exist in the source file (via grep). They'll pass as long as the tokens are present, regardless of whether the purge logic actually removes the wrapper correctly. The spec file already has good integration-style precedents (e.g. native-binary reinstall integration at line 251) — a test that seeds a fake bun/package.json under a temp global modules dir and asserts it's gone after sourcing/running the function would provide real regression protection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/npm_globals_spec.sh, line 191:

<comment>These tests only verify that certain string patterns exist in the source file (via `grep`). They'll pass as long as the tokens are present, regardless of whether the purge logic actually removes the wrapper correctly. The spec file already has good integration-style precedents (e.g. `native-binary reinstall integration` at line 251) — a test that seeds a fake `bun/package.json` under a temp global modules dir and asserts it's gone after sourcing/running the function would provide real regression protection.</comment>

<file context>
@@ -187,6 +187,28 @@ The output should include 'find'
 End
 
+Describe 'bun npm shim purge'
+  It 'defines a purge_bun_npm_shim function'
+    When run bash -c "grep 'purge_bun_npm_shim()' '$SCRIPT'"
+    The output should include 'purge_bun_npm_shim'
</file context>

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.

1 participant