#!/usr/bin/env bash # Bake the golden NixOS qcow2: boot the minimal ISO headless under qemu, drive a # scripted nixos-install over the serial console, power off. Output is a golden # disk image; Kiln (or boot-overlay.sh) boots throwaway overlays from it. # # Usage: build-golden.sh # Tunables via env: DISK_GB (20), MEM_MB (4096), SMP (2), INSTALL_TIMEOUT (2400) set -euo pipefail ISO="${1:?usage: build-golden.sh }" NINA="${2:?need nina linux binary}" OUT="${3:?need output qcow2 path}" HERE="$(cd "$(dirname "$0")" && pwd)" DISK_GB="${DISK_GB:-20}" MEM_MB="${MEM_MB:-4096}" SMP="${SMP:-2}" INSTALL_TIMEOUT="${INSTALL_TIMEOUT:-2400}" for f in "$ISO" "$NINA"; do [ -f "$f" ] || { echo "missing: $f" >&2; exit 1; }; done work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT # Seed media: our config + install script + nina, as an ISO the installer mounts. seed="$work/seed" mkdir -p "$seed" cp "$HERE/configuration.nix" "$seed/configuration.nix" cp "$HERE/install.sh" "$seed/install.sh" cp "$NINA" "$seed/nina" SEED_ISO="$work/seed.iso" hdiutil makehybrid -quiet -iso -joliet -default-volume-name NINASEED -o "$SEED_ISO" "$seed" # Fresh blank disk for the install target. qemu-img create -f qcow2 "$OUT" "${DISK_GB}G" >/dev/null echo "golden target: $OUT (${DISK_GB}G) ยท seed: $SEED_ISO" expect -c ' set timeout 600 set iso [lindex $argv 0] set seed_iso [lindex $argv 1] set out [lindex $argv 2] set mem [lindex $argv 3] set smp [lindex $argv 4] set install_timeout [lindex $argv 5] spawn qemu-system-x86_64 \ -machine q35 -accel tcg -cpu max -smp $smp -m $mem \ -drive file=$out,if=virtio,format=qcow2 \ -drive file=$iso,media=cdrom,readonly=on \ -drive file=$seed_iso,media=cdrom,readonly=on \ -boot d \ -netdev user,id=n0 -device virtio-net-pci,netdev=n0 \ -nographic # Reach an installer root shell (autologin on the ISO serial console). expect { timeout { puts "\n!! no shell prompt from installer"; exit 2 } -re {\[root@nixos[^\]]*\]#} {} -re {nixos@nixos} { send "sudo -i\r"; exp_continue } -re {login:} { send "root\r"; exp_continue } } send "\r" expect -re {\[root@nixos[^\]]*\]#} # Mount seed media and run the installer script. send "mkdir -p /mnt-seed && mount /dev/sr1 /mnt-seed && echo NINA_SEED_OK\r" expect { timeout { puts "\n!! seed mount failed"; exit 3 } "NINA_SEED_OK" {} } send "bash /mnt-seed/install.sh\r" set timeout $install_timeout expect { timeout { puts "\n!! install timed out"; exit 4 } "NINA_INSTALL_DONE" {} "NINA_INSTALL_START" { exp_continue } } # qemu exits when the guest powers off. set timeout 120 expect eof ' "$ISO" "$SEED_ISO" "$OUT" "$MEM_MB" "$SMP" "$INSTALL_TIMEOUT" echo "=== golden image built: $OUT ===" qemu-img info "$OUT" | sed 's/^/ /'