Files
config-atlas/tools/validate_registry.py
tegwick 72bbdad2c8
Some checks failed
validate-registry / validate (push) Has been cancelled
feat(registry): complete ATLAS-WP-0002 T02, T03, T06
T02: remove inherited capability.infotech.repo-template and template consumer
docs (statehub-register, template-validation-checklist); add
capability.infotech.config-surface-atlas and rewrite capabilities.yaml.

T03: seed 4 configuration surfaces (state-hub api-config, ops-warden
routing-catalog, reuse-surface federation-sources, ops-bridge tunnel-config)
with registry/indexes/surfaces.yaml; source-linked, no values, secret deps by
reference.

T06: add tools/validate_registry.py (schema + index gate), Makefile (make
validate), and .github/workflows/validate.yml (GitHub + Gitea Actions);
document in stack-and-commands. Verified malformed entries are rejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 23:19:18 +02:00

105 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Validate config-atlas registry entries.
Gate run by agents and CI (ATLAS-WP-0002-T06). Checks:
1. Every registry/surfaces/*.md frontmatter validates against
schemas/surface-entry.schema.json (Draft 2020-12).
2. Entries never inline a configuration value or secret value
(enforced by additionalProperties:false in the schema).
3. registry/indexes/surfaces.yaml is consistent with the entry files
(same id set, and each declared path exists with a matching id).
Exit code 0 = pass, 1 = validation failure, 2 = setup error.
Dependencies: PyYAML, jsonschema.
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
try:
import yaml
from jsonschema import Draft202012Validator
except ImportError as exc: # pragma: no cover
print(f"setup error: missing dependency ({exc}). pip install pyyaml jsonschema", file=sys.stderr)
raise SystemExit(2)
ROOT = Path(__file__).resolve().parent.parent
SCHEMA_PATH = ROOT / "schemas" / "surface-entry.schema.json"
SURFACES_DIR = ROOT / "registry" / "surfaces"
SURFACES_INDEX = ROOT / "registry" / "indexes" / "surfaces.yaml"
FRONTMATTER = re.compile(r"^---\n(.*?)\n---\n", re.S)
def load_frontmatter(path: Path) -> dict:
m = FRONTMATTER.match(path.read_text())
if not m:
raise ValueError(f"{path}: missing YAML frontmatter")
data = yaml.safe_load(m.group(1))
if not isinstance(data, dict):
raise ValueError(f"{path}: frontmatter is not a mapping")
return data
def main() -> int:
if not SCHEMA_PATH.exists():
print(f"setup error: schema not found at {SCHEMA_PATH}", file=sys.stderr)
return 2
schema = json.loads(SCHEMA_PATH.read_text())
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
errors: list[str] = []
seen_ids: dict[str, Path] = {}
entry_files = sorted(SURFACES_DIR.glob("*.md")) if SURFACES_DIR.exists() else []
for path in entry_files:
try:
fm = load_frontmatter(path)
except ValueError as exc:
errors.append(str(exc))
continue
for err in sorted(validator.iter_errors(fm), key=lambda e: list(e.path)):
loc = "/".join(str(p) for p in err.path) or "(root)"
errors.append(f"{path.name}: {loc}: {err.message}")
sid = fm.get("id")
if sid in seen_ids:
errors.append(f"{path.name}: duplicate id '{sid}' (also {seen_ids[sid].name})")
elif sid:
seen_ids[sid] = path
# filename should match the id for discoverability
if sid and path.stem != sid:
errors.append(f"{path.name}: filename does not match id '{sid}'")
# index consistency
if SURFACES_INDEX.exists():
index = yaml.safe_load(SURFACES_INDEX.read_text()) or {}
indexed = {row.get("id"): row for row in index.get("surfaces", [])}
for sid in indexed.keys() - seen_ids.keys():
errors.append(f"surfaces.yaml: id '{sid}' indexed but no entry file found")
for sid in seen_ids.keys() - indexed.keys():
errors.append(f"surfaces.yaml: entry '{sid}' present but not indexed")
for sid, row in indexed.items():
declared = row.get("path")
if declared and not (ROOT / declared).exists():
errors.append(f"surfaces.yaml: '{sid}' path '{declared}' does not exist")
elif entry_files:
errors.append("registry/indexes/surfaces.yaml missing but surface entries exist")
if errors:
print(f"FAIL: {len(errors)} problem(s) in registry validation:", file=sys.stderr)
for e in errors:
print(f" - {e}", file=sys.stderr)
return 1
print(f"OK: {len(entry_files)} surface entr{'y' if len(entry_files)==1 else 'ies'} valid; index consistent.")
return 0
if __name__ == "__main__":
raise SystemExit(main())