#!/usr/bin/env python3 """lint:yaml — validate every convention against convention.yaml.schema. Checks each programming_/.yaml: parses, validates against the meta-schema, and enforces name==filename and scope==directory. Run from the @conventions repo root (the `run` wrapper cd's there). """ import glob import sys try: import yaml import jsonschema except ImportError as e: sys.exit(f"lint:yaml needs pyyaml + jsonschema ({e}). pip install pyyaml jsonschema") def main() -> int: try: schema = yaml.safe_load(open("convention.yaml.schema")) except FileNotFoundError: return _fail("convention.yaml.schema not found — run from the @conventions root") files = sorted(glob.glob("programming_*/*.yaml")) if not files: return _fail("no convention files found under programming_*/") failed = 0 for f in files: scope_dir = f.split("/")[0].removeprefix("programming_") stem = f.split("/")[-1].removesuffix(".yaml") try: doc = yaml.safe_load(open(f)) jsonschema.validate(doc, schema) if doc.get("name") != stem: failed += 1; print(f"FAIL {f}: name '{doc.get('name')}' != filename '{stem}'"); continue if doc.get("scope") != scope_dir: failed += 1; print(f"FAIL {f}: scope '{doc.get('scope')}' != dir '{scope_dir}'"); continue print(f"ok {f} ({doc['name']} v{doc.get('version', '?')}, {doc.get('status')})") except yaml.YAMLError as e: failed += 1; print(f"FAIL {f}: YAML parse: {e}") except jsonschema.ValidationError as e: failed += 1; print(f"FAIL {f}: {e.message}") print(f"\n{len(files) - failed}/{len(files)} valid") return 1 if failed else 0 def _fail(msg: str) -> int: print(f"FAIL: {msg}") return 1 if __name__ == "__main__": sys.exit(main())