From c07a23e9a126633c3a674e490dec5a0096670f53 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Tue, 7 Jul 2026 07:44:56 -0700 Subject: [PATCH] install: wire non-interactive PATH into ~/.zshenv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bare `ssh host 'castle …'` / `'pnpm …'` couldn't find the toolchain: uv, castle, pnpm, and nvm node live on the interactive PATH (~/.zsh.d/*.sh), which non-interactive/non-login shells don't load. Add ensure_shell_path() to mirror just those PATH entries into ~/.zshenv (which non-interactive zsh does load), so every bootstrapped node can drive castle/pnpm over ssh. Idempotent, marker- guarded, and skipped for non-zsh users. --- install.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/install.sh b/install.sh index cb75422..b7a54e5 100755 --- a/install.sh +++ b/install.sh @@ -249,6 +249,45 @@ install_cli() { log_ok } +# Non-interactive/non-login shells (`ssh host cmd`, systemd units, `castle` dev verbs) +# load ~/.zshenv but NOT ~/.zshrc → ~/.zsh.d/*.sh — so tools installed there (uv, +# castle, pnpm, nvm node) don't resolve over a bare `ssh host 'castle …'`. Mirror just +# the PATH entries into ~/.zshenv (interactive niceties stay in ~/.zsh.d). Idempotent. +ensure_shell_path() { + log_step "Wiring PATH for non-interactive shells" + case "${SHELL:-}" in + */zsh) : ;; + *) + if [ ! -e "${HOME}/.zshrc" ] && [ ! -e "${HOME}/.zshenv" ]; then + log_skip "not a zsh user" + return + fi + ;; + esac + local zshenv="${HOME}/.zshenv" + if [ -f "$zshenv" ] && grep -q "castle non-interactive PATH" "$zshenv"; then + log_skip "already wired" + return + fi + cat >> "$zshenv" <<'ZSHENV' + +# >>> castle non-interactive PATH >>> +# Non-interactive/non-login shells (ssh 'cmd', systemd, castle dev verbs) load +# ~/.zshenv but not ~/.zshrc → ~/.zsh.d/*.sh. Mirror the PATH entries castle's +# toolchain needs (uv/castle in ~/.local/bin, pnpm, nvm node) so they resolve there. +for _d in "$HOME/.local/bin" "$HOME/.local/share/pnpm"; do + case ":$PATH:" in *":$_d:"*) ;; *) [ -d "$_d" ] && PATH="$_d:$PATH" ;; esac +done +for _nb in "$HOME"/.nvm/versions/node/*/bin(N); do + case ":$PATH:" in *":$_nb:"*) ;; *) PATH="$_nb:$PATH" ;; esac +done +export PATH +unset _d _nb +# <<< castle non-interactive PATH <<< +ZSHENV + log_ok "~/.zshenv" +} + # --------------------------------------------------------------------------- # Directory structure # --------------------------------------------------------------------------- @@ -607,6 +646,7 @@ main() { [ -n "$WITH_DNS_PLUGIN" ] && ensure_caddy_dns_plugin "$WITH_DNS_PLUGIN" ensure_uv install_cli + ensure_shell_path create_directories seed_control_plane enable_lingering