remember the pre-flight answers between runs

The --only flag worked, in that it reached the section — but reaching it meant
answering four pre-flight questions first, every time, which is not usable for
working on one section. The same problem was already there without --only: a
resumed run re-asked the role, the account and the Officer path that it had been
told on the previous pass.

Answers are saved beside the progress file and loaded before anything is asked.
The environment still wins over what was saved, so SETUP_USERNAME=x on the
command line overrides it, and --reask throws the file away and asks again.

Read as assignments rather than sourced. The file sits next to the script and is
read by a run that is already root; sourcing it would make it executable content
in a place nothing guards.

Second run now goes straight through pre-flight, printing what it remembered, to
the one step asked for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 19:29:15 +00:00
co-authored by Claude Opus 5
parent 09303299cc
commit a63a327065
3 changed files with 56 additions and 1 deletions
+43
View File
@@ -85,6 +85,49 @@ fail() {
# step_ok
# fi
# -----------------------------------------------------------------------------
# Remembering the answers
# -----------------------------------------------------------------------------
#
# Pre-flight asks four things — role, account, where Officer goes — and every
# section needs them. Asking again on every run made a resumed run re-answer
# questions it had already been told, and made --only unusable: four questions to
# reach one section.
#
# Saved beside the progress file, and loaded before anything is asked. The
# environment still wins, so SETUP_USERNAME=x on the command line overrides what
# was saved.
ANSWERS_FILE="${ANSWERS_FILE:-}"
save_answers() {
[[ -n "$ANSWERS_FILE" ]] || return 0
cat >"$ANSWERS_FILE" <<EOF
# Written by machine-setup. Delete this to be asked again.
MACHINE_ROLE=${MACHINE_ROLE}
SETUP_USERNAME=${USERNAME}
OFFICER_ROOT=${OFFICER_ROOT}
EOF
chmod 600 "$ANSWERS_FILE"
}
# Loaded as assignments, not sourced as a script: this file sits beside the
# script and is read by a root run, so it should not be able to execute anything.
load_answers() {
[[ -n "$ANSWERS_FILE" && -r "$ANSWERS_FILE" ]] || return 0
local key value
while IFS='=' read -r key value; do
[[ "$key" =~ ^[A-Z_]+$ ]] || continue
[[ -n "$value" ]] || continue
# The environment wins over what was saved.
case "$key" in
MACHINE_ROLE) [[ -z "${MACHINE_ROLE:-}" ]] && MACHINE_ROLE="$value" ;;
SETUP_USERNAME) [[ -z "${SETUP_USERNAME:-}" ]] && SETUP_USERNAME="$value" ;;
OFFICER_ROOT) [[ -z "${OFFICER_ROOT:-}" ]] && OFFICER_ROOT="$value" ;;
esac
done <"$ANSWERS_FILE"
}
# Set by --only. When it is set, every step whose name does not match is passed
# over in silence, and the one that does matches runs regardless of the progress
# file — the point of asking for a single step is to run that step.
+12 -1
View File
@@ -12,6 +12,7 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROGRESS_FILE="$SCRIPT_DIR/.setup-progress"
ANSWERS_FILE="$SCRIPT_DIR/.setup-answers"
# --only <step> runs one section and nothing else, for working on it. Pre-flight
# still runs, because every section needs what it establishes — the system, the
@@ -27,12 +28,16 @@ while [[ $# -gt 0 ]]; do
ONLY_STEP="${1#*=}"
shift
;;
--reask)
RE_ASK=1
shift
;;
-l | --list)
grep -oP '^step "\K[^"]+' "${BASH_SOURCE[0]}"
exit 0
;;
-h | --help)
echo "usage: machine-setup.sh [--only <step>] [--list]"
echo "usage: machine-setup.sh [--only <step>] [--reask] [--list]"
exit 0
;;
*) echo "unknown option: $1" >&2 && exit 2 ;;
@@ -78,6 +83,10 @@ echo -e "${BOLD}╔════════════════════
echo -e "${BOLD}║ Machine Setup ║${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════╝${NC}"
# Anything answered on a previous run, unless --reask says to ask again.
[[ -n "${RE_ASK:-}" ]] && rm -f "$ANSWERS_FILE"
load_answers
detect_os
echo ""
info "Machine: ${OS_NAME} (${ARCH})"
@@ -123,6 +132,8 @@ else
info "Officer: ${OFFICER_ROOT}"
fi
save_answers
# Always, and outside any step: everything below reads this index — core utils,
# the fastfetch PPA, the Docker repo — and `step` skips a step whose name is
# already in the progress file. With the refresh inside one of those, a resumed