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:
11
flake.nix
11
flake.nix
@@ -44,16 +44,19 @@
|
||||
hardwareModuleNames = pkgs.writeText "nixos-hardware-modules.txt"
|
||||
(nixpkgs.lib.concatStringsSep "\n"
|
||||
(builtins.attrNames nixos-hardware.nixosModules));
|
||||
nixpkgsPath = nixpkgs.outPath;
|
||||
flakeUrl = "git+${gitUrl}";
|
||||
# Future updates re-resolve from the forge (original); the install
|
||||
# itself resolves from the ISO store (locked = path) — fully
|
||||
# offline, even from a dirty-tree ISO.
|
||||
originalNode = { type = "git"; url = gitUrl; };
|
||||
lockedNode = {
|
||||
type = "git";
|
||||
url = gitUrl;
|
||||
rev = self.rev or null; # null on a dirty tree → installer needs network
|
||||
type = "path";
|
||||
path = self.outPath;
|
||||
narHash = self.narHash;
|
||||
lastModified = self.lastModified or 0;
|
||||
revCount = self.revCount or 0;
|
||||
};
|
||||
rev = self.rev or null;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
@@ -27,8 +27,8 @@ def main() -> None:
|
||||
src_lock, out, locked_json, original_json = sys.argv[1:5]
|
||||
|
||||
locked = json.loads(locked_json)
|
||||
if not locked.get("rev"):
|
||||
sys.exit("compose-lock: no rev in locked metadata (ISO built from a dirty tree?)")
|
||||
if not locked.get("narHash"):
|
||||
sys.exit("compose-lock: no narHash in locked metadata")
|
||||
|
||||
with open(src_lock) as f:
|
||||
upstream = json.load(f)
|
||||
@@ -50,7 +50,8 @@ def main() -> None:
|
||||
with open(out, "w") as f:
|
||||
json.dump({"nodes": nodes, "root": "root", "version": 7}, f, indent=2)
|
||||
f.write("\n")
|
||||
print(f"compose-lock: wrote {out} (nomarchy @ {locked['rev'][:12]})")
|
||||
pin = locked.get("rev", locked["narHash"])[:12]
|
||||
print(f"compose-lock: wrote {out} (nomarchy @ {pin})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
, templateDir # templates/downstream (home.nix, theme-state.json)
|
||||
, nomarchyLock # the distro's flake.lock (for offline lock composition)
|
||||
, hardwareModuleNames # newline-separated nixos-hardware module names
|
||||
, nixpkgsPath # pinned nixpkgs source (NIX_PATH for disko's eval)
|
||||
, flakeUrl # flake-style URL written into the generated flake.nix
|
||||
, lockedNode # attrset: the flake.lock "locked" node for nomarchy
|
||||
# (rev = null when the ISO was built from a dirty tree)
|
||||
, originalNode # attrset: the flake.lock "original" node
|
||||
# (path-type: resolves from the ISO store, offline)
|
||||
, originalNode # attrset: the flake.lock "original" node (the forge,
|
||||
# so `nix flake update` later re-resolves normally)
|
||||
, rev ? null # informational: rev the ISO was built from
|
||||
}:
|
||||
|
||||
stdenvNoCC.mkDerivation {
|
||||
@@ -44,6 +47,8 @@ stdenvNoCC.mkDerivation {
|
||||
install -Dm644 compose-lock.py "$share/compose-lock.py"
|
||||
install -Dm644 ${nomarchyLock} "$share/flake.lock"
|
||||
install -Dm644 ${hardwareModuleNames} "$share/hardware-modules.txt"
|
||||
# Empty flake registry: no network lookups for indirect refs.
|
||||
echo '{"flakes":[],"version":2}' > "$share/registry.json"
|
||||
mkdir -p "$share/template"
|
||||
cp ${templateDir}/home.nix ${templateDir}/theme-state.json "$share/template/"
|
||||
|
||||
@@ -55,8 +60,9 @@ stdenvNoCC.mkDerivation {
|
||||
util-linux gptfdisk parted cryptsetup lvm2 pciutils btrfs-progs
|
||||
]} \
|
||||
--set NOMARCHY_INSTALL_SHARE "$share" \
|
||||
--set NOMARCHY_NIXPKGS ${nixpkgsPath} \
|
||||
--set NOMARCHY_FLAKE_URL ${lib.escapeShellArg flakeUrl} \
|
||||
--set NOMARCHY_REV ${lib.escapeShellArg (toString (lockedNode.rev or ""))} \
|
||||
--set NOMARCHY_REV ${lib.escapeShellArg (toString rev)} \
|
||||
--set NOMARCHY_LOCKED_JSON ${lib.escapeShellArg (builtins.toJSON lockedNode)} \
|
||||
--set NOMARCHY_ORIGINAL_JSON ${lib.escapeShellArg (builtins.toJSON originalNode)}
|
||||
|
||||
|
||||
@@ -140,8 +140,10 @@ nomarchy_detect_hw() {
|
||||
(( intelgpu )) && { echo "MODULE common-gpu-intel"; echo "DETAIL gpu: Intel"; detected=1; }
|
||||
fi
|
||||
|
||||
# Chassis
|
||||
if compgen -G "/sys/class/power_supply/BAT*" >/dev/null; then
|
||||
# Chassis (glob test, not compgen — nixpkgs' non-interactive bash
|
||||
# is built without the completion builtins)
|
||||
local bats=(/sys/class/power_supply/BAT*)
|
||||
if [[ -e "${bats[0]}" ]]; then
|
||||
echo "MODULE common-pc-laptop"
|
||||
echo "DETAIL chassis: laptop (battery present)"
|
||||
else
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user