fix(installer): put $PREFIX/bin on PATH instead of warning about it

The three duplicated PATH blocks in tools/install.sh only warned, so an
unattended install finished with rc=0 and left `mosaic: command not found`
— there was no operator to read the advice and act on it. Measured on a
greenfield Debian 13 sandbox: `--next --yes` installed
@mosaicstack/[email protected] successfully and the CLI was still
unreachable.

Replaces all three copies with one ensure_prefix_on_path helper that
appends the export to ~/.profile (~/.zshenv under zsh) and is a no-op when
the prefix is already on PATH or already in a shell profile.

Not ~/.bashrc: Debian's default .bashrc returns early for non-interactive
shells, so a line appended there is unreachable to `bash -lc`, systemd
units and agent seats — the consumers that need the CLI.
This commit is contained in:
2026-08-15 15:47:30 -05:00
parent 7a6fb024b4
commit 40fecd4d38
+52 -15
View File
@@ -309,6 +309,55 @@ require_cmd() {
fi 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() { installed_cli_version() {
local json local json
json="$(npm ls -g --depth=0 --json --prefix="$PREFIX" 2>/dev/null)" || true json="$(npm ls -g --depth=0 --json --prefix="$PREFIX" 2>/dev/null)" || true
@@ -682,11 +731,7 @@ if [[ "$FLAG_CLI" == "true" ]]; then
ensure_monorepo ensure_monorepo
install_cli_from_source install_cli_from_source
# PATH check for npm prefix ensure_prefix_on_path
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
elif is_next_registry_lane; then elif is_next_registry_lane; then
info "Next mode — trying fast npm @next install from ${REGISTRY}" info "Next mode — trying fast npm @next install from ${REGISTRY}"
if install_next_cli_from_registry; then if install_next_cli_from_registry; then
@@ -699,11 +744,7 @@ if [[ "$FLAG_CLI" == "true" ]]; then
export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1
fi fi
# PATH check for npm prefix ensure_prefix_on_path
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
else else
if [[ -z "$LATEST" ]]; then if [[ -z "$LATEST" ]]; then
warn "Could not reach registry at $REGISTRY — skipping npm CLI." 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)." ok "CLI is at or ahead of registry ($CURRENT$LATEST)."
fi fi
# PATH check for npm prefix ensure_prefix_on_path
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
fi fi
fi fi