authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-03 20:06:24-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-03 20:06:49-07:00
log8ae4895a8ff02e7ee0c7e506cc0488f2b353d8cf
treefbae407f58f166a629b421689806698c749b8841
parent76765cb2aed4394bf710c2bb4d026ec80115f593
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

Make the first main deployment available to NixOS host units

Assisted-by: gpt-6

3 files changed, 15 insertions(+), 1 deletions(-)

tools/dashboard-deploy-test.py+12
...@@ -173,6 +173,18 @@ class DeploymentBoundary(unittest.TestCase):...@@ -173,6 +173,18 @@ class DeploymentBoundary(unittest.TestCase):
173 self.assertEqual(self.call("deploy.run", id=identity), {"lines": ["legacy", "\ufffd"], "code": 0})173 self.assertEqual(self.call("deploy.run", id=identity), {"lines": ["legacy", "\ufffd"], "code": 0})
174 executed.assert_not_called()174 executed.assert_not_called()
175175
176 def test_first_activation_exposes_current_before_starting_host_units(self):
177 (self.root / "current").unlink()
178
179 def execute(argv, **kwargs):
180 if argv[:2] == ["nixos-rebuild", "switch"]:
181 self.assertEqual((self.root / "current").resolve(), self.snapshot)
182 return subprocess.CompletedProcess(argv, 0, stdout='"leader"', stderr="")
183
184 with patch.object(release.subprocess, "run", side_effect=execute):
185 self.assertIsNone(release.activate(self.version, initial=True))
186 self.assertEqual(release.current_release(), self.version)
187
176 def test_state_reads_reject_symlinks_and_large_files(self):188 def test_state_reads_reject_symlinks_and_large_files(self):
177 source = self.root / "fixture"189 source = self.root / "fixture"
178 source.write_bytes(b"x" * 200)190 source.write_bytes(b"x" * 200)
tools/dashboard-run.py+1-1
...@@ -344,7 +344,7 @@ def handle(request):...@@ -344,7 +344,7 @@ def handle(request):
344 found.append((name, 0, text))344 found.append((name, 0, text))
345 else:345 else:
346 source, version = value["source"], value["release"]346 source, version = value["source"], value["release"]
347 selected = (name.endswith("-prod-" + source + ".log") or name.endswith("-prod-" + version + ".log")) or name.endswith("-rollback-" + version + ".log")347 selected = name.endswith(("-prod-" + source + ".log", "-prod-" + version + ".log", "-rollback-" + version + ".log"))
348 if not selected and not re.fullmatch(r"[0-9a-f-]{36}\.log", name):348 if not selected and not re.fullmatch(r"[0-9a-f-]{36}\.log", name):
349 continue349 continue
350 delta = abs(path.stat().st_mtime - value["time"])350 delta = abs(path.stat().st_mtime - value["time"])
tools/release.py+2
...@@ -158,6 +158,8 @@ def activate(release, legacy=False, initial=False):...@@ -158,6 +158,8 @@ def activate(release, legacy=False, initial=False):
158 print(result.stdout.strip(), flush=True)158 print(result.stdout.strip(), flush=True)
159 backup = result.stdout.split("backup=", 1)[1].split()[0]159 backup = result.stdout.split("backup=", 1)[1].split()[0]
160 if old_digest != digest:160 if old_digest != digest:
161 if current is None:
162 (ROOT / "current").symlink_to(path)
161 subprocess.run(["nixos-rebuild", "switch", "--flake", f"path:{path}#{configuration}"], check=True)163 subprocess.run(["nixos-rebuild", "switch", "--flake", f"path:{path}#{configuration}"], check=True)
162 next_link = ROOT / ("next-" + release)164 next_link = ROOT / ("next-" + release)
163 next_link.unlink(missing_ok=True)165 next_link.unlink(missing_ok=True)