diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/changelog.py | 23 | ||||
| -rw-r--r-- | scripts/test_changelog.py | 37 |
2 files changed, 59 insertions, 1 deletions
diff --git a/scripts/changelog.py b/scripts/changelog.py index 8db0bf97..e930fd82 100644 --- a/scripts/changelog.py +++ b/scripts/changelog.py @@ -17,6 +17,7 @@ SECTION_RE = re.compile(r"^##\s+(?:\[(?P<bracketed>[^\]]+)\]|(?P<plain>.+?))\s*$ VERSION_WITH_DATE_RE = re.compile(r"^(?P<version>.+?)\s+-\s+\d{4}-\d{2}-\d{2}$") DEFAULT_RELEASE_REPO = "ogulcancelik/herdr" DEFAULT_LATEST_JSON_PATH = Path("website/latest.json") +PROTOCOL_SOURCE_PATH = Path("src/server/protocol.rs") ASSET_TARGETS = ( "linux-x86_64", "linux-aarch64", @@ -124,12 +125,25 @@ def prepare_release(text: str, version: str, release_date: str) -> str: return rebuilt + "\n" -def build_latest_json(version: str, notes: str, assets: dict[str, str]) -> str: +def read_protocol_version(source_path: Path = PROTOCOL_SOURCE_PATH) -> int: + content = source_path.read_text(encoding="utf-8") + match = re.search(r"pub const PROTOCOL_VERSION: u32 = (\d+);", content) + if not match: + raise ChangelogError(f"could not read PROTOCOL_VERSION from {source_path}") + return int(match.group(1)) + + +def build_latest_json( + version: str, notes: str, assets: dict[str, str], protocol: int | None = None +) -> str: normalized_version = normalize_version(version) normalized_notes = notes.strip() if not normalized_notes: raise ChangelogError("release notes are empty") + if protocol is None: + protocol = read_protocol_version() + missing_targets = [target for target in ASSET_TARGETS if target not in assets] if missing_targets: raise ChangelogError(f"missing asset targets: {', '.join(missing_targets)}") @@ -139,6 +153,7 @@ def build_latest_json(version: str, notes: str, assets: dict[str, str]) -> str: return json.dumps( { "version": normalized_version, + "protocol": protocol, "notes": normalized_notes, "assets": ordered_assets, }, @@ -194,6 +209,7 @@ def manifest_from_release_payload(payload: dict[str, Any], version: str) -> dict return { "version": normalized_version, + "protocol": read_protocol_version(), "notes": notes, "assets": manifest_assets, } @@ -208,6 +224,10 @@ def canonicalize_manifest(manifest: dict[str, Any], label: str) -> dict[str, Any if not isinstance(notes, str) or not notes.strip(): raise ChangelogError(f"{label} is missing non-empty release notes") + protocol = manifest.get("protocol") + if not isinstance(protocol, int): + raise ChangelogError(f"{label} is missing an integer protocol") + assets = manifest.get("assets") if not isinstance(assets, dict): raise ChangelogError(f"{label} is missing an assets object") @@ -221,6 +241,7 @@ def canonicalize_manifest(manifest: dict[str, Any], label: str) -> dict[str, Any return { "version": normalize_version(version), + "protocol": protocol, "notes": notes.strip(), "assets": normalized_assets, } diff --git a/scripts/test_changelog.py b/scripts/test_changelog.py index 10af5c47..5778b6ac 100644 --- a/scripts/test_changelog.py +++ b/scripts/test_changelog.py @@ -13,6 +13,7 @@ from scripts.changelog import ( extract_section_body, manifest_from_release_payload, prepare_release, + read_protocol_version, ) @@ -50,6 +51,7 @@ class ChangelogScriptTests(unittest.TestCase): ) ) + self.assertEqual(manifest["protocol"], read_protocol_version()) self.assertEqual(manifest["notes"], "### Fixed\n- One") def test_build_latest_json_embeds_notes_and_release_assets(self) -> None: @@ -62,6 +64,7 @@ class ChangelogScriptTests(unittest.TestCase): ) self.assertEqual(manifest["version"], "0.1.1") + self.assertEqual(manifest["protocol"], read_protocol_version()) self.assertEqual(manifest["notes"], "### Fixed\n- Smoothed Claude flapping.") self.assertEqual( manifest["assets"], @@ -94,6 +97,7 @@ class ChangelogScriptTests(unittest.TestCase): manifest, { "version": "0.1.1", + "protocol": read_protocol_version(), "notes": "### Fixed\n- One", "assets": { "linux-x86_64": "https://example.com/linux-x86_64", @@ -136,6 +140,7 @@ class ChangelogScriptTests(unittest.TestCase): canonicalize_manifest( { "version": "0.1.1", + "protocol": read_protocol_version(), "notes": "### Fixed\n- One", "assets": { "linux-x86_64": "https://example.com/linux-x86_64", @@ -149,6 +154,7 @@ class ChangelogScriptTests(unittest.TestCase): def test_ensure_manifest_matches_expected_normalizes_whitespace(self) -> None: actual = { "version": "v0.1.1", + "protocol": read_protocol_version(), "notes": "\n### Fixed\n- One\n", "assets": { "linux-x86_64": " https://example.com/linux-x86_64 ", @@ -159,6 +165,7 @@ class ChangelogScriptTests(unittest.TestCase): } expected = { "version": "0.1.1", + "protocol": read_protocol_version(), "notes": "### Fixed\n- One", "assets": { "linux-x86_64": "https://example.com/linux-x86_64", @@ -176,6 +183,7 @@ class ChangelogScriptTests(unittest.TestCase): ensure_manifest_matches_expected( { "version": "0.1.1", + "protocol": read_protocol_version(), "notes": "### Fixed\n- Different", "assets": { "linux-x86_64": "https://example.com/linux-x86_64", @@ -186,6 +194,7 @@ class ChangelogScriptTests(unittest.TestCase): }, { "version": "0.1.1", + "protocol": read_protocol_version(), "notes": "### Fixed\n- One", "assets": { "linux-x86_64": "https://example.com/linux-x86_64", @@ -197,6 +206,34 @@ class ChangelogScriptTests(unittest.TestCase): "test manifest", ) + def test_canonicalize_manifest_requires_protocol(self) -> None: + with self.assertRaisesRegex(ChangelogError, "missing an integer protocol"): + canonicalize_manifest( + { + "version": "0.1.1", + "notes": "### Fixed\n- One", + "assets": default_release_assets("0.1.1"), + }, + "test manifest", + ) + + def test_ensure_manifest_matches_expected_rejects_different_protocol(self) -> None: + actual = { + "version": "0.1.1", + "protocol": read_protocol_version() + 1, + "notes": "### Fixed\n- One", + "assets": default_release_assets("0.1.1"), + } + expected = { + "version": "0.1.1", + "protocol": read_protocol_version(), + "notes": "### Fixed\n- One", + "assets": default_release_assets("0.1.1"), + } + + with self.assertRaisesRegex(ChangelogError, "does not match"): + ensure_manifest_matches_expected(actual, expected, "test manifest") + if __name__ == "__main__": unittest.main() |
