correct the mechanism recorded for the set -e failure

The previous commit blamed the loop body. That is wrong, and I only found out by
trying to reproduce it: the same `[[ … ]] && assign` inside a case inside a while
loop survives `set -e` perfectly well at top level.

What actually happened is one level further out. The failing assignment was the
last thing the case ran, the case was the last thing the loop body ran, and the
loop was the last thing THE FUNCTION ran — so load_answers returned non-zero, and
calling a function that returns non-zero is a plain command failure, which does
end the script.

Worth getting right because the general rule is different from the one I wrote: it
is not "avoid && in loops", it is "a function whose last statement can return
non-zero fails when it is called, however innocuous the statement looks".

Scanned the libraries for that shape. The only hit is lan_cidr, which ends in an
awk pipeline and returns 0. Predicate functions ending in a bare test —
ballast_exists, has_authorized_key and the rest — are meant to return non-zero
and are only ever called in conditions, which set -e exempts.

The fix itself was already correct and is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 19:46:30 +00:00
co-authored by Claude Opus 5
parent 4d478cc0f1
commit ff7035a47a
+16 -6
View File
@@ -121,12 +121,22 @@ load_answers() {
[[ -n "$value" ]] || continue
# The environment wins over what was saved.
#
# Written as if/then rather than `[[ … ]] && assign`. That form returns
# non-zero when the test is false, and as the last statement in a loop body
# under `set -e` it takes the whole script down — which is exactly what
# happened the first time this ran with the variables already set in the
# environment: the file loaded fine, every value was already present, and the
# run died at "Step: unknown" before pre-flight.
# Written as if/then rather than `[[ … ]] && assign`.
#
# That form returns non-zero when the test is false. Harmless on its own —
# `set -e` exempts the left side of an && list — but here it is the last
# thing the case runs, the case is the last thing the loop body runs, and the
# loop is the last thing THE FUNCTION runs. So load_answers returned
# non-zero, and calling a function that returns non-zero is a plain command
# failure, which does end the script.
#
# It needed the answers file to exist AND the variables to be set already, so
# it only appeared when running with env overrides. The trap reported "Step:
# unknown" at a line inside this library, before pre-flight had run.
#
# The general rule this is an instance of: a function whose last statement
# can return non-zero fails when it is called, however innocuous the
# statement looks.
case "$key" in
MACHINE_ROLE) if [[ -z "${MACHINE_ROLE:-}" ]]; then MACHINE_ROLE="$value"; fi ;;
SETUP_USERNAME) if [[ -z "${SETUP_USERNAME:-}" ]]; then SETUP_USERNAME="$value"; fi ;;