feat(shell): prioritize bun global bin in bash/fish/zsh PATH - #1887
Conversation
|
📝 WalkthroughWalkthroughAdds Bun global npm binaries directory to PATH initialization for Bash, Fish, and Zsh, updates tests for presence and macOS ordering, converts SSH IdentityFile values to single-element lists, and relocates/adjusts the llama-cpp package override. ChangesBun Global Node Binaries in Shell PATH
SSH IdentityFile List Format
Package override relocation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Mesa DescriptionTL;DRPrioritizes globally installed Bun binaries in shell What changed?
Test plan
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
Code Review
This pull request updates the shell configuration files for Bash, Fish, and Zsh to add the global Bun node modules binary directory ($HOME/.bun/install/global/node_modules/.bin) to the PATH environment variable, ensuring globally installed Bun packages are accessible. I have no feedback to provide as there are no review comments.
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.
There was a problem hiding this comment.
Performed full review of 4818a0c...fa84fc8
Analysis
• Inconsistent idempotency protection across shell initialization: bash_env.sh uses proper case-based duplicate checking, but bashrcExtra, profileExtra, and zsh initContent lack this protection, risking PATH pollution with repeated entries on multiple shell invocations.
• PATH duplication across three separate bash initialization surfaces (bashrcExtra, profileExtra, bash_env.sh) without centralized policy creates maintenance burden and risk of divergence in command resolution order.
• Missing idempotent insertion patterns in zsh and inline bash exports could cause unintended command shadowing behavior if users source configurations multiple times, though Fish's built-in duplicate handling mitigates this for one shell.
• No documented specification of the intended command resolution precedence hierarchy across all shells, making it difficult to verify correctness and maintain consistency as the configuration evolves.
Tip
Help
Slash Commands:
/review- Request a full code review/review latest- Review only changes since the last review/describe- Generate PR description. This will update the PR body or issue comment depending on your configuration/help- Get help with Mesa commands and configuration options
0 files reviewed | 2 comments | Edit Agent Settings • Read Docs
| export PATH="$HOME/.bun/bin:$PATH" | ||
| export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH" | ||
| export PATH="/opt/homebrew/bin:$PATH" | ||
| export PATH="$HOME/.bun/install/global/node_modules/.bin:$PATH" |
There was a problem hiding this comment.
The bun global path is added here without idempotent duplicate checking, unlike the robust pattern in bash_env.sh (lines 22-25). In environments where both bashrcExtra and bash_env.sh are sourced, this could lead to duplicate PATH entries. Consider applying the same case-based guard pattern used in bash_env.sh, or ensure these initialization contexts are mutually exclusive.
Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#1887
File: home-manager/programs/bash/default.nix#L87
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.
Feedback:
The bun global path is added here without idempotent duplicate checking, unlike the robust pattern in `bash_env.sh` (lines 22-25). In environments where both `bashrcExtra` and `bash_env.sh` are sourced, this could lead to duplicate PATH entries. Consider applying the same case-based guard pattern used in `bash_env.sh`, or ensure these initialization contexts are mutually exclusive.
| export PATH="$HOME/.bun/bin:$PATH" | ||
| export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH" | ||
| export PATH="/opt/homebrew/bin:$PATH" | ||
| export PATH="$HOME/.bun/install/global/node_modules/.bin:$PATH" |
There was a problem hiding this comment.
This PATH prepend in initContent lacks duplicate checking. Similar to line 35 in envExtra, if both initialization paths execute (depending on shell invocation mode), the bun global path could be added multiple times. Consider wrapping with a case statement like case ":$PATH:" in *":$HOME/.bun/install/global/node_modules/.bin:"*) ;; *) export PATH="...:$PATH" ;; esac to ensure idempotent insertion.
Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#1887
File: home-manager/programs/zsh/default.nix#L77
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.
Feedback:
This PATH prepend in `initContent` lacks duplicate checking. Similar to line 35 in `envExtra`, if both initialization paths execute (depending on shell invocation mode), the bun global path could be added multiple times. Consider wrapping with a case statement like `case ":$PATH:" in *":$HOME/.bun/install/global/node_modules/.bin:"*) ;; *) export PATH="...:$PATH" ;; esac` to ensure idempotent insertion.
There was a problem hiding this comment.
Pull request overview
This PR updates the Home Manager shell configurations to ensure Bun global package executables resolve from Bun’s global node_modules/.bin directory with the highest PATH precedence across bash, zsh, and fish.
Changes:
- Prepend
$HOME/.bun/install/global/node_modules/.binahead of other user-level bin directories in zsh and fish early init. - Add/adjust PATH prepends in interactive/login initialization blocks for bash, zsh, and fish to keep the Bun global
.bindirectory at the highest priority. - Extend non-interactive bash environment setup (
bash_env.sh) to include the Bun global.bindirectory.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| home-manager/programs/zsh/default.nix | Prepends Bun global .bin in both always-sourced and interactive zsh init PATH setup. |
| home-manager/programs/fish/default.nix | Adds Bun global .bin to PATH in shellInit and to fish_user_paths in login/interactive init. |
| home-manager/programs/bash/default.nix | Prepends Bun global .bin in bashrcExtra and profileExtra PATH setup. |
| home-manager/programs/bash/bash_env.sh | Adds Bun global .bin handling for non-interactive bash via BASH_ENV. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case ":$PATH:" in | ||
| *":$HOME/.bun/install/global/node_modules/.bin:"*) ;; | ||
| *) export PATH="$HOME/.bun/install/global/node_modules/.bin:$PATH" ;; | ||
| esac |
- Align atuin_history_spec PATH assertions with new bun-globals-first order - Add ordering assertions that bun globals win over /opt/homebrew/bin on macOS - Switch programs.ssh.settings.*.IdentityFile to list (home-manager type)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@spec/atuin_history_spec.sh`:
- Around line 155-170: The awk checks in the three tests use the whole file so
matches outside the target block can satisfy the assertion; update each test
that reads BASH_CONFIG, ZSH_CONFIG, and FISH_CONFIG (the tests named "prepends
bun ... in bash initExtra", "in zsh initContent", and "in fish loginShellInit")
to scope the awk search to the intended config block: have awk first detect the
block start (e.g., the marker/header for bashrcExtra/initExtra, zsh initContent,
fish loginShellInit) by setting a flag when the block header is seen and
clearing it when the next block header appears, and only record NR for brew and
bun while that flag is set, then compare order; this keeps
profileExtra/interactiveShellInit matches from affecting the bashrc/login
checks.
In `@spec/ssh_config_spec.sh`:
- Around line 14-18: Extend the spec "pins the Codex-compatible SSH identity" in
spec/ssh_config_spec.sh to also assert that the rendered SSH config includes
IdentityFile lists for the wildcard host and github.com host; after reading
"$SSH_CONFIG_NIX" add two expectations similar to the existing checks (which
already assert 'IdentityFile = [ "~/.ssh/id_rsa" ]' and 'IdentitiesOnly =
"yes"') but target the Host="*" block and the Host="github.com" block (e.g.
assert the output contains Host = "*" with IdentityFile = [ "~/.ssh/id_rsa" ]
and Host = "github.com" with IdentityFile = [ "~/.ssh/id_rsa" ]) so both kyber
and the other host entries are covered.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6c5fd546-a4dc-40a0-b1c1-57f1eb9347f2
📒 Files selected for processing (7)
home-manager/programs/bash/bash_env.shhome-manager/programs/bash/default.nixhome-manager/programs/fish/default.nixhome-manager/programs/ssh/default.nixhome-manager/programs/zsh/default.nixspec/atuin_history_spec.shspec/ssh_config_spec.sh
| It 'prepends bun global node_modules/.bin after homebrew in bash initExtra so it wins on macOS' | ||
| When run bash -c "awk '/export PATH=\"\\/opt\\/homebrew\\/bin:/{brew=NR} /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$BASH_CONFIG'" | ||
| The status should be success | ||
| The output should eq 'ok' | ||
| End | ||
|
|
||
| It 'prepends bun global node_modules/.bin after homebrew in zsh initContent so it wins on macOS' | ||
| When run bash -c "awk '/export PATH=\"\\/opt\\/homebrew\\/bin:/{brew=NR} /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$ZSH_CONFIG'" | ||
| The status should be success | ||
| The output should eq 'ok' | ||
| End | ||
|
|
||
| It 'prepends bun global node_modules/.bin after homebrew in fish loginShellInit so it wins on macOS' | ||
| When run bash -c "awk '/fish_add_path -p -m \\/opt\\/homebrew\\/bin/{brew=NR} /fish_add_path -p -m ~\\/.bun\\/install\\/global\\/node_modules\\/.bin/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$FISH_CONFIG'" | ||
| The status should be success | ||
| The output should eq 'ok' |
There was a problem hiding this comment.
Scope the awk precedence checks to the intended config block to avoid false positives.
Line 156 and Line 168 currently scan the entire file, so matches from profileExtra/interactiveShellInit can satisfy tests that claim to validate bashrcExtra/loginShellInit. That can hide regressions in the target block.
Suggested fix (scope by block before comparing line order)
-It 'prepends bun global node_modules/.bin after homebrew in bash initExtra so it wins on macOS'
-When run bash -c "awk '/export PATH=\"\\/opt\\/homebrew\\/bin:/{brew=NR} /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$BASH_CONFIG'"
+It 'prepends bun global node_modules/.bin after homebrew in bash bashrcExtra so it wins on macOS'
+When run bash -c "awk '
+ /bashrcExtra = '\'''/ {inblk=1; next}
+ inblk && /'\''';/ {inblk=0}
+ inblk && /export PATH=\"\\/opt\\/homebrew\\/bin:/ {brew=NR}
+ inblk && /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/ {bun=NR}
+ END { if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }
+' '$BASH_CONFIG'"
-It 'prepends bun global node_modules/.bin after homebrew in fish loginShellInit so it wins on macOS'
-When run bash -c "awk '/fish_add_path -p -m \\/opt\\/homebrew\\/bin/{brew=NR} /fish_add_path -p -m ~\\/.bun\\/install\\/global\\/node_modules\\/.bin/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$FISH_CONFIG'"
+It 'prepends bun global node_modules/.bin after homebrew in fish loginShellInit so it wins on macOS'
+When run bash -c "awk '
+ /loginShellInit = '\'''/ {inblk=1; next}
+ inblk && /'\''';/ {inblk=0}
+ inblk && /fish_add_path -p -m \\/opt\\/homebrew\\/bin/ {brew=NR}
+ inblk && /fish_add_path -p -m ~\\/.bun\\/install\\/global\\/node_modules\\/.bin/ {bun=NR}
+ END { if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }
+' '$FISH_CONFIG'"🤖 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 `@spec/atuin_history_spec.sh` around lines 155 - 170, The awk checks in the
three tests use the whole file so matches outside the target block can satisfy
the assertion; update each test that reads BASH_CONFIG, ZSH_CONFIG, and
FISH_CONFIG (the tests named "prepends bun ... in bash initExtra", "in zsh
initContent", and "in fish loginShellInit") to scope the awk search to the
intended config block: have awk first detect the block start (e.g., the
marker/header for bashrcExtra/initExtra, zsh initContent, fish loginShellInit)
by setting a flag when the block header is seen and clearing it when the next
block header appears, and only record NR for brew and bun while that flag is
set, then compare order; this keeps profileExtra/interactiveShellInit matches
from affecting the bashrc/login checks.
| It 'pins the Codex-compatible SSH identity' | ||
| When run cat "$SSH_CONFIG_NIX" | ||
| The output should include 'IdentityFile = "~/.ssh/id_rsa"' | ||
| The output should include 'IdentityFile = [ "~/.ssh/id_rsa" ]' | ||
| The output should include 'IdentitiesOnly = "yes"' | ||
| End |
There was a problem hiding this comment.
Add assertions for wildcard and github.com IdentityFile list rendering.
This only guards kyber; the same contract change on "*" and "github.com" can regress undetected.
Suggested spec extension
It 'pins the Codex-compatible SSH identity'
When run cat "$SSH_CONFIG_NIX"
The output should include 'IdentityFile = [ "~/.ssh/id_rsa" ]'
+The output should include 'IdentityFile = [ "~/.ssh/id_ed25519" ]'
+The output should include 'IdentityFile = [ "~/.ssh/id_ed25519_github" ]'
The output should include 'IdentitiesOnly = "yes"'
End📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| It 'pins the Codex-compatible SSH identity' | |
| When run cat "$SSH_CONFIG_NIX" | |
| The output should include 'IdentityFile = "~/.ssh/id_rsa"' | |
| The output should include 'IdentityFile = [ "~/.ssh/id_rsa" ]' | |
| The output should include 'IdentitiesOnly = "yes"' | |
| End | |
| It 'pins the Codex-compatible SSH identity' | |
| When run cat "$SSH_CONFIG_NIX" | |
| The output should include 'IdentityFile = [ "~/.ssh/id_rsa" ]' | |
| The output should include 'IdentityFile = [ "~/.ssh/id_ed25519" ]' | |
| The output should include 'IdentityFile = [ "~/.ssh/id_ed25519_github" ]' | |
| The output should include 'IdentitiesOnly = "yes"' | |
| End |
🤖 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 `@spec/ssh_config_spec.sh` around lines 14 - 18, Extend the spec "pins the
Codex-compatible SSH identity" in spec/ssh_config_spec.sh to also assert that
the rendered SSH config includes IdentityFile lists for the wildcard host and
github.com host; after reading "$SSH_CONFIG_NIX" add two expectations similar to
the existing checks (which already assert 'IdentityFile = [ "~/.ssh/id_rsa" ]'
and 'IdentitiesOnly = "yes"') but target the Host="*" block and the
Host="github.com" block (e.g. assert the output contains Host = "*" with
IdentityFile = [ "~/.ssh/id_rsa" ] and Host = "github.com" with IdentityFile = [
"~/.ssh/id_rsa" ]) so both kyber and the other host entries are covered.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
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/atuin_history_spec.sh">
<violation number="1" location="spec/atuin_history_spec.sh:156">
P2: The awk precedence check scans the entire config file, not just the `bashrcExtra`/`initExtra` block it claims to validate. A match from `profileExtra` or another block could satisfy this test even if the target block regresses. Scope the awk pattern to the intended config block (e.g., track entering/leaving the block) to avoid false positives.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| End | ||
|
|
||
| It 'prepends bun global node_modules/.bin after homebrew in bash initExtra so it wins on macOS' | ||
| When run bash -c "awk '/export PATH=\"\\/opt\\/homebrew\\/bin:/{brew=NR} /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$BASH_CONFIG'" |
There was a problem hiding this comment.
P2: The awk precedence check scans the entire config file, not just the bashrcExtra/initExtra block it claims to validate. A match from profileExtra or another block could satisfy this test even if the target block regresses. Scope the awk pattern to the intended config block (e.g., track entering/leaving the block) to avoid false positives.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/atuin_history_spec.sh, line 156:
<comment>The awk precedence check scans the entire config file, not just the `bashrcExtra`/`initExtra` block it claims to validate. A match from `profileExtra` or another block could satisfy this test even if the target block regresses. Scope the awk pattern to the intended config block (e.g., track entering/leaving the block) to avoid false positives.</comment>
<file context>
@@ -142,11 +144,30 @@ The output should include '.local/bin'
End
+
+It 'prepends bun global node_modules/.bin after homebrew in bash initExtra so it wins on macOS'
+When run bash -c "awk '/export PATH=\"\\/opt\\/homebrew\\/bin:/{brew=NR} /export PATH=\"\\\$HOME\\/.bun\\/install\\/global\\/node_modules\\/.bin:/{bun=NR} END{ if (brew && bun && bun > brew) print \"ok\"; else print \"bad brew=\" brew \" bun=\" bun }' '$BASH_CONFIG'"
+The status should be success
+The output should eq 'ok'
</file context>
The llama-cpp WebUI npm step hits a libuv kqueue assertion (Assertion failed: (errno == EINTR), function uv__io_poll) on macOS CI runners, failing nix-darwin builds intermittently. llama-cpp on macOS only added a CLI binary; move it under the isLinux block.
home-manager's programs.ssh module defines a default IdentityFile of ~/.ssh/id_rsa for the "*" block, which conflicts with our id_ed25519 value during galactica eval (enableDefaultConfig = false does not prevent this default in the current home-manager release). Use lib.mkForce so our value wins without touching upstream.
Summary
$HOME/.bun/install/global/node_modules/.binas the highest-priority PATH entry in bash, fish, and zsh~/.bun/bin, so global packages installed viabun install -gresolve from the actual node_modules.bindirectory firstbashrcExtra+profileExtra+bash_env.sh, zshenvExtra+initContent, fishshellInit+loginShellInit+interactiveShellInitTest plan
nix flake check --no-buildpassesmake switch,echo $PATHin each shell shows~/.bun/install/global/node_modules/.binat the frontbun add-ed CLIs still resolve as expectedSummary by cubic
Prepends $HOME/.bun/install/global/node_modules/.bin to PATH with highest priority in bash, zsh, and fish so global
bunCLIs resolve first across interactive and login shells. Mirrors ~/.bun/bin precedence and ensures it wins over /opt/homebrew/bin on macOS.Bug Fixes
IdentityFileto list type and forced*to use~/.ssh/id_ed25519withlib.mkForceto override@home-managerdefaults.llama-cppVulkan override toisDesktopfor simpler platform handling.Refactors
Written for commit a8b993d. Summary will update on new commits.