#!/usr/bin/env python3 """validate_content.py — Validate all content YAML files against their schemas. Usage: python3 tools/validate_content.py Requires: pip install pyyaml jsonschema Exit codes: 0 — all files valid 1 — one or more validation errors """ import sys import pathlib import yaml import json try: import jsonschema except ImportError: print("ERROR: jsonschema not installed. Run: pip install pyyaml jsonschema") sys.exit(1) CONTENT_ROOT = pathlib.Path(__file__).parent.parent / "content" SCHEMA_DIR = CONTENT_ROOT / "schema" DATA_DIR = CONTENT_ROOT / "data" # Map schema file names to data subdirectories SCHEMA_DATA_MAP = { "character.yaml": DATA_DIR / "characters", "job.yaml": DATA_DIR / "jobs", "resource.yaml": DATA_DIR / "resources", "banner.yaml": DATA_DIR / "banners", "event.yaml": DATA_DIR / "events", } errors_found = 0 def load_yaml(path: pathlib.Path) -> list[dict]: """Load a YAML file, returning a list of documents (multi-doc files are split).""" with open(path) as f: docs = list(yaml.safe_load_all(f)) return [d for d in docs if d is not None] def yaml_schema_to_jsonschema(schema_doc: dict) -> dict: """ Our schema files are JSON-Schema-like YAML. Extract the JSON Schema portion. We strip the custom '$schema' key and pass the rest to jsonschema. """ s = dict(schema_doc) s.pop("$schema", None) return s def validate_directory(schema_path: pathlib.Path, data_dir: pathlib.Path) -> int: """Validate all YAML files in data_dir against schema_path. Returns error count.""" global errors_found if not data_dir.exists(): print(f" [SKIP] Directory not found: {data_dir}") return 0 schema_docs = load_yaml(schema_path) if not schema_docs: print(f" [WARN] Empty schema: {schema_path.name}") return 0 schema = yaml_schema_to_jsonschema(schema_docs[0]) local_errors = 0 yaml_files = sorted(data_dir.glob("*.yaml")) if not yaml_files: print(f" [INFO] No data files in {data_dir.name}/") return 0 for data_file in yaml_files: docs = load_yaml(data_file) for i, doc in enumerate(docs): try: # jsonschema uses JSON Schema draft-07 by default jsonschema.validate(instance=doc, schema=schema) except jsonschema.ValidationError as e: print(f" [FAIL] {data_file.name} (doc {i+1}): {e.message}") local_errors += 1 except jsonschema.SchemaError as e: print(f" [SCHEMA ERROR] {schema_path.name}: {e.message}") local_errors += 1 break if local_errors == 0: pass # silent success; only print failures if local_errors == 0: print(f" [OK] {data_dir.name}/ — {len(yaml_files)} file(s) valid") return local_errors def main() -> None: print("Xyvera content validation\n") for schema_name, data_dir in SCHEMA_DATA_MAP.items(): schema_path = SCHEMA_DIR / schema_name if not schema_path.exists(): print(f" [WARN] Schema not found: {schema_name}") continue print(f"Schema: {schema_name}") errs = validate_directory(schema_path, data_dir) errors_found += errs print() if errors_found: print(f"FAILED — {errors_found} validation error(s) found.") sys.exit(1) else: print("All content valid.") sys.exit(0) if __name__ == "__main__": main()