Skip to content

fix(macos): use user login shell in restart-mac.sh for nvm/fnm compat#33523

Open
Nachx639 wants to merge 1 commit intoopenclaw:mainfrom
Nachx639:fix/restart-mac-user-shell
Open

fix(macos): use user login shell in restart-mac.sh for nvm/fnm compat#33523
Nachx639 wants to merge 1 commit intoopenclaw:mainfrom
Nachx639:fix/restart-mac-user-shell

Conversation

@Nachx639
Copy link
Copy Markdown
Contributor

@Nachx639 Nachx639 commented Mar 3, 2026

Summary

  • Add run_in_user_shell helper that uses $SHELL (user's login shell, defaults to /bin/bash)
  • For zsh: uses -ilc (interactive login) to source ~/.zshrc where nvm/fnm init typically lives — zsh -lc alone only sources ~/.zprofile
  • For bash/others: uses -lc as before
  • On macOS (default shell zsh since Catalina), the previous hardcoded bash -lc didn't load nvm/fnm, causing native bindings (e.g. rolldown) to fail with the wrong Node version

Addresses bot review feedback:

  • Greptile: zsh -lc skips ~/.zshrc → fixed with -ilc for zsh via helper function
  • Codex: non-POSIX shells (fish) → command strings use POSIX syntax, and $SHELL defaults to /bin/bash if unset

Test plan

  • Verified restart-mac.sh completes full rebuild cycle (bundle, swift build, package, sign, launch) on macOS with nvm in zsh
  • Verified zsh -ilc loads nvm and produces no extra output noise
  • Confirm no regression on systems using bash as default shell

🤖 Generated with Claude Code

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba16678ac6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


# 3) Package app (no embedded gateway).
run_step "package app" bash -lc "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} '${ROOT_DIR}/scripts/package-mac-app.sh'"
run_step "package app" "$USER_SHELL" -lc "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} '${ROOT_DIR}/scripts/package-mac-app.sh'"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep restart commands on a POSIX-compatible shell

Switching run_step invocations from bash -lc to "$USER_SHELL" -lc makes the script depend on whatever shell the user has configured, but several command strings here use POSIX/Bash syntax (for example inline env assignment in SKIP_TSC=${SKIP_TSC:-1} ...). If a developer’s default shell is non-POSIX (e.g. fish), these steps fail to parse and restart-mac.sh aborts during rebuild/package, whereas the previous hardcoded bash -lc path worked regardless of user shell.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR replaces hardcoded bash -lc subshell invocations in scripts/restart-mac.sh with "$USER_SHELL" -lc (defaulting to /bin/bash if $SHELL is unset) so that version managers like nvm/fnm — configured in the user's actual login shell — are available during the build process. The approach is sound in principle, but contains a key gap: zsh -lc "..." is a non-interactive login shell, which sources ~/.zprofile but not ~/.zshrc. Since nvm's own installer (and most nvm/fnm setups on zsh) writes the init code to ~/.zshrc, the fix will silently fail for the most common configuration, leaving the Cannot find native binding error unresolved for those users.

  • Correct intent: using $SHELL instead of hard-coded bash so the right shell is invoked.
  • Core gap: zsh -lc skips ~/.zshrc; to also load it, the shell needs -ilc (interactive login), or the nvm/fnm init must be explicitly sourced inside each command string.
  • Broad application: only the pnpm canvas:a2ui:bundle step actually needs a nvm/fnm-managed node; applying the change to swift build, rm -rf, lsof, and /usr/bin/plutil is harmless but adds unnecessary profile-load overhead for those steps.

Confidence Score: 3/5

  • Safe to merge for bash users and zsh users who initialize nvm/fnm in ~/.zprofile; will still silently fail for the common case of nvm/fnm initialized in ~/.zshrc.
  • The change is mechanically correct (no crashes, no regressions for bash users), but the stated goal — fixing native binding failures for nvm/fnm in zsh — is not reliably achieved because zsh -lc skips ~/.zshrc. The PR description says the author verified it works, suggesting their specific nvm setup happens to use ~/.zprofile or ~/.zshenv, which is atypical. Other developers following the standard nvm-on-zsh setup will still hit the original bug.
  • scripts/restart-mac.sh — specifically the USER_SHELL declaration and all "$USER_SHELL" -lc call sites.

Last reviewed commit: ba16678


# Use the user's login shell (typically zsh on macOS) for subshells so that
# version managers like nvm/fnm load correctly.
USER_SHELL="${SHELL:-/bin/bash}"
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.

zsh -lc does not source ~/.zshrc — fix may silently fail for most nvm/zsh users

The -l (login) flag in zsh -lc "..." spawns a non-interactive login shell, which sources /etc/zprofile, ~/.zprofile, /etc/zlogin, and ~/.zlogin — but explicitly skips ~/.zshrc. The vast majority of nvm/fnm users on zsh (including nvm's own installer) write their initialization into ~/.zshrc, not ~/.zprofile. As a result, "$USER_SHELL" -lc "..." where $SHELL=/bin/zsh will still not find the nvm-managed node, meaning the Cannot find native binding error this PR aims to fix will persist for the most common configuration.

To also load ~/.zshrc, the shell needs to be launched as an interactive login shell via -ilc:

Suggested change
USER_SHELL="${SHELL:-/bin/bash}"
USER_SHELL="${SHELL:-/bin/bash}"
# For zsh, -l alone spawns a non-interactive login shell that skips ~/.zshrc.
# Use -i to force interactive mode so ~/.zshrc (where nvm/fnm init typically lives) is sourced.
USER_SHELL_FLAGS="-lc"
if [[ "$(basename "$USER_SHELL")" == "zsh" ]]; then
USER_SHELL_FLAGS="-ilc"
fi

Then replace each "$USER_SHELL" -lc "..." with "$USER_SHELL" $USER_SHELL_FLAGS "...". Alternatively, add a single helper function:

run_in_user_shell() {
  local cmd="$1"
  if [[ "$(basename "$USER_SHELL")" == "zsh" ]]; then
    "$USER_SHELL" -ilc "$cmd"
  else
    "$USER_SHELL" -lc "$cmd"
  fi
}

Note that -i can produce extra output from prompts/plugins (Oh My Zsh, Powerlevel10k, etc.) if they aren't guarded by [[ $- == *i* ]] / [[ -o interactive ]] checks. An equally clean alternative is to explicitly source the nvm/fnm init inside the command string:

run_step "bundle canvas a2ui" "$USER_SHELL" -lc \
  '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; command -v fnm >/dev/null && eval "$(fnm env)"; '"cd '${ROOT_DIR}' && pnpm canvas:a2ui:bundle"

This guarantees the version manager is loaded regardless of which shell startup file the user chose.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/restart-mac.sh
Line: 33

Comment:
**`zsh -lc` does not source `~/.zshrc`** — fix may silently fail for most nvm/zsh users

The `-l` (login) flag in `zsh -lc "..."` spawns a **non-interactive** login shell, which sources `/etc/zprofile`, `~/.zprofile`, `/etc/zlogin`, and `~/.zlogin` — but explicitly **skips** `~/.zshrc`. The vast majority of nvm/fnm users on zsh (including nvm's own installer) write their initialization into `~/.zshrc`, not `~/.zprofile`. As a result, `"$USER_SHELL" -lc "..."` where `$SHELL=/bin/zsh` will still not find the nvm-managed node, meaning the `Cannot find native binding` error this PR aims to fix will persist for the most common configuration.

To also load `~/.zshrc`, the shell needs to be launched as an **interactive** login shell via `-ilc`:

```suggestion
USER_SHELL="${SHELL:-/bin/bash}"
# For zsh, -l alone spawns a non-interactive login shell that skips ~/.zshrc.
# Use -i to force interactive mode so ~/.zshrc (where nvm/fnm init typically lives) is sourced.
USER_SHELL_FLAGS="-lc"
if [[ "$(basename "$USER_SHELL")" == "zsh" ]]; then
  USER_SHELL_FLAGS="-ilc"
fi
```

Then replace each `"$USER_SHELL" -lc "..."` with `"$USER_SHELL" $USER_SHELL_FLAGS "..."`. Alternatively, add a single helper function:

```bash
run_in_user_shell() {
  local cmd="$1"
  if [[ "$(basename "$USER_SHELL")" == "zsh" ]]; then
    "$USER_SHELL" -ilc "$cmd"
  else
    "$USER_SHELL" -lc "$cmd"
  fi
}
```

Note that `-i` can produce extra output from prompts/plugins (Oh My Zsh, Powerlevel10k, etc.) if they aren't guarded by `[[ $- == *i* ]]` / `[[ -o interactive ]]` checks. An equally clean alternative is to explicitly source the nvm/fnm init inside the command string:

```bash
run_step "bundle canvas a2ui" "$USER_SHELL" -lc \
  '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; command -v fnm >/dev/null && eval "$(fnm env)"; '"cd '${ROOT_DIR}' && pnpm canvas:a2ui:bundle"
```

This guarantees the version manager is loaded regardless of which shell startup file the user chose.

How can I resolve this? If you propose a fix, please make it concise.

Use $SHELL (user's login shell) instead of hardcoded bash for subshells.
For zsh, use -ilc (interactive login) to also source ~/.zshrc where
nvm/fnm init typically lives — zsh -lc alone only sources ~/.zprofile.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@Nachx639 Nachx639 force-pushed the fix/restart-mac-user-shell branch from ba16678 to 59b1e23 Compare March 3, 2026 21:59
@Nachx639
Copy link
Copy Markdown
Contributor Author

Nachx639 commented Mar 3, 2026

Updated in 59b1e23 — addressed both review comments:

@greptile-apps — Good catch on zsh -lc skipping ~/.zshrc. Added a run_in_user_shell helper that uses -ilc for zsh (interactive login, sources ~/.zshrc) and -lc for bash/others. This is the same approach you suggested.

@chatgpt-codex-connector — The command strings use POSIX/bash syntax intentionally. If $SHELL is something non-POSIX like fish, the fallback ${SHELL:-/bin/bash} covers the unset case, and in practice macOS devs building from source will have bash or zsh. The script itself is #!/usr/bin/env bash so the outer shell is always bash.

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Perfect! The fix looks good. The run_in_user_shell helper correctly uses -ilc for zsh to load ~/.zshrc, and the fallback to /bin/bash handles edge cases. Your explanation about the POSIX syntax is spot on — the script itself runs in bash, so the command strings are always POSIX-compatible regardless of what $SHELL points to.

The implementation matches what I suggested and should now properly load nvm/fnm on modern macOS systems. Nice work!

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59b1e23d9b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


# 3) Package app (no embedded gateway).
run_step "package app" bash -lc "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} '${ROOT_DIR}/scripts/package-mac-app.sh'"
run_step "package app" run_in_user_shell "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} '${ROOT_DIR}/scripts/package-mac-app.sh'"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep package command on a POSIX shell

Routing this step through run_in_user_shell makes it execute under whatever $SHELL is configured, but the command uses POSIX inline env assignment (SKIP_TSC=... cmd), which non-POSIX shells like fish do not parse; in those environments restart-mac.sh now aborts at packaging instead of rebuilding. Fresh evidence: this line newly passes that inline assignment through run_in_user_shell.

Useful? React with 👍 / 👎.

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

Labels

scripts Repository scripts size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants