Add ci_report_hook/src/ci_report_hook/core.py

This commit is contained in:
Mika 2026-01-28 16:22:44 +00:00
parent 176a79fac7
commit a1eecfea11

View file

@ -0,0 +1,92 @@
import json
import os
from pathlib import Path
from datetime import datetime
from typing import Any, Dict
class CIReport:
"""Datenmodell für einen CI-Run-Report."""
def __init__(
self,
timestamp: str,
run_id: str,
decision: str,
margin: float,
flaky_flag: bool,
subset_flip_count: int,
mischfenster_p95: float,
) -> None:
self.timestamp = timestamp
self.run_id = run_id
self.decision = decision
self.margin = margin
self.flaky_flag = flaky_flag
self.subset_flip_count = subset_flip_count
self.mischfenster_p95 = mischfenster_p95
def to_json(self) -> Dict[str, Any]:
"""Serialisiert den Bericht in ein JSON-kompatibles Dictionary."""
return {
"timestamp": self.timestamp,
"run_id": self.run_id,
"decision": self.decision,
"margin": self.margin,
"flaky_flag": self.flaky_flag,
"subset_flip_count": self.subset_flip_count,
"mischfenster_p95": self.mischfenster_p95,
}
def record_ci_run(
run_id: str,
decision: str,
margin: float,
flaky_flag: bool,
subset_flip_count: int,
mischfenster_p95: float,
) -> None:
"""Speichert die Daten eines CI-Runs als JSON-Datei im Reports-Ordner."""
# Input-Validation
if not isinstance(run_id, str) or not run_id:
raise ValueError("run_id muss ein nicht-leerer String sein.")
if decision not in {"PASS", "WARN", "FAIL"}:
raise ValueError("decision muss PASS, WARN oder FAIL sein.")
if not isinstance(margin, (float, int)):
raise TypeError("margin muss eine float-Zahl sein.")
if not isinstance(flaky_flag, bool):
raise TypeError("flaky_flag muss vom Typ bool sein.")
if not isinstance(subset_flip_count, int) or subset_flip_count < 0:
raise ValueError("subset_flip_count muss eine nicht-negative ganze Zahl sein.")
if not isinstance(mischfenster_p95, (float, int)):
raise TypeError("mischfenster_p95 muss eine float-Zahl sein.")
# Zeitstempel erzeugen
timestamp = datetime.now().isoformat(timespec='seconds')
# CIReport-Objekt erstellen
report = CIReport(
timestamp=timestamp,
run_id=run_id,
decision=decision,
margin=float(margin),
flaky_flag=flaky_flag,
subset_flip_count=subset_flip_count,
mischfenster_p95=float(mischfenster_p95),
)
# Reports-Ordner sicherstellen
reports_dir = Path("reports")
reports_dir.mkdir(parents=True, exist_ok=True)
# Dateiname ableiten
report_path = reports_dir / f"{run_id}.json"
# JSON-Datei schreiben
with report_path.open("w", encoding="utf-8") as f:
json.dump(report.to_json(), f, ensure_ascii=False, indent=2)
# Sanity-Check für CI (ci_ready)
assert report_path.exists(), f"Report-Datei {report_path} wurde nicht erstellt."