conventions/scripts/cli/lint_yaml.py

56 lines
1.9 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""lint:yaml — validate every convention against convention.yaml.schema.
Checks each programming_<scope>/<name>.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())