diff --git a/scripts/publish_test_metrics.py b/scripts/publish_test_metrics.py index 82bc9c5..e49223a 100755 --- a/scripts/publish_test_metrics.py +++ b/scripts/publish_test_metrics.py @@ -9,6 +9,8 @@ Outputs pushed: - platform_quality_gate_runs_total{suite="pegasus",status="ok|failed"} - pegasus_quality_gate_tests_total{suite="pegasus",result=*} - pegasus_quality_gate_coverage_percent{suite="pegasus"} +- platform_quality_gate_workspace_line_coverage_percent{suite="pegasus"} +- platform_quality_gate_source_lines_over_500_total{suite="pegasus"} """ from __future__ import annotations @@ -19,6 +21,9 @@ import urllib.request import xml.etree.ElementTree as ET from pathlib import Path +SOURCE_SCAN_ROOTS = ("backend", "frontend/src", "scripts", "testing") +SOURCE_EXTENSIONS = {".go", ".py", ".ts", ".tsx", ".sh"} + def _escape_label(value: str) -> str: return value.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"') @@ -146,7 +151,25 @@ def _post_text(url: str, payload: str) -> None: raise RuntimeError(f"push failed status={resp.status}") +def _count_source_files_over_limit(repo_root: Path, max_lines: int = 500) -> int: + count = 0 + for rel_root in SOURCE_SCAN_ROOTS: + base = repo_root / rel_root + if not base.exists(): + continue + for path in base.rglob("*"): + if not path.is_file(): + continue + if path.suffix not in SOURCE_EXTENSIONS: + continue + lines = len(path.read_text(encoding="utf-8", errors="ignore").splitlines()) + if lines > max_lines: + count += 1 + return count + + def main() -> int: + repo_root = Path(__file__).resolve().parents[1] suite = os.getenv("SUITE_NAME", "pegasus") pushgateway_url = os.getenv( "PUSHGATEWAY_URL", "http://platform-quality-gateway.monitoring.svc.cluster.local:9091" @@ -192,6 +215,7 @@ def main() -> int: } gate_ok = bool(gate_summary.get("ok")) gate_issues = gate_summary.get("issues") or [] + source_lines_over_500 = _count_source_files_over_limit(repo_root, max_lines=500) outcome = ( "ok" if gate_ok @@ -234,6 +258,10 @@ def main() -> int: f'pegasus_quality_gate_tests_total{{suite="{suite}",result="skipped"}} {totals["skipped"]}', "# TYPE pegasus_quality_gate_coverage_percent gauge", f'pegasus_quality_gate_coverage_percent{{suite="{suite}"}} {coverage_pct:.3f}', + "# TYPE platform_quality_gate_workspace_line_coverage_percent gauge", + f'platform_quality_gate_workspace_line_coverage_percent{{suite="{suite}"}} {coverage_pct:.3f}', + "# TYPE platform_quality_gate_source_lines_over_500_total gauge", + f'platform_quality_gate_source_lines_over_500_total{{suite="{suite}"}} {source_lines_over_500}', "# TYPE pegasus_quality_gate_status gauge", f'pegasus_quality_gate_status{{suite="{suite}",result="{"ok" if gate_ok else "failed"}"}} 1', "# TYPE pegasus_quality_gate_issues_total gauge", @@ -254,6 +282,7 @@ def main() -> int: "tests_errors": totals["errors"], "tests_skipped": totals["skipped"], "coverage_percent": round(coverage_pct, 3), + "source_lines_over_500": source_lines_over_500, "outcome": outcome, "backend_rc": backend_rc, "frontend_rc": frontend_rc,