Files
pulse/scripts/release_control/release_continuity_test.py
T
pulse-triage[bot] ef6e784c2e docs(release): clarify combined continuity incident recovery
Fresh stable readback still shows mutable v6.4.1 alongside orphan v6.4.2. Explain forward supersession without deleting historical tags or treating frontier success as release admission. Cover that distinction with a focused recovery regression; publication authority and all existing gates remain unchanged.

Change-source: pulse-maintainer
2026-09-05 20:12:48 +01:00

448 lines
17 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = ROOT / "scripts" / "release_control" / "release_continuity.py"
SOURCE_SHA = "a" * 40
DIGEST = "sha256:" + "b" * 64
def encoded(payload: object) -> bytes:
return json.dumps(payload).encode("utf-8")
def activation_asset(payload: object) -> dict[str, object]:
content = encoded(payload)
return {
"name": "release-activation.json",
"state": "uploaded",
"size": len(content),
"digest": "sha256:" + hashlib.sha256(content).hexdigest(),
}
def valid_release(activation: object | None = None) -> dict[str, object]:
if activation is None:
activation = valid_activation()
return {
"id": 12345,
"tag_name": "v6.4.2",
"target_commitish": SOURCE_SHA,
"draft": False,
"prerelease": False,
"immutable": True,
"published_at": "2026-08-31T17:00:00Z",
"assets": [activation_asset(activation)],
}
def valid_activation() -> dict[str, object]:
return {
"schema_version": 1,
"tag": "v6.4.2",
"release_id": "12345",
"target_commitish": SOURCE_SHA,
"source_release_run_id": "1001",
"convergence_run_id": "1002",
"r2_prefix": "releases/v6.4.2",
"server_image_digest": DIGEST,
"control_plane_image_digest": DIGEST,
"helm_chart_digest": DIGEST,
}
class ReleaseContinuityTest(unittest.TestCase):
def run_command(
self,
command: str,
release: object,
activation: object | None = None,
stable_refs: object | None = None,
releases: object | None = None,
registries: list[object] | None = None,
) -> tuple[subprocess.CompletedProcess[str], dict[str, object], str]:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
release_path = root / "release.json"
release_path.write_text(json.dumps(release), encoding="utf-8")
diagnostic = root / "diagnostic.json"
output = root / "github-output"
output.touch()
args = [
sys.executable,
str(SCRIPT),
command,
"--release-json",
str(release_path),
"--diagnostic",
str(diagnostic),
]
if command != "frontier":
args.extend(["--github-output", str(output)])
if command == "activation":
activation_path = root / "activation.json"
activation_path.write_bytes(encoded(activation))
args.extend(["--activation-json", str(activation_path)])
if command == "frontier":
stable_refs_path = root / "stable-refs.json"
releases_path = root / "releases.json"
stable_refs_path.write_text(json.dumps(stable_refs), encoding="utf-8")
releases_path.write_text(json.dumps(releases), encoding="utf-8")
args.extend(
[
"--stable-refs-json",
str(stable_refs_path),
"--releases-json",
str(releases_path),
]
)
for index, registry in enumerate(
registries or [{"name": "registry.example/pulse", "tags": []}]
):
registry_path = root / f"registry-{index}.json"
registry_path.write_text(json.dumps(registry), encoding="utf-8")
args.extend(["--registry-tags-json", str(registry_path)])
result = subprocess.run(
args,
cwd=ROOT,
text=True,
capture_output=True,
check=False,
)
return (
result,
json.loads(diagnostic.read_text(encoding="utf-8")),
output.read_text(encoding="utf-8"),
)
def test_accepts_exact_immutable_stable_release(self) -> None:
result, diagnostic, output = self.run_command("release", valid_release())
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(diagnostic["status"], "success")
self.assertEqual(diagnostic["violations"], [])
self.assertIn("referenceable=true\n", output)
self.assertIn("tag=v6.4.2\n", output)
self.assertIn("release_id=12345\n", output)
self.assertIn(f"source_sha={SOURCE_SHA}\n", output)
def test_mutable_release_fails_with_one_actionable_reason(self) -> None:
release = valid_release()
release["immutable"] = False
result, diagnostic, output = self.run_command("release", release)
self.assertEqual(result.returncode, 1)
self.assertIn("referenceable=true\n", output)
self.assertIn("tag=v6.4.2\n", output)
self.assertIn("release_id=12345\n", output)
self.assertIn(f"source_sha={SOURCE_SHA}\n", output)
self.assertEqual(diagnostic["status"], "failure")
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["release_mutable"],
)
self.assertIn("immutable=false", result.stderr)
self.assertIn("never repair the packet in place", result.stderr)
def test_reports_every_release_identity_violation(self) -> None:
result, diagnostic, output = self.run_command(
"release",
{
"id": True,
"tag_name": "v6.4.2-rc.1\nforged",
"target_commitish": "main",
"draft": True,
"prerelease": True,
"immutable": None,
"published_at": "",
},
)
self.assertEqual(result.returncode, 1)
self.assertEqual(output, "")
self.assertEqual(
{item["code"] for item in diagnostic["violations"]},
{
"release_id_invalid",
"stable_tag_invalid",
"source_identity_invalid",
"release_is_draft",
"release_is_prerelease",
"release_mutable",
"publication_time_invalid",
},
)
self.assertNotIn("forged", result.stderr)
def test_accepts_exact_activation_binding_and_emits_digests(self) -> None:
activation = valid_activation()
result, diagnostic, output = self.run_command(
"activation", valid_release(activation), activation
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(diagnostic["status"], "success")
self.assertRegex(output, r"activation_sha256=[0-9a-f]{64}\n")
self.assertIn(f"server_image_digest={DIGEST}\n", output)
self.assertIn(f"control_plane_image_digest={DIGEST}\n", output)
self.assertIn(f"helm_chart_digest={DIGEST}\n", output)
def test_activation_mismatches_are_classified_without_outputs(self) -> None:
activation = valid_activation()
activation.update(
{
"schema_version": True,
"tag": "v6.4.1",
"release_id": 12345,
"target_commitish": "c" * 40,
"source_release_run_id": "",
"convergence_run_id": 1002,
"r2_prefix": "",
"server_image_digest": "latest",
"control_plane_image_digest": None,
"helm_chart_digest": "sha256:ABC",
}
)
result, diagnostic, output = self.run_command(
"activation", valid_release(activation), activation
)
self.assertEqual(result.returncode, 1)
self.assertEqual(output, "")
self.assertEqual(
{item["code"] for item in diagnostic["violations"]},
{
"activation_schema_invalid",
"activation_tag_mismatch",
"activation_release_mismatch",
"activation_source_mismatch",
"source_run_invalid",
"convergence_run_invalid",
"delivery_prefix_invalid",
"server_digest_invalid",
"control_plane_digest_invalid",
"helm_digest_invalid",
},
)
self.assertTrue(
all(
"publish a corrected replacement" in item["action"]
for item in diagnostic["violations"]
)
)
def test_mutable_release_does_not_hide_activation_damage(self) -> None:
activation = valid_activation()
del activation["server_image_digest"]
del activation["control_plane_image_digest"]
del activation["helm_chart_digest"]
release = valid_release(activation)
release["immutable"] = False
result, diagnostic, output = self.run_command(
"activation", release, activation
)
self.assertEqual(result.returncode, 1)
self.assertEqual(output, "")
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
[
"server_digest_invalid",
"control_plane_digest_invalid",
"helm_digest_invalid",
],
)
def test_activation_requires_one_digest_bound_release_asset(self) -> None:
activation = valid_activation()
release = valid_release(activation)
release["assets"] = []
result, diagnostic, output = self.run_command(
"activation", release, activation
)
self.assertEqual(result.returncode, 1)
self.assertEqual(output, "")
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["activation_asset_invalid"],
)
def test_activation_rejects_download_that_differs_from_release_metadata(self) -> None:
expected = valid_activation()
downloaded = valid_activation()
downloaded["r2_prefix"] = "releases/v6.4.2-corrupted"
result, diagnostic, output = self.run_command(
"activation", valid_release(expected), downloaded
)
self.assertEqual(result.returncode, 1)
self.assertEqual(output, "")
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
[
"activation_asset_size_mismatch",
"activation_asset_digest_mismatch",
],
)
def test_frontier_accepts_tags_at_or_behind_advertised_release(self) -> None:
release = valid_release()
result, diagnostic, output = self.run_command(
"frontier",
release,
stable_refs=[
{"ref": "refs/tags/v6.4.1"},
{"ref": "refs/tags/v6.4.2"},
{"ref": "refs/tags/v6.4.3-rc.1"},
],
releases=[release],
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(diagnostic["status"], "success")
self.assertEqual(diagnostic["identity"]["newer_stable_tags"], "")
self.assertEqual(output, "")
def test_replacement_supersedes_orphan_without_granting_release_trust(self) -> None:
# Keep the same historical inventories across recovery. Deleting the
# orphan is neither necessary nor a substitute for release admission.
old = {**valid_release(), "tag_name": "v6.4.1", "immutable": False}
replacement = {**valid_release(), "tag_name": "v6.4.3"}
refs = [{"ref": "refs/tags/v6.4.2"}]
registries = [{"name": "ghcr.io/rcourtman/pulse", "tags": ["6.4.2"]}]
result, diagnostic, _ = self.run_command(
"frontier", old, stable_refs=refs, releases=[old],
registries=registries,
)
self.assertEqual(result.returncode, 1)
self.assertEqual(
{item["code"] for item in diagnostic["violations"]},
{"stable_tag_without_release", "registry_stable_tag_beyond_latest"},
)
for immutable in (False, True):
with self.subTest(immutable=immutable):
candidate = {**replacement, "immutable": immutable}
result, diagnostic, _ = self.run_command(
"frontier", candidate, stable_refs=refs,
releases=[old, candidate], registries=registries,
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(diagnostic["identity"]["orphaned_stable_tags"], "")
result, diagnostic, _ = self.run_command("release", candidate)
self.assertEqual(result.returncode, 0 if immutable else 1)
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
[] if immutable else ["release_mutable"],
)
def test_frontier_rejects_newer_stable_tag_without_release_packet(self) -> None:
release = valid_release()
result, diagnostic, _ = self.run_command(
"frontier",
release,
stable_refs=[[{"ref": "refs/tags/v6.4.2"}, {"ref": "refs/tags/v6.4.3"}]],
releases=[[release]],
)
self.assertEqual(result.returncode, 1)
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["stable_tag_without_release"],
)
self.assertEqual(diagnostic["identity"]["orphaned_stable_tags"], "v6.4.3")
self.assertIn("orphaned version", result.stderr)
def test_frontier_rejects_public_registry_version_beyond_latest(self) -> None:
release = valid_release()
result, diagnostic, _ = self.run_command(
"frontier",
release,
stable_refs=[{"ref": "refs/tags/v6.4.2"}],
releases=[release],
registries=[
{
"name": "docker.io/rcourtman/pulse",
"tags": ["latest", "6.4", "6.4.2", "v6.4.3"],
},
{
"name": "ghcr.io/rcourtman/pulse",
"tags": ["v6.4.3", "6.4.3-rc.1"],
},
],
)
self.assertEqual(result.returncode, 1)
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["registry_stable_tag_beyond_latest"],
)
self.assertEqual(
diagnostic["identity"]["registry_stable_tags_beyond_latest"],
"v6.4.3",
)
self.assertIn("docker.io/rcourtman/pulse", diagnostic["violations"][0]["actual"])
self.assertIn("ghcr.io/rcourtman/pulse", diagnostic["violations"][0]["actual"])
def test_frontier_rejects_published_stable_release_beyond_latest(self) -> None:
release = valid_release()
newer = {**release, "id": 67890, "tag_name": "v6.4.3"}
result, diagnostic, _ = self.run_command(
"frontier",
release,
stable_refs=[{"ref": "refs/tags/v6.4.3"}],
releases=[release, newer],
)
self.assertEqual(result.returncode, 1)
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["newer_stable_release_not_advertised"],
)
def test_frontier_checks_published_releases_without_a_matching_ref(self) -> None:
release = valid_release()
newer = {**release, "id": 67890, "tag_name": "v6.4.3"}
result, diagnostic, _ = self.run_command(
"frontier",
release,
stable_refs=[{"ref": "refs/tags/v6.4.2"}],
releases=[release, newer],
)
self.assertEqual(result.returncode, 1)
self.assertEqual(diagnostic["identity"]["newer_stable_tags"], "")
self.assertEqual(
diagnostic["identity"]["unadvertised_published_stable_tags"],
"v6.4.3",
)
self.assertEqual(
[item["code"] for item in diagnostic["violations"]],
["newer_stable_release_not_advertised"],
)
def test_frontier_allows_in_progress_draft_beyond_latest(self) -> None:
release = valid_release()
draft = {
**release,
"id": 67890,
"tag_name": "v6.4.3",
"draft": True,
"published_at": None,
}
result, diagnostic, _ = self.run_command(
"frontier",
release,
stable_refs=[{"ref": "refs/tags/v6.4.3"}],
releases=[release, draft],
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(diagnostic["status"], "success")
if __name__ == "__main__":
unittest.main()