-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrebuild
More file actions
executable file
·117 lines (96 loc) · 4.06 KB
/
Copy pathrebuild
File metadata and controls
executable file
·117 lines (96 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python3
"""Bootstrap rebuild script.
Use when nix-update is not yet installed or broken. This reuses the actual
nix-update build/activation engine (imported directly from its library file,
`nix_update_lib.py`) so the logic is never duplicated. The library is kept to
the Python standard library plus `nix`/`git`, which is exactly what a bootstrap
can rely on -- the full `nix-update` CLI assumes fancier dependencies
(inotify-tools for its waybar watcher, network access for its upstream check)
that may be unavailable here. The only behavioural difference from
`nix-update` is that the bootstrap builds from the committed, known-good
flake.lock under version control: tracked inputs are NOT updated, only the
machine-local overlay is relocked.
Usage:
./rebuild hm # build + home-manager activation
./rebuild nixos # build + nixos boot activation
./rebuild both # both
"""
from __future__ import annotations
import importlib.util
import os
import re
import subprocess
import sys
from pathlib import Path
FLAKE_DIR = Path(__file__).resolve().parent
NIX_UPDATE_LIB = FLAKE_DIR / "pkgs/by-name/ni/nix-update/nix_update_lib.py"
def check_nix_version() -> bool:
try:
out = subprocess.run(
["nix", "--version"], check=False, text=True, stdout=subprocess.PIPE
).stdout
except FileNotFoundError:
return False
m = re.search(r"(\d+)\.(\d+)", out or "")
if not m:
return False
major, minor = int(m.group(1)), int(m.group(2))
if (major, minor) < (2, 18):
print(f"ERROR: Nix version {major}.{minor} is too old (requires >= 2.18). Please upgrade Nix first.", file=sys.stderr)
raise SystemExit(1)
return (major, minor) >= (2, 32)
def ensure_nix_version() -> None:
"""Make a recent enough Nix available on PATH before doing anything else.
nix-update normally relies on its package wrapper for this; the bootstrap
has to do it itself since it may run with the system's (older) Nix.
"""
if check_nix_version():
return
print("==> Nix version < 2.32 detected. Building Nix from flake...")
proc = subprocess.run(
["nix", "build", f"{FLAKE_DIR}#nix^out", "--no-link", "--print-out-paths"],
check=False,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if proc.returncode != 0 or not proc.stdout.strip():
print(f"ERROR: Could not build Nix from flake\n{proc.stderr.strip()}", file=sys.stderr)
raise SystemExit(1)
nix_path = proc.stdout.strip().splitlines()[-1]
os.environ["PATH"] = f"{nix_path}/bin:{os.environ.get('PATH', '')}"
print(f"==> Using Nix from: {nix_path}/bin")
def load_nix_update():
spec = importlib.util.spec_from_file_location("nix_update_lib", NIX_UPDATE_LIB)
if spec is None or spec.loader is None:
raise SystemExit(f"ERROR: cannot load nix-update library from {NIX_UPDATE_LIB}")
module = importlib.util.module_from_spec(spec)
# Register before exec so dataclasses can resolve module-level annotations
# (the source uses `from __future__ import annotations`).
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def main(argv: list[str]) -> int:
if len(argv) != 1 or argv[0] not in {"hm", "os", "both"}:
print(f"Usage: {Path(sys.argv[0]).name} <hm|os|both>", file=sys.stderr)
return 1
target = argv[0]
ensure_nix_version()
nix_update = load_nix_update()
# Build from the committed lock: do not update tracked inputs.
app = nix_update.App(FLAKE_DIR, update_refs=False)
if app.is_tree_dirty():
print("WARNING: Flake directory is dirty - building only (will NOT be activated).", file=sys.stderr)
rc = app.cmd_build(target)
if rc != 0:
return rc
print("==> Dirty build complete. Commit and use nix-update (or a clean ./rebuild) to activate.")
return 0
# Clean tree: build then activate the prebuilt result (nixos -> boot).
return app.cmd_apply(target, do_rebuild=True, do_switch=False)
if __name__ == "__main__":
try:
raise SystemExit(main(sys.argv[1:]))
except Exception as exc: # noqa: BLE001 - bootstrap: surface any failure cleanly
print(str(exc), file=sys.stderr)
raise SystemExit(1)