Public source validation / validate (push) Failing after 3m8s
89 lines
3.3 KiB
Python
89 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate starter JSON Schemas and their canonical examples.
|
|
|
|
Requires the development-only `jsonschema` package. The core `projectctl.py`
|
|
remains standard-library-only so repository state can be recovered first.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def load(path: Path) -> Any:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def main() -> int:
|
|
try:
|
|
from jsonschema import Draft202012Validator
|
|
from referencing import Registry, Resource
|
|
except ModuleNotFoundError:
|
|
print(
|
|
"ERROR: contract validation requires the development package 'jsonschema'.",
|
|
file=sys.stderr,
|
|
)
|
|
return 2
|
|
|
|
schema_paths = sorted((ROOT / "specs").glob("*.json"))
|
|
schemas: dict[str, dict[str, Any]] = {}
|
|
resources: list[tuple[str, Any]] = []
|
|
|
|
for path in schema_paths:
|
|
schema = load(path)
|
|
Draft202012Validator.check_schema(schema)
|
|
schema_id = schema.get("$id")
|
|
if not isinstance(schema_id, str) or not schema_id:
|
|
print(f"ERROR: {path.relative_to(ROOT)} has no $id", file=sys.stderr)
|
|
return 1
|
|
schemas[path.relative_to(ROOT).as_posix()] = schema
|
|
resource = Resource.from_contents(schema)
|
|
resources.append((schema_id, resource))
|
|
resources.append((path.resolve().as_uri(), resource))
|
|
|
|
registry = Registry().with_resources(resources)
|
|
checks: list[tuple[str, str]] = [
|
|
("config/metrics/catalog.example.json", "specs/metric-catalog.schema.json"),
|
|
("config/dashboards/default-overview.example.json", "specs/dashboard.schema.json"),
|
|
("config/alerts/default-rules.example.json", "specs/alert-rule-set.schema.json"),
|
|
("config/probes/probe.example.json", "specs/probe.schema.json"),
|
|
]
|
|
# The canonical private repository validates its implementation ledger. The
|
|
# curated public-source export deliberately excludes private planning state.
|
|
if (ROOT / "planning" / "task-ledger.json").exists():
|
|
checks.insert(0, ("planning/task-ledger.json", "specs/task-ledger.schema.json"))
|
|
checks.extend(
|
|
(path.relative_to(ROOT).as_posix(), "specs/simulator-scenario.schema.json")
|
|
for path in sorted((ROOT / "fixtures" / "scenarios").glob("*.json"))
|
|
)
|
|
|
|
failures = 0
|
|
for document_name, schema_name in checks:
|
|
document = load(ROOT / document_name)
|
|
schema = schemas[schema_name]
|
|
validator = Draft202012Validator(schema, registry=registry)
|
|
errors = sorted(validator.iter_errors(document), key=lambda item: list(item.path))
|
|
if not errors:
|
|
print(f"PASS: {document_name}")
|
|
continue
|
|
failures += len(errors)
|
|
print(f"FAIL: {document_name}", file=sys.stderr)
|
|
for error in errors:
|
|
location = "/".join(str(part) for part in error.path) or "<root>"
|
|
print(f"- {location}: {error.message}", file=sys.stderr)
|
|
|
|
if failures:
|
|
print(f"CONTRACT VALIDATION: FAIL ({failures} errors)", file=sys.stderr)
|
|
return 1
|
|
print(f"CONTRACT VALIDATION: PASS ({len(schema_paths)} schemas, {len(checks)} documents)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|