- validate-frontmatter.py: - In ein sauberes, idiomatisches Skript mit Funktionen (load_schema, extract_frontmatter, validate_file, main) refaktoriert. - Pfadbehandlung auf pathlib umgestellt; robustere Frontmatter-Erkennung via Regex (unterstützt LF/CRLF, nur am Datei‑Anfang). - Verbesserte, klare Fehlermeldungen; Exit-Code jetzt 0/1 über sys.exit(main()). - Typannotationen und Module‑Docstring ergänzt; __future__ für |‑Unions hinzugefügt. - Sichere Schema-Ladung mit Fehlerbehandlung (Datei fehlt / ungültiges JSON). - youtrack-sync-kb.py: - Kleinere, idiomatische Typkorrektur: parent_id als Optional (str | None) in create_article, keine Verhaltensänderung. Ergebnis - Die beiden Python-Skripte folgen nun einer sauberen Syntax und idiomatischen Python‑Praktiken (klare Funktionen, Typen, robuste Fehlerbehandlung). Das Verhalten der bestehenden YouTrack‑Synchronisation bleibt unverändert.
79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""
|
|
Validiert YAML-Frontmatter in Markdown-Dateien unterhalb von docs/ gegen ein JSON-Schema.
|
|
|
|
- Erwartet das Schema in docs/.frontmatter.schema.json
|
|
- Meldet fehlendes oder ungültiges Frontmatter pro Datei
|
|
- Exit-Code 0 bei Erfolg, 1 wenn mindestens ein Fehler auftritt
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
try:
|
|
import jsonschema
|
|
except ImportError: # pragma: no cover
|
|
# GitHub Actions step installiert dies vor dem Lauf; freundlichere Fehlermeldung
|
|
raise SystemExit("[FM] jsonschema nicht installiert. Bitte ausführen: pip install jsonschema pyyaml")
|
|
|
|
|
|
SCHEMA_PATH = Path("docs/.frontmatter.schema.json")
|
|
# Erlaubt LF und CRLF, verlangt Frontmatter am Datei-Anfang
|
|
FM_REGEX = re.compile(r"\A---\r?\n(.*?)\r?\n---(?:\r?\n|$)", re.S)
|
|
|
|
|
|
def load_schema(path: Path) -> dict[str, Any]:
|
|
try:
|
|
with path.open(encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except FileNotFoundError:
|
|
print(f"[FM] Schema-Datei fehlt: {path.as_posix()}")
|
|
sys.exit(1)
|
|
except json.JSONDecodeError as e: # pragma: no cover
|
|
print(f"[FM] Ungültiges JSON-Schema in {path.as_posix()}: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def extract_frontmatter(text: str) -> str | None:
|
|
m = FM_REGEX.search(text)
|
|
return m.group(1) if m else None
|
|
|
|
|
|
def validate_file(path: Path, schema: dict[str, Any]) -> bool:
|
|
content = path.read_text(encoding="utf-8")
|
|
fm_text = extract_frontmatter(content)
|
|
if fm_text is None:
|
|
print(f"[FM] fehlt: {path.as_posix()}")
|
|
return False
|
|
try:
|
|
fm = yaml.safe_load(fm_text) or {}
|
|
jsonschema.validate(fm, schema)
|
|
return True
|
|
except Exception as e: # jsonschema.ValidationError u.a.
|
|
print(f"[FM] ungültig in {path.as_posix()}: {e}")
|
|
return False
|
|
|
|
|
|
def main() -> int:
|
|
schema = load_schema(SCHEMA_PATH)
|
|
|
|
had_error = False
|
|
for path in Path("docs").rglob("*.md"):
|
|
# ADRs und ggf. generierte Inhalte vorerst ausnehmen (separater Rollout für FM)
|
|
if path.as_posix().startswith("docs/architecture/adr/"):
|
|
continue
|
|
if not validate_file(path, schema):
|
|
had_error = True
|
|
|
|
return 1 if had_error else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|