Ask everything first, then run without needing anybody
sunhat's failure mode was a question twenty minutes into a run. Walking away from an install meant coming back to a prompt that had been waiting an hour. So the questions move to the front. A new interview stage asks what Panama needs to be told -- hostname, git identity, whether to sign in to GitHub, whether to make an SSH key -- shows the answers back, and asks once to proceed. After that nothing asks again. gum is bootstrapped before it runs, because the interview is built on gum and gum arrives with a stage that has not run yet. Answers reach the stages through a mktemp file that install sources and the existing trap deletes, since a child process cannot export into its siblings. They are not remembered between runs: there is no state file to go stale, and one of the answers is an email address. The interview asks only what a stage in this repository actually consumes. Extras, hardware and debloat questions arrive with the stages that act on them -- a prompt whose answer nothing reads is a control that lies. The new contract pins that in both directions, and four deliberate mutations confirmed it catches a question nobody reads, a stage reading something nobody asks, an answers file left on disk, and a declined interview that fails to stop the run. The run now ends with panama-doctor, because a failed-stage count says nothing about a service that did not start. It never changes the exit code: on a fresh machine, unconfigured is the honest answer, not a failure. espanso and oh-my-posh stop being exceptions -- Terra packages espanso-wayland and Fedora packages oh-my-posh, so the curl installer is gone. bun is now the only remaining one. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
This commit is contained in:
@@ -8,25 +8,22 @@ set -uo pipefail
|
||||
PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
||||
source "$PANAMA_PATH/bin/ascii"
|
||||
|
||||
# ── Hostname, which is optional ──────────────────────────────────────────────
|
||||
# ── The interview ────────────────────────────────────────────────────────────
|
||||
#
|
||||
# Declining this used to `exit`, which aborted the ENTIRE installation. The
|
||||
# prompt defaults to N, so simply pressing Enter -- the obvious thing to do when
|
||||
# you do not want to rename your machine -- installed nothing at all and said
|
||||
# nothing about it.
|
||||
echo -e "Current hostname is: $(hostname)"
|
||||
read -r -p "Do you want to change the hostname? [y/N]: " confirm_change
|
||||
if [[ "$confirm_change" =~ ^[Yy]$ ]]; then
|
||||
read -r -p "Hostname: " HOST_NAME
|
||||
read -r -p "Set hostname to '$HOST_NAME'? [y/N]: " confirm_hostname
|
||||
if [[ "$confirm_hostname" =~ ^[Yy]$ ]]; then
|
||||
sudo hostnamectl set-hostname "$HOST_NAME"
|
||||
echo "Hostname set to: $(hostname)"
|
||||
else
|
||||
echo "Hostname not changed."
|
||||
fi
|
||||
else
|
||||
echo "Keeping the current hostname."
|
||||
# Everything Panama needs to be told is asked here, before a single package is
|
||||
# installed, and nothing asks again afterwards. That is the whole bargain: the
|
||||
# rest of the run takes twenty minutes and needs nobody watching it.
|
||||
#
|
||||
# gum is bootstrapped first because the interview is built on it and it cannot
|
||||
# install itself -- it is declared in initial-packages, which install-packages
|
||||
# installs, which runs after this. One small dnf call buys a real interface for
|
||||
# the only part of the install a person actually interacts with.
|
||||
if ! command -v gum >/dev/null 2>&1; then
|
||||
echo "Installing gum, which the setup questions are built on"
|
||||
sudo dnf install -y gum >/dev/null || {
|
||||
echo "Could not install gum, so the setup questions cannot be asked." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# ── Keep the machine awake for the duration ──────────────────────────────────
|
||||
@@ -34,11 +31,14 @@ fi
|
||||
# out mid-transaction is unpleasant. Restored on every exit path, including
|
||||
# failure and Ctrl-C, so an interrupted install does not leave the screen
|
||||
# permanently awake.
|
||||
restore_idle() {
|
||||
cleanup() {
|
||||
gsettings set org.gnome.desktop.screensaver lock-enabled true 2>/dev/null || true
|
||||
gsettings set org.gnome.desktop.session idle-delay 300 2>/dev/null || true
|
||||
# Deleted on every exit path, including Ctrl-C. The answers are transient by
|
||||
# design, and one of them is an email address.
|
||||
[[ -n "${PANAMA_ANSWERS:-}" ]] && rm -f "$PANAMA_ANSWERS"
|
||||
}
|
||||
trap restore_idle EXIT INT TERM
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
gsettings set org.gnome.desktop.screensaver lock-enabled false 2>/dev/null || true
|
||||
gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
|
||||
@@ -52,9 +52,39 @@ gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true
|
||||
#
|
||||
# Explicit order, not glob order: change-settings runs `vicinae theme set`,
|
||||
# which needs both vicinae itself (installed by install-packages) and the
|
||||
# theme files it selects among (symlinked into place by link-dotfiles). New
|
||||
# theme files it selects among (symlinked into place by link-dotfiles);
|
||||
# setup-identity needs the gh and git-all that install-packages provides. New
|
||||
# scripts must be added here explicitly, or they will not run at all.
|
||||
STAGES=(install-packages link-dotfiles change-settings link-vicinae-scripts)
|
||||
|
||||
# The interview is not in that list, because it is the one stage whose output the
|
||||
# installer reads back -- and because declining it must stop everything rather
|
||||
# than be recorded as one failure among several.
|
||||
#
|
||||
# The answers live for exactly one run. There is no state file to go stale and
|
||||
# nothing personal reaches a durable path, which is what keeps this repository
|
||||
# something somebody else could clone. Created here rather than earlier so the
|
||||
# trap that deletes it is already armed before the file exists.
|
||||
PANAMA_ANSWERS="$(mktemp -t panama-answers.XXXXXX)"
|
||||
export PANAMA_ANSWERS
|
||||
|
||||
if ! "$PANAMA_PATH/setup/scripts/interview"; then
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=/dev/null
|
||||
source "$PANAMA_ANSWERS"
|
||||
export PANAMA_HOSTNAME PANAMA_GIT_NAME PANAMA_GIT_EMAIL PANAMA_GIT_EDITOR \
|
||||
PANAMA_GH_LOGIN PANAMA_SSH_KEY
|
||||
|
||||
# Applied here rather than in a stage, and applied early: it needs sudo, and
|
||||
# sudo is warm right now. At the end of a long unattended run the timestamp has
|
||||
# expired, and a password prompt then is exactly the interruption the interview
|
||||
# exists to prevent.
|
||||
if [[ -n "${PANAMA_HOSTNAME:-}" ]]; then
|
||||
sudo hostnamectl set-hostname "$PANAMA_HOSTNAME"
|
||||
echo "Hostname set to: $(hostname)"
|
||||
fi
|
||||
|
||||
STAGES=(install-packages link-dotfiles change-settings link-vicinae-scripts setup-identity)
|
||||
failed=()
|
||||
for stage in "${STAGES[@]}"; do
|
||||
script="$PANAMA_PATH/setup/scripts/$stage"
|
||||
@@ -66,6 +96,22 @@ for stage in "${STAGES[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Did it actually work? ────────────────────────────────────────────────────
|
||||
#
|
||||
# A failed-stage count only reports what exited non-zero. It says nothing about a
|
||||
# service that did not start or a font that did not land, and those are the
|
||||
# failures that survive an install unnoticed. Doctor answers the question the
|
||||
# stage list cannot.
|
||||
#
|
||||
# It never changes the exit code. On a fresh machine it legitimately reports
|
||||
# things as unconfigured -- no Home Assistant token yet, Nextcloud not signed in
|
||||
# -- and failing an install over those would be crying wolf.
|
||||
doctor="$PANAMA_PATH/config/dot/quickshell/scripts/panama-doctor"
|
||||
if [[ -x "$doctor" ]]; then
|
||||
printf '\n=== health ===\n'
|
||||
"$doctor" --summary || true
|
||||
fi
|
||||
|
||||
printf '\n'
|
||||
if (( ${#failed[@]} == 0 )); then
|
||||
echo "Panama installed. Log out and choose the Hyprland session to start it."
|
||||
|
||||
Reference in New Issue
Block a user