state-hub/scripts/sync_classification_allowed.py

92 lines
3.1 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Sync the canon classification allowed-values into the chart (CUST-WP-0067-T09).
The State Hub validates repository classification against
``the-custodian/canon/standards/repo-classification.allowed.yaml``. That file is
canon and must stay authoritative, but a container has no the-custodian
checkout, so a copy has to travel with the release.
A copy that nobody checks is a copy that silently drifts. This script owns the
copy: ``--check`` fails when it diverges from canon, so the release can refuse
to ship a stale vocabulary rather than validate against yesterday's rules.
Usage:
python scripts/sync_classification_allowed.py # write the copy
python scripts/sync_classification_allowed.py --check # verify, exit 1 on drift
"""
from __future__ import annotations
import argparse
import difflib
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CHART_COPY = (
REPO_ROOT
/ "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml"
)
CANON_CANDIDATES = (
Path("/home/worsch/the-custodian/canon/standards/repo-classification.allowed.yaml"),
Path("/home/tegwick/the-custodian/canon/standards/repo-classification.allowed.yaml"),
REPO_ROOT.parent / "the-custodian/canon/standards/repo-classification.allowed.yaml",
)
HEADER = (
"# GENERATED — do not edit.\n"
"# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml\n"
"# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).\n"
"# Canon is authoritative; this copy exists only so the container has one.\n"
)
def find_canon() -> Path:
for candidate in CANON_CANDIDATES:
if candidate.is_file():
return candidate
raise SystemExit(
"ERROR: canon allowed-values not found. Looked in:\n "
+ "\n ".join(str(c) for c in CANON_CANDIDATES)
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--check",
action="store_true",
help="Verify the chart copy matches canon; exit 1 on drift",
)
args = parser.parse_args()
canon = find_canon()
desired = HEADER + canon.read_text(encoding="utf-8")
if args.check:
if not CHART_COPY.is_file():
print(f"DRIFT: {CHART_COPY} is missing; run without --check to create it")
return 1
current = CHART_COPY.read_text(encoding="utf-8")
if current != desired:
print(f"DRIFT: chart copy differs from canon ({canon})")
sys.stdout.writelines(
difflib.unified_diff(
current.splitlines(keepends=True),
desired.splitlines(keepends=True),
fromfile="chart copy",
tofile="canon",
)
)
return 1
print(f"OK: chart copy matches canon ({canon})")
return 0
CHART_COPY.parent.mkdir(parents=True, exist_ok=True)
CHART_COPY.write_text(desired, encoding="utf-8")
print(f"Synced {canon} -> {CHART_COPY}")
return 0
if __name__ == "__main__":
raise SystemExit(main())