fix(install): survive the live ISO offline and its non-interactive bash

Found by running the unattended install in an offline QEMU VM:
- compgen is missing from nixpkgs' non-interactive bash (the package
  shebang) — replaced with plain glob tests in hardware-db and prewipe
- disko's eval resolves <nixpkgs> via NIX_PATH (a dead channel on the
  ISO) and tried channels.nixos.org — export NIX_PATH to the pinned
  nixpkgs source and point the flake registry at an empty baked file
- lock nomarchy by path (the source the ISO carries) instead of
  git+https: git inputs clone even when narHash is known, which kills
  offline installs; `original` keeps the forge URL so a later
  `nix flake update` re-resolves normally. Works from dirty-tree ISOs too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-10 16:48:33 +01:00
parent 71614fa6ca
commit 479193b10b
5 changed files with 42 additions and 25 deletions

View File

@@ -62,6 +62,12 @@ command -v nixos-install >/dev/null \
grep -q nomarchy-live /etc/hostname 2>/dev/null \
|| warn "This doesn't look like the Nomarchy live ISO; proceeding anyway."
# Offline-proof nix: disko's eval resolves <nixpkgs> (a dead channel on
# the live ISO) and flake commands may consult the global registry —
# point both at what the ISO already carries.
export NIX_PATH="nixpkgs=$NOMARCHY_NIXPKGS"
export NIX_CONFIG="flake-registry = $SHARE/registry.json"
# ─── Disk selection ─────────────────────────────────────────────────────
section "Target disk"
@@ -201,7 +207,7 @@ gum style --border normal --padding "0 2" \
"Timezone: $TIMEZONE" \
"Hardware: ${HW_PROFILES[*]:-none}" \
"Snapshots: snapper timeline on /" \
"Offline: $([[ -n "${NOMARCHY_REV:-}" ]] && echo "yes (pinned ${NOMARCHY_REV:0:12})" || echo "no (will fetch nomarchy over the network)")"
"Source: nomarchy ${NOMARCHY_REV:0:12}${NOMARCHY_REV:+ }$([[ -z "${NOMARCHY_REV:-}" ]] && echo "(dirty tree) ")— pinned into the ISO, no network needed"
if [[ "$UNATTENDED" != "1" ]]; then
typed=$(gum input --placeholder "type the disk name ($(basename "$TARGET_DISK")) to confirm the wipe")
@@ -230,11 +236,11 @@ prewipe() {
cryptsetup close "$name"
done < <(dmsetup ls --target crypt 2>/dev/null)
fi
if compgen -G "${drive}?*" >/dev/null; then
for part in "${drive}"?*; do
[[ -b "$part" ]] && wipefs -af "$part" >/dev/null
done
fi
for part in "${drive}"?*; do
# unmatched glob stays literal; -b filters it out
[[ -b "$part" ]] || continue
wipefs -af "$part" >/dev/null
done
wipefs -af "$drive" >/dev/null
sgdisk --zap-all "$drive" >/dev/null
# 16 MiB covers LUKS2 headers and the first BTRFS superblock —
@@ -363,13 +369,12 @@ $AUTOLOGIN_CONFIG$RESUME_CONFIG
}
EOF
# The flake.lock: offline when the ISO knows what it was built from,
# otherwise resolve over the network.
if [[ -n "${NOMARCHY_REV:-}" ]]; then
python3 "$SHARE/compose-lock.py" "$SHARE/flake.lock" "$FLAKE_DIR/flake.lock" \
"$NOMARCHY_LOCKED_JSON" "$NOMARCHY_ORIGINAL_JSON"
else
warn "ISO built from a dirty tree — resolving nomarchy from its forge (needs network)."
# The flake.lock: composed offline — nomarchy is path-locked to the very
# source the ISO carries (original stays the forge URL, so a later
# `nix flake update` on the installed machine re-resolves normally).
if ! python3 "$SHARE/compose-lock.py" "$SHARE/flake.lock" "$FLAKE_DIR/flake.lock" \
"$NOMARCHY_LOCKED_JSON" "$NOMARCHY_ORIGINAL_JSON"; then
warn "Offline lock composition failed — resolving over the network."
(cd "$FLAKE_DIR" && nix --extra-experimental-features "nix-command flakes" flake lock)
fi