ci: publish LOC source file totals

This commit is contained in:
codex 2026-05-11 17:39:53 -03:00
parent 65d5212072
commit 1c9d362403
5 changed files with 20 additions and 4 deletions

View File

@ -29,11 +29,11 @@ def _build_parser() -> argparse.ArgumentParser:
return parser
def _load_quality_report(path: Path) -> tuple[float, int, dict[str, str]]:
def _load_quality_report(path: Path) -> tuple[float, int, int, dict[str, str]]:
"""Read workspace coverage/LOC summary from the quality gate JSON output."""
if not path.exists():
return 0.0, 0, {
return 0.0, 0, 0, {
"tests": "not_applicable",
"coverage": "not_applicable",
"loc": "not_applicable",
@ -49,6 +49,10 @@ def _load_quality_report(path: Path) -> tuple[float, int, dict[str, str]]:
source_lines = payload.get("source_lines_over_500")
if not isinstance(source_lines, int):
source_lines = 0
source_files = payload.get("source_files_total")
if not isinstance(source_files, int):
managed_files = payload.get("managed_files")
source_files = len(managed_files) if isinstance(managed_files, list) else 0
issue_checks = [item.get("check") for item in payload.get("issues", []) if isinstance(item, dict)]
docs_failed = any(str(check).lower() in {"docstring", "docs", "naming"} for check in issue_checks)
coverage_failed = any(str(check).lower() == "coverage" for check in issue_checks)
@ -95,7 +99,7 @@ def _load_quality_report(path: Path) -> tuple[float, int, dict[str, str]]:
checks["supply_chain"] = "failed" if ironbank_required else "not_applicable"
except Exception:
checks["supply_chain"] = "failed" if ironbank_required else "not_applicable"
return float(coverage), int(source_lines), checks
return float(coverage), int(source_files), int(source_lines), checks
def main(argv: list[str] | None = None) -> int:
@ -106,7 +110,7 @@ def main(argv: list[str] | None = None) -> int:
junit_paths = [Path(path) for path in args.junit]
summary = load_junit_summary(junit_paths)
test_cases = load_junit_cases(junit_paths)
coverage_percent, source_lines_over_500, checks = _load_quality_report(Path(args.quality_report))
coverage_percent, source_files_total, source_lines_over_500, checks = _load_quality_report(Path(args.quality_report))
publish_quality_metrics(
gateway=args.gateway,
suite=args.suite,
@ -114,6 +118,7 @@ def main(argv: list[str] | None = None) -> int:
status=args.status,
summary=summary,
workspace_line_coverage_percent=coverage_percent,
source_files_total=source_files_total,
source_lines_over_500=source_lines_over_500,
branch=args.branch,
build_number=args.build_number,

View File

@ -330,6 +330,9 @@ def run_gate(contract_path: Path, *, backend_coverage: Path, frontend_coverage:
frontend_report=frontend_coverage,
)
workspace_line_coverage_percent = round(sum(coverage_values) / len(coverage_values), 3) if coverage_values else 0.0
source_files_total = sum(
1 for path in managed_files if path.exists() and path.suffix.lower() in TEXT_EXTENSIONS
)
source_lines_over_500 = sum(1 for issue in issues if issue.check == "loc")
report = {
"managed_files": [str(path.relative_to(ROOT)) for path in managed_files],
@ -338,6 +341,7 @@ def run_gate(contract_path: Path, *, backend_coverage: Path, frontend_coverage:
"max_lines": max_lines,
"coverage_threshold_pct": threshold,
"workspace_line_coverage_percent": workspace_line_coverage_percent,
"source_files_total": source_files_total,
"source_lines_over_500": source_lines_over_500,
"issue_count": len(issues),
"issues": [issue.__dict__ for issue in issues],

View File

@ -124,6 +124,7 @@ def render_payload(
failed: int,
summary: RunSummary,
workspace_line_coverage_percent: float = 0.0,
source_files_total: int = 0,
source_lines_over_500: int = 0,
branch: str = "",
build_number: str = "",
@ -150,6 +151,8 @@ def render_payload(
f'bstein_home_quality_gate_tests_total{{suite="{suite}",result="skipped"}} {summary.skipped}\n'
"# TYPE platform_quality_gate_workspace_line_coverage_percent gauge\n"
f'platform_quality_gate_workspace_line_coverage_percent{{suite="{suite}"}} {workspace_line_coverage_percent:.3f}\n'
"# TYPE platform_quality_gate_source_files_total gauge\n"
f'platform_quality_gate_source_files_total{{suite="{suite}"}} {int(source_files_total)}\n'
"# TYPE platform_quality_gate_source_lines_over_500_total gauge\n"
f'platform_quality_gate_source_lines_over_500_total{{suite="{suite}"}} {int(source_lines_over_500)}\n'
"# TYPE platform_quality_gate_build_info gauge\n"
@ -183,6 +186,7 @@ def publish_quality_metrics(
status: str,
summary: RunSummary,
workspace_line_coverage_percent: float = 0.0,
source_files_total: int = 0,
source_lines_over_500: int = 0,
branch: str = "",
build_number: str = "",
@ -218,6 +222,7 @@ def publish_quality_metrics(
failed=int(counters["failed"]),
summary=summary,
workspace_line_coverage_percent=workspace_line_coverage_percent,
source_files_total=source_files_total,
source_lines_over_500=source_lines_over_500,
branch=branch,
build_number=build_number,

View File

@ -25,6 +25,7 @@ def test_load_junit_summary_combines_suites(tmp_path: Path) -> None:
assert 'platform_quality_gate_runs_total{suite="bstein_home",status="ok"} 2' in payload
assert 'bstein_home_quality_gate_tests_total{suite="bstein_home",result="skipped"} 1' in payload
assert 'platform_quality_gate_workspace_line_coverage_percent{suite="bstein_home"} 0.000' in payload
assert 'platform_quality_gate_source_files_total{suite="bstein_home"} 0' in payload
assert 'platform_quality_gate_source_lines_over_500_total{suite="bstein_home"} 0' in payload

View File

@ -160,6 +160,7 @@ def test_run_gate_reports_workspace_coverage_and_loc_totals(tmp_path: Path) -> N
assert any(issue.check == "loc" for issue in issues)
assert report["workspace_line_coverage_percent"] == 100.0
assert report["source_files_total"] == 2
assert report["source_lines_over_500"] == 1
finally:
quality_gate_module.ROOT = root_before