From 1f7e26d3602c2c6f5543b25dac8a20d97a9fe3b2 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Sat, 3 Oct 2026 20:24:48 -0700 Subject: [PATCH] Complete first-boot deployment validation Provide Nomad render output while checking that current exists before NixOS activates host units. Assisted-by: gpt-6 --- tools/dashboard-deploy-test.py | 13 +++++++++++++ tools/dashboard-run.py | 2 +- tools/release.py | 2 ++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/dashboard-deploy-test.py b/tools/dashboard-deploy-test.py index 6fbebb615476c2d43763fb14583374711e9b3dec..c6f8f1aa3515e02ddd68c6e938a26c887c26cf1a 100644 --- a/tools/dashboard-deploy-test.py +++ b/tools/dashboard-deploy-test.py @@ -173,6 +173,19 @@ class DeploymentBoundary(unittest.TestCase): self.assertEqual(self.call("deploy.run", id=identity), {"lines": ["legacy", "\ufffd"], "code": 0}) executed.assert_not_called() + def test_first_activation_exposes_current_before_starting_host_units(self): + (self.root / "current").unlink() + + def execute(argv, **kwargs): + if argv[:2] == ["nixos-rebuild", "switch"]: + self.assertEqual((self.root / "current").resolve(), self.snapshot) + output = 'job "fixture" {\n' if argv[-1] == "render" else '"leader"' + return subprocess.CompletedProcess(argv, 0, stdout=output, stderr="") + + with patch.object(release.subprocess, "run", side_effect=execute): + self.assertIsNone(release.activate(self.version, initial=True)) + self.assertEqual(release.current_release(), self.version) + def test_state_reads_reject_symlinks_and_large_files(self): source = self.root / "fixture" source.write_bytes(b"x" * 200) diff --git a/tools/dashboard-run.py b/tools/dashboard-run.py index bb0ead0efafd11eb19a117b8b2240baca572b242..e604556033f5c424aceade9bb942d79a50062987 100644 --- a/tools/dashboard-run.py +++ b/tools/dashboard-run.py @@ -344,7 +344,7 @@ def handle(request): found.append((name, 0, text)) else: source, version = value["source"], value["release"] - selected = (name.endswith("-prod-" + source + ".log") or name.endswith("-prod-" + version + ".log")) or name.endswith("-rollback-" + version + ".log") + selected = name.endswith(("-prod-" + source + ".log", "-prod-" + version + ".log", "-rollback-" + version + ".log")) if not selected and not re.fullmatch(r"[0-9a-f-]{36}\.log", name): continue delta = abs(path.stat().st_mtime - value["time"]) diff --git a/tools/release.py b/tools/release.py index 26aea41c70403a438670813179292f83c5621215..8b5d2d8668aa643e143566e6fb60473834f04a8c 100644 --- a/tools/release.py +++ b/tools/release.py @@ -158,6 +158,8 @@ def activate(release, legacy=False, initial=False): print(result.stdout.strip(), flush=True) backup = result.stdout.split("backup=", 1)[1].split()[0] if old_digest != digest: + if current is None: + (ROOT / "current").symlink_to(path) subprocess.run(["nixos-rebuild", "switch", "--flake", f"path:{path}#{configuration}"], check=True) next_link = ROOT / ("next-" + release) next_link.unlink(missing_ok=True) -- 2.54.0