Files
Nomarchy/pkgs/nomarchy-install/compose-lock.py
Bernardo Magri 479193b10b 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>
2026-06-10 16:48:33 +01:00

59 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Compose a flake.lock for the freshly installed machine — offline.
The generated downstream flake has exactly one input, `nomarchy`. A normal
`nix flake lock` would fetch it from its forge; on an offline install we
already KNOW the answer: the live ISO was built from a specific nomarchy
rev whose source (and all transitive inputs, via the ISO's pinned store
paths) is on hand. So we write the lock nix would have written:
root ─▶ nomarchy (locked to the baked rev+narHash)
└─▶ every node from nomarchy's own flake.lock, verbatim
Nix resolves locked inputs by narHash against the store before touching
the network, so evaluation on the target works without connectivity.
Usage:
compose-lock.py <nomarchy-flake.lock> <out> <locked-json> <original-json>
where <locked-json>/<original-json> are the flake-lock node fields for the
nomarchy input (any input type — github, git, …), baked at package build.
"""
import json
import sys
def main() -> None:
src_lock, out, locked_json, original_json = sys.argv[1:5]
locked = json.loads(locked_json)
if not locked.get("narHash"):
sys.exit("compose-lock: no narHash in locked metadata")
with open(src_lock) as f:
upstream = json.load(f)
nodes = dict(upstream["nodes"])
# nomarchy's root node describes ITS inputs; that mapping becomes the
# input set of the new `nomarchy` node.
nomarchy_inputs = nodes.pop("root")["inputs"]
if "nomarchy" in nodes:
sys.exit("compose-lock: upstream lock already has a 'nomarchy' node")
nodes["nomarchy"] = {
"inputs": nomarchy_inputs,
"locked": locked,
"original": json.loads(original_json),
}
nodes["root"] = {"inputs": {"nomarchy": "nomarchy"}}
with open(out, "w") as f:
json.dump({"nodes": nodes, "root": "root", "version": 7}, f, indent=2)
f.write("\n")
pin = locked.get("rev", locked["narHash"])[:12]
print(f"compose-lock: wrote {out} (nomarchy @ {pin})")
if __name__ == "__main__":
main()