95 lines
3.9 KiB
Python
95 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate current repository files without changing the Git index."""
|
|
|
|
from collections import deque
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def main():
|
|
os.chdir(ROOT)
|
|
run_dir = Path(tempfile.mkdtemp(prefix="nixos-validate-", dir="/tmp"))
|
|
print(f"Logs: {run_dir}", flush=True)
|
|
|
|
def run(label, command):
|
|
log = run_dir / f"{label}.log"
|
|
print(f"{label} ...", flush=True)
|
|
with log.open("wb") as output:
|
|
result = subprocess.run(command, stdout=output, stderr=subprocess.STDOUT)
|
|
if result.returncode:
|
|
print(f"{label}: FAILED ({result.returncode}); {log}", flush=True)
|
|
with log.open(errors="replace") as output:
|
|
excerpt = "".join(deque(output, maxlen=40))
|
|
print(excerpt[-12000:])
|
|
raise SystemExit(result.returncode)
|
|
print(f"{label}: OK", flush=True)
|
|
|
|
# Git supplies selection only: contents always come from the working tree.
|
|
paths = subprocess.check_output(
|
|
["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z"]
|
|
).split(b"\0")
|
|
files = sorted({os.fsdecode(path) for path in paths if path})
|
|
files = [name for name in files if os.path.lexists(ROOT / name)]
|
|
|
|
with tempfile.TemporaryDirectory(prefix="nixos-source-", dir="/tmp") as temp:
|
|
source = Path(temp) / "source"
|
|
|
|
def snapshot():
|
|
if source.exists():
|
|
shutil.rmtree(source)
|
|
source.mkdir()
|
|
for name in files:
|
|
original = ROOT / name
|
|
destination = source / name
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
if original.is_symlink():
|
|
destination.symlink_to(os.readlink(original))
|
|
elif original.is_file():
|
|
shutil.copy2(original, destination)
|
|
else:
|
|
raise RuntimeError(f"Unsupported source entry: {name}")
|
|
|
|
snapshot()
|
|
flake = f"path:{source}"
|
|
nix = ["nix", "--quiet"]
|
|
locked = ["--no-update-lock-file"]
|
|
# The repository currently exposes only x86_64-linux outputs.
|
|
nix_files = [str(ROOT / name) for name in files if name.endswith(".nix")]
|
|
run("format", nix + ["run", *locked, f"{flake}#formatter.x86_64-linux", "--", *nix_files])
|
|
snapshot()
|
|
run("check", nix + ["flake", "check", "--no-build", *locked, flake])
|
|
hosts_file = run_dir / "hosts.json"
|
|
# Keep machine-readable output separate from Nix diagnostics.
|
|
with hosts_file.open("wb") as output, (run_dir / "hosts.log").open("wb") as errors:
|
|
result = subprocess.run(
|
|
nix + ["eval", *locked, "--json", f"{flake}#nixosConfigurations", "--apply", "builtins.attrNames"],
|
|
stdout=output, stderr=errors,
|
|
)
|
|
if result.returncode:
|
|
print(f"Host discovery failed; see {run_dir / 'hosts.log'}")
|
|
raise SystemExit(result.returncode)
|
|
available = json.loads(hosts_file.read_text())
|
|
hosts = list(dict.fromkeys(sys.argv[1:])) or available
|
|
unknown = set(hosts) - set(available)
|
|
if unknown:
|
|
raise RuntimeError(f"Unknown hosts: {', '.join(sorted(unknown))}")
|
|
for host in hosts:
|
|
run(f"build-{host}", nix + ["build", *locked, "--no-link", f"{flake}#nixosConfigurations.{host}.config.system.build.toplevel"])
|
|
if not sys.argv[1:]:
|
|
run("build-workbench", nix + ["build", *locked, "--no-link", f"{flake}#devShells.x86_64-linux.workbench"])
|
|
print(f"Validation passed: {', '.join(hosts)}. Logs: {run_dir}", flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except (OSError, ValueError, RuntimeError, subprocess.CalledProcessError) as error:
|
|
sys.exit(str(error))
|