81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""check_version_surfaces.py — the LIGHTWEIGHT version-bump gate.
|
|
|
|
Nick's policy (2026-09-08): "version bumps don't need to do the full
|
|
pipeline — just a check that the code that updates the TUI version is
|
|
correct."
|
|
|
|
The TUI displays the version via VersionLabel.render() which reads
|
|
rune.__version__ AT CALL TIME (src/rune/rune_tui.py:990) — so __init__.py
|
|
IS what the footer shows. The gate proves every recorded version surface
|
|
agrees with the bump target:
|
|
|
|
src/rune/__init__.py __version__ = "X.Y.Z" (canonical)
|
|
src/rune/rune_tui.py Version: vX.Y.Z (header)
|
|
src/rune/canary_gitea_automation.py '__version__ = "X.Y.Z"'
|
|
pyproject.toml version = "X.Y.Z"
|
|
README.md vX.Y.Z
|
|
|
|
Exit 0 = all surfaces agree; exit 1 = named drifts. This replaces the full
|
|
CI pipeline on bump PRs: the version surfaces are the only code a bump
|
|
contributes, so they are the only thing worth re-checking.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
VER = r"\d+\.\d+\.\d+"
|
|
|
|
|
|
def _surface(root: Path, rel: str, pattern: str, description: str, version: str) -> str | None:
|
|
path = root / rel
|
|
if not path.exists():
|
|
return f"missing surface: {rel} ({description})"
|
|
text = path.read_text(encoding="utf-8", errors="replace")
|
|
hits = re.findall(pattern, text)
|
|
if not hits:
|
|
return f"{rel}: pattern not found (expected {description})"
|
|
if version not in hits:
|
|
return f"{rel}: found {sorted(set(hits))} — expected {version} ({description})"
|
|
return None
|
|
|
|
|
|
def check(root: Path, version: str) -> list[str]:
|
|
errs = []
|
|
checks = [
|
|
("src/rune/__init__.py", rf'__version__ = "({VER})"', "__version__ assignment"),
|
|
("src/rune/rune_tui.py", rf"Version: v({VER})", "TUI header comment"),
|
|
("src/rune/canary_gitea_automation.py", rf"'__version__ = \"({VER})\"'", "canary assertion literal"),
|
|
("pyproject.toml", rf'version = "({VER})"', "wheel metadata"),
|
|
("README.md", rf"v({VER})", "README badge"),
|
|
]
|
|
for rel, pattern, desc in checks:
|
|
err = _surface(root, rel, pattern, desc, version)
|
|
if err:
|
|
errs.append(err)
|
|
return errs
|
|
|
|
|
|
def main_with(argv: list[str]) -> int:
|
|
parser = argparse.ArgumentParser(description="Lightweight version-surface gate for bump PRs")
|
|
parser.add_argument("cmd", choices=["check"])
|
|
parser.add_argument("--root", required=True)
|
|
parser.add_argument("--version", required=True)
|
|
args = parser.parse_args(argv) # caller passes args directly (no prog prefix)
|
|
errs = check(Path(args.root), args.version)
|
|
if errs:
|
|
print("version-surface drift:", file=sys.stderr)
|
|
for e in errs:
|
|
print(f" - {e}", file=sys.stderr)
|
|
return 1
|
|
print(f"version surfaces agree: {args.version} (TUI footer will show v{args.version})")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main_with(sys.argv[1:])) # python strips script name; keep that convention
|