diff --git a/tools/install.sh b/tools/install.sh index 46c54f34..f41040e4 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -309,6 +309,55 @@ require_cmd() { fi } +# Persist $PREFIX/bin on PATH instead of only warning about it. +# +# The warning it replaces was the last step of an otherwise successful install, +# so the installer reported success and left `mosaic: command not found` — an +# unattended install had no operator to read the advice and act on it. +# +# Deliberately NOT ~/.bashrc: Debian's default .bashrc returns early for +# non-interactive shells, so a PATH line appended to the bottom of it is +# unreachable to `bash -lc`, to systemd units, and to every agent seat — the +# exact consumers that need the CLI. ~/.profile is read by login shells and +# Debian's .profile sources .bashrc for interactive ones, so a single line there +# reaches both. For zsh the always-sourced file is .zshenv, not .zshrc. +ensure_prefix_on_path() { + if [[ ":$PATH:" == *":$PREFIX/bin:"* ]]; then + return + fi + + local profile rc_files=("$HOME/.profile" "$HOME/.zshenv" "$HOME/.zshrc" "$HOME/.bashrc") + if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$(basename "${SHELL:-}")" == "zsh" ]]; then + profile="$HOME/.zshenv" + else + profile="$HOME/.profile" + fi + + if grep -qslF "$PREFIX/bin" "${rc_files[@]}" 2>/dev/null; then + warn "$PREFIX/bin is in your shell profile but not in this shell" + dim " Run: export PATH=\"$PREFIX/bin:\$PATH\" (or start a new login shell)" + return + fi + + # Probe writability in a subshell. A redirection failure on a special built-in + # aborts the shell it runs in, so it has to be a child; and the redirection on + # the subshell is what silences the "Permission denied" the shell would + # otherwise print ahead of our own message. + if ! ( : >>"$profile" ) 2>/dev/null; then + warn "$PREFIX/bin is not on your PATH and $profile could not be written" + dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\"" + return + fi + + { + echo "" + echo "# Mosaic CLI" + echo "export PATH=\"$PREFIX/bin:\$PATH\"" + } >>"$profile" + ok "Added $PREFIX/bin to PATH in $profile" + dim " Run: export PATH=\"$PREFIX/bin:\$PATH\" (or start a new login shell)" +} + installed_cli_version() { local json json="$(npm ls -g --depth=0 --json --prefix="$PREFIX" 2>/dev/null)" || true @@ -682,11 +731,7 @@ if [[ "$FLAG_CLI" == "true" ]]; then ensure_monorepo install_cli_from_source - # PATH check for npm prefix - if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then - warn "$PREFIX/bin is not on your PATH" - dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\"" - fi + ensure_prefix_on_path elif is_next_registry_lane; then info "Next mode — trying fast npm @next install from ${REGISTRY}…" if install_next_cli_from_registry; then @@ -699,11 +744,7 @@ if [[ "$FLAG_CLI" == "true" ]]; then export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 fi - # PATH check for npm prefix - if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then - warn "$PREFIX/bin is not on your PATH" - dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\"" - fi + ensure_prefix_on_path else if [[ -z "$LATEST" ]]; then warn "Could not reach registry at $REGISTRY — skipping npm CLI." @@ -721,11 +762,7 @@ if [[ "$FLAG_CLI" == "true" ]]; then ok "CLI is at or ahead of registry ($CURRENT ≥ $LATEST)." fi - # PATH check for npm prefix - if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then - warn "$PREFIX/bin is not on your PATH" - dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\"" - fi + ensure_prefix_on_path fi fi