mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 13:59:40 +00:00
- load_schema/validate use jsonschema Draft7Validator - enrich() fills version (git describe) + last_updated (git log -1 %cI) - 8 pytest cases covering valid/invalid/extra fields, with-git/no-git, and preservation of existing version Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
"""
|
|
SecuBox-Deb :: MetaBlogizer site.json schema validator + enricher
|
|
CyberMind — https://cybermind.fr
|
|
Author: Gérald Kerma <gandalf@gk2.net>
|
|
License: CMSD-1.0
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import jsonschema
|
|
|
|
SCHEMA_PATH = Path(__file__).parent.parent / "schema" / "site.json.schema.json"
|
|
|
|
|
|
def load_schema() -> dict:
|
|
"""Read the JSON Schema once."""
|
|
with SCHEMA_PATH.open() as f:
|
|
return json.load(f)
|
|
|
|
|
|
def validate(doc: dict) -> tuple[bool, list[str]]:
|
|
"""Validate doc against the schema.
|
|
|
|
Returns (ok, errors). Permissive: doc may have extra fields (the schema
|
|
has additionalProperties: true).
|
|
"""
|
|
schema = load_schema()
|
|
validator = jsonschema.Draft7Validator(schema)
|
|
errors = [
|
|
f"{'.'.join(map(str, e.path)) or '<root>'}: {e.message}"
|
|
for e in validator.iter_errors(doc)
|
|
]
|
|
return (not errors, errors)
|
|
|
|
|
|
def _git(*args: str, cwd: Path) -> str | None:
|
|
"""Run git with a 5-second timeout; return stripped stdout or None."""
|
|
try:
|
|
out = subprocess.run(
|
|
["git", "-C", str(cwd), *args],
|
|
capture_output=True, text=True, timeout=5,
|
|
)
|
|
if out.returncode == 0:
|
|
return out.stdout.strip() or None
|
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
pass
|
|
return None
|
|
|
|
|
|
def enrich(doc: dict, app_dir: Path) -> dict:
|
|
"""Fill derived fields (version, last_updated) if missing.
|
|
|
|
- version -> git describe --tags --exact-match (else --tags --always)
|
|
- last_updated -> git log -1 --format=%cI (RFC 3339)
|
|
|
|
Does nothing if app_dir has no .git/.
|
|
"""
|
|
out: dict[str, Any] = dict(doc)
|
|
if not (app_dir / ".git").exists():
|
|
return out
|
|
if not out.get("version"):
|
|
out["version"] = (
|
|
_git("describe", "--tags", "--exact-match", cwd=app_dir)
|
|
or _git("describe", "--tags", "--always", cwd=app_dir)
|
|
)
|
|
if not out.get("last_updated"):
|
|
out["last_updated"] = _git("log", "-1", "--format=%cI", cwd=app_dir)
|
|
return out
|