diff --git a/scripts/setup/machine-setup/lib/dev.sh b/scripts/setup/machine-setup/lib/dev.sh index 95a0cd65..bc1d2dbf 100644 --- a/scripts/setup/machine-setup/lib/dev.sh +++ b/scripts/setup/machine-setup/lib/dev.sh @@ -92,27 +92,36 @@ oh_my_zsh_installed() { [[ -d "${USER_HOME}/.oh-my-zsh" ]]; } install_oh_my_zsh() { # The installer refuses to run unattended over an existing install, so this is # only ever called when there is none. + # + # ── `|| true` is what makes this non-fatal, NOT the `return 0` below ── + # + # It used to be `return 0` alone, with a comment claiming the function returned + # zero whatever happened. It did not. Under `set -e` a failing command inside a + # function aborts the SHELL at that line when the function is called plainly — + # `return 0` is never reached. So a machine where this curl or the installer + # failed died here, silently, because the output is redirected: the run just + # stopped after apt finished installing zsh, with nothing said. Observed on a + # fresh Hetzner VPS, 2026-08-14. sudo -H -u "$USERNAME" sh -c \ - "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended >/dev/null 2>&1 - # Returns 0 whatever happens. This is an optional improvement, and a - # function that ends on a failing command is fatal under `set -e` when it - # is called as a plain command — which would abort the remaining sections - # over something the run could simply report. The caller checks the outcome. + "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended >/dev/null 2>&1 || + true + # Belt and braces: `|| true` above already makes the last command succeed, and + # this states the contract for anyone adding a line beneath it. return 0 } # `chsh` is what actually changes the login shell. Asked separately from # installing zsh, because having a shell available and being handed it at every # login are different decisions. +# Reports whether chsh worked, rather than swallowing it. The same `set -e` trap as +# install_oh_my_zsh applies — a bare `chsh` that fails kills the run at this line — +# but here the answer matters: the caller announces the new login shell, and `|| true` +# would have it announce one that was never set. So the status comes back and the +# CALLER guards the call, which is also what keeps set -e out of it. set_login_shell() { local shell="$1" grep -qxF "$shell" /etc/shells || echo "$shell" >>/etc/shells - chsh -s "$shell" "$USERNAME" - # Returns 0 whatever happens. This is an optional improvement, and a - # function that ends on a failing command is fatal under `set -e` when it - # is called as a plain command — which would abort the remaining sections - # over something the run could simply report. The caller checks the outcome. - return 0 + chsh -s "$shell" "$USERNAME" >/dev/null 2>&1 } # ----------------------------------------------------------------------------- diff --git a/scripts/setup/machine-setup/machine-setup.sh b/scripts/setup/machine-setup/machine-setup.sh index a8c9faae..9913204f 100755 --- a/scripts/setup/machine-setup/machine-setup.sh +++ b/scripts/setup/machine-setup/machine-setup.sh @@ -2217,10 +2217,20 @@ if ! skip; then echo "" echo " ${USERNAME}'s login shell is ${SHELL_NOW}. Changing it to zsh takes" echo " effect at the next login, and does not affect this session." + # Guarded, not bare: `chsh` can refuse — a PAM policy, a shell missing from + # /etc/shells, an account whose password field blocks it — and a bare call would + # end the run there under `set -e`. Reported instead, because a machine with the + # right shell installed and the wrong one at login still works. if confirm "Make zsh the login shell?"; then - set_login_shell "$(command -v zsh)" - ok "login shell is now $(user_login_shell)" - SUMMARY+=("Shell: login shell set to zsh") + if set_login_shell "$(command -v zsh)"; then + ok "login shell is now $(user_login_shell)" + SUMMARY+=("Shell: login shell set to zsh") + else + warn "chsh refused — login shell is still $(user_login_shell)" + echo " Change it later with: chsh -s $(command -v zsh) ${USERNAME}" + ERRORS+=("Shell: chsh refused, login shell left as $(user_login_shell)") + SUMMARY+=("Shell: login shell NOT changed") + fi else warn "left as ${SHELL_NOW}" SUMMARY+=("Shell: login shell left as ${SHELL_NOW}")