Give every config array entry its own image, and build arm64 audio once
Follow-up to the tools/ build system: the kiosk and amd64 audio-endpoint builders left their ISO in the live-build tree under live-build's fixed filename, and never moved it to build-output/. Configure two thin clients and build-all would build both into the same path — the second silently overwriting the first, leaving one ISO carrying the second room's hostname and no sign the first was ever produced. core_publish_image() now moves each finished image to build-output/smarthome-<hostname>-<pairid>.iso, so one array entry produces one artifact. Verified: two thin clients now yield two distinct ISOs, and the per-type builders still refuse with the real list when the hostname is omitted or wrong. The arm64 audio endpoint is the deliberate exception and is now handled as one: Pi Imager sets hostname/Wi-Fi per unit at flash time, so a single generic .img serves every room and build-all builds it once regardless of how many arm64 entries are listed. amd64 has no equivalent for a generic x86 ISO, so it bakes the hostname in and does need one per room (project-plan Phase 15.5). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>digest-per-person-and-agendas
parent
ea82ee70ad
commit
bbeabde97e
0
hosts/thin-client/live-build/config/hooks/normal/0800-eww-widget.hook.chroot
Normal file → Executable file
0
hosts/thin-client/live-build/config/hooks/normal/0800-eww-widget.hook.chroot
Normal file → Executable file
0
hosts/thin-client/live-build/config/hooks/normal/0900-firefox.hook.chroot
Normal file → Executable file
0
hosts/thin-client/live-build/config/hooks/normal/0900-firefox.hook.chroot
Normal file → Executable file
0
hosts/thin-client/live-build/config/hooks/normal/1000-ydotool.hook.chroot
Normal file → Executable file
0
hosts/thin-client/live-build/config/hooks/normal/1000-ydotool.hook.chroot
Normal file → Executable file
|
|
@ -94,6 +94,24 @@ Building one image is supported but unusual: the images are a set that has to ag
|
|||
with itself, which is why `build-all.sh` is the default and a failure in one image
|
||||
doesn't abandon the rest.
|
||||
|
||||
### One entry, one image
|
||||
|
||||
Kiosks and audio endpoints come from **arrays** in the config, and every entry gets its
|
||||
own artifact in `build-output/`, named `smarthome-<hostname>-<pairid>.iso`. That naming
|
||||
is load-bearing rather than cosmetic: live-build always writes the same filename into
|
||||
the same per-host tree, so two thin clients would otherwise have the second silently
|
||||
overwrite the first — leaving one ISO carrying the second room's hostname and nothing
|
||||
to indicate the first was lost.
|
||||
|
||||
Per-type builders take a hostname when several of that type exist, and refuse with the
|
||||
list of real ones if you omit it or get it wrong.
|
||||
|
||||
**The one deliberate exception is the arm64 audio endpoint.** Raspberry Pi Imager sets
|
||||
hostname and Wi-Fi per unit at flash time, so one generic `.img` serves every room and
|
||||
`build-all.sh` builds it once no matter how many arm64 entries are listed. amd64 has no
|
||||
equivalent tool for a generic x86 ISO, so it bakes the hostname in and genuinely needs
|
||||
one ISO per room — see `docs/project-plan.md` Phase 15.5.
|
||||
|
||||
## The ISOs contain secrets
|
||||
|
||||
This is deliberate — burning everything in is what makes installation unattended, with
|
||||
|
|
|
|||
|
|
@ -137,18 +137,35 @@ if [[ "$MODE" == "all" || "$MODE" == "kiosks" ]]; then
|
|||
run_build "${ktype} — ${khost}" "$builder" "$khost"
|
||||
done
|
||||
|
||||
# The two audio-endpoint architectures are asymmetric on purpose, and it changes how
|
||||
# many images each needs (docs/project-plan.md Phase 15.5):
|
||||
# amd64 — no x86 equivalent of Pi Imager's OS Customisation exists, so the hostname
|
||||
# is baked in at build time. One ISO PER ROOM.
|
||||
# arm64 — Raspberry Pi Imager sets hostname/Wi-Fi per unit at flash time, so one
|
||||
# generic .img serves every room. Built ONCE no matter how many are listed.
|
||||
arm64_done="false"
|
||||
for row in "${AUDIO_ROWS[@]:-}"; do
|
||||
[[ -n "$row" ]] || continue
|
||||
IFS=$'\t' read -r aarch ahost _ <<< "$row"
|
||||
# Two genuinely different toolchains, not one builder with a flag: live-build for
|
||||
# the amd64 mini-PC, rpi-image-gen for the arm64 Pi.
|
||||
case "$aarch" in
|
||||
amd64) builder="${SCRIPT_DIR}/build-audio-endpoint-iso-amd64.sh" ;;
|
||||
arm64) builder="${SCRIPT_DIR}/build-audio-endpoint-image-arm64.sh" ;;
|
||||
*) core_warn "Unknown audio endpoint arch '${aarch}' — skipping ${ahost}"
|
||||
FAILED+=("audio/${ahost} (bad arch)"); continue ;;
|
||||
amd64)
|
||||
run_build "audio endpoint (amd64) — ${ahost}" \
|
||||
"${SCRIPT_DIR}/build-audio-endpoint-iso-amd64.sh" "$ahost"
|
||||
;;
|
||||
arm64)
|
||||
if [[ "$arm64_done" == "true" ]]; then
|
||||
core_log "arm64 audio image already built — ${ahost} flashes the same .img (hostname set in Pi Imager)"
|
||||
continue
|
||||
fi
|
||||
run_build "audio endpoint (arm64, generic image)" \
|
||||
"${SCRIPT_DIR}/build-audio-endpoint-image-arm64.sh" "$ahost"
|
||||
arm64_done="true"
|
||||
;;
|
||||
*)
|
||||
core_warn "Unknown audio endpoint arch '${aarch}' — skipping ${ahost}"
|
||||
FAILED+=("audio/${ahost} (bad arch)")
|
||||
;;
|
||||
esac
|
||||
run_build "audio endpoint (${aarch}) — ${ahost}" "$builder" "$ahost"
|
||||
done
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,10 @@ lb build
|
|||
|
||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
||||
# Per-room name: unlike the arm64 image below, amd64 bakes the hostname in at build
|
||||
# time (there is no x86 equivalent of Pi Imager's OS Customisation), so each room is a
|
||||
# genuinely different ISO and they must not share a filename.
|
||||
ISO_PATH="$(core_publish_image "$ISO_PATH" "audio-endpoint" "$IMAGE_HOSTNAME")"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
|
|
|
|||
|
|
@ -242,6 +242,9 @@ lb build
|
|||
|
||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
||||
# Published under this kiosk's own hostname — live-build reuses one filename per host
|
||||
# tree, so two of the same kiosk type would otherwise overwrite each other.
|
||||
ISO_PATH="$(core_publish_image "$ISO_PATH" "door-panel" "$IMAGE_HOSTNAME")"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ lb build
|
|||
|
||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
||||
# Published under this kiosk's own hostname — live-build reuses one filename per host
|
||||
# tree, so two of the same kiosk type would otherwise overwrite each other.
|
||||
ISO_PATH="$(core_publish_image "$ISO_PATH" "kitchen-display" "$IMAGE_HOSTNAME")"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
|
|
|
|||
|
|
@ -328,6 +328,9 @@ lb build
|
|||
|
||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
||||
# Published under this kiosk's own hostname — live-build reuses one filename per host
|
||||
# tree, so two of the same kiosk type would otherwise overwrite each other.
|
||||
ISO_PATH="$(core_publish_image "$ISO_PATH" "thin-client" "$IMAGE_HOSTNAME")"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
|
|
|
|||
|
|
@ -255,6 +255,9 @@ lb build
|
|||
|
||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
||||
# Published under this kiosk's own hostname — live-build reuses one filename per host
|
||||
# tree, so two of the same kiosk type would otherwise overwrite each other.
|
||||
ISO_PATH="$(core_publish_image "$ISO_PATH" "touch-panel" "$IMAGE_HOSTNAME")"
|
||||
|
||||
echo
|
||||
echo "=== Done ==="
|
||||
|
|
|
|||
|
|
@ -173,6 +173,42 @@ SMARTHOME_LLM_HOST=${CORE_LLM_HOST_IP}
|
|||
EOF
|
||||
}
|
||||
|
||||
# Move a finished image out of its live-build tree into build-output/, named for the
|
||||
# thing it actually is.
|
||||
#
|
||||
# THIS IS LOad-BEARING FOR MULTI-INSTANCE BUILDS, not cosmetic. live-build always
|
||||
# writes the same filename (live-image-amd64.hybrid.iso) into the same per-host tree,
|
||||
# so building two thin clients — a living-room one and a bedroom one — would have the
|
||||
# second silently overwrite the first, leaving one ISO carrying the second room's
|
||||
# hostname and no indication the first was ever lost. Publishing under
|
||||
# <role>-<hostname>-<pairid> makes each config entry produce its own artifact.
|
||||
core_publish_image() {
|
||||
local src="$1" role="$2" instance="${3:-}"
|
||||
local out_dir="${CORE_REPO_ROOT}/${CORE_BUILD_OUTPUT_DIR}"
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
# Hostnames usually already carry the role ("thin-client-bedroom"), so appending the
|
||||
# role verbatim gives "thin-client-thin-client-bedroom". Use the hostname alone when
|
||||
# it already starts with the role.
|
||||
local name="smarthome-${role}"
|
||||
if [[ -n "$instance" ]]; then
|
||||
if [[ "$instance" == "$role"* ]]; then
|
||||
name="smarthome-${instance}"
|
||||
else
|
||||
name="smarthome-${role}-${instance}"
|
||||
fi
|
||||
fi
|
||||
local dest="${out_dir}/${name}-$(core_pair_id).${src##*.}"
|
||||
|
||||
if [[ ! -f "$src" ]]; then
|
||||
core_warn "Expected an image at ${src} but found none — nothing published."
|
||||
return 1
|
||||
fi
|
||||
mv "$src" "$dest"
|
||||
core_log "Image: ${dest}"
|
||||
echo "$dest"
|
||||
}
|
||||
|
||||
# Guard for the ISO builders: `lb build` needs root, and finding that out after the
|
||||
# config phase has already written files is worse than finding out now.
|
||||
core_require_root() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue