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
|
with itself, which is why `build-all.sh` is the default and a failure in one image
|
||||||
doesn't abandon the rest.
|
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
|
## The ISOs contain secrets
|
||||||
|
|
||||||
This is deliberate — burning everything in is what makes installation unattended, with
|
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"
|
run_build "${ktype} — ${khost}" "$builder" "$khost"
|
||||||
done
|
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
|
for row in "${AUDIO_ROWS[@]:-}"; do
|
||||||
[[ -n "$row" ]] || continue
|
[[ -n "$row" ]] || continue
|
||||||
IFS=$'\t' read -r aarch ahost _ <<< "$row"
|
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
|
case "$aarch" in
|
||||||
amd64) builder="${SCRIPT_DIR}/build-audio-endpoint-iso-amd64.sh" ;;
|
amd64)
|
||||||
arm64) builder="${SCRIPT_DIR}/build-audio-endpoint-image-arm64.sh" ;;
|
run_build "audio endpoint (amd64) — ${ahost}" \
|
||||||
*) core_warn "Unknown audio endpoint arch '${aarch}' — skipping ${ahost}"
|
"${SCRIPT_DIR}/build-audio-endpoint-iso-amd64.sh" "$ahost"
|
||||||
FAILED+=("audio/${ahost} (bad arch)"); continue ;;
|
;;
|
||||||
|
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
|
esac
|
||||||
run_build "audio endpoint (${aarch}) — ${ahost}" "$builder" "$ahost"
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,10 @@ lb build
|
||||||
|
|
||||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
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
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,9 @@ lb build
|
||||||
|
|
||||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
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
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,9 @@ lb build
|
||||||
|
|
||||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
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
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,9 @@ lb build
|
||||||
|
|
||||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
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
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,9 @@ lb build
|
||||||
|
|
||||||
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
ISO_PATH="$(find "$LIVE_BUILD_DIR" -maxdepth 1 -name '*.iso' -print -quit)"
|
||||||
ISO_PATH="${ISO_PATH:-${LIVE_BUILD_DIR}/live-image-amd64.hybrid.iso}"
|
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
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,42 @@ SMARTHOME_LLM_HOST=${CORE_LLM_HOST_IP}
|
||||||
EOF
|
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
|
# 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.
|
# config phase has already written files is worse than finding out now.
|
||||||
core_require_root() {
|
core_require_root() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue