diff --git a/testing/ci/publish_metrics.py b/testing/ci/publish_metrics.py index c59dc3c..d816db6 100644 --- a/testing/ci/publish_metrics.py +++ b/testing/ci/publish_metrics.py @@ -20,6 +20,8 @@ def _build_parser() -> argparse.ArgumentParser: parser.add_argument("--status", choices=("ok", "failed"), required=True, help="Gate outcome") parser.add_argument("--junit", nargs="*", default=(), help="JUnit XML files to aggregate") parser.add_argument("--quality-report", default="build/quality-gate.json", help="Quality gate JSON report") + parser.add_argument("--branch", default=os.getenv("BRANCH_NAME", os.getenv("GIT_BRANCH", "")), help="SCM branch") + parser.add_argument("--build-number", default=os.getenv("BUILD_NUMBER", ""), help="Jenkins build number") return parser @@ -104,6 +106,8 @@ def main(argv: list[str] | None = None) -> int: summary=summary, workspace_line_coverage_percent=coverage_percent, source_lines_over_500=source_lines_over_500, + branch=args.branch, + build_number=args.build_number, checks=checks, test_cases=test_cases, ) diff --git a/testing/ci/summary.py b/testing/ci/summary.py index c608fd5..5e4eb14 100644 --- a/testing/ci/summary.py +++ b/testing/ci/summary.py @@ -15,6 +15,12 @@ def _escape_label(value: str) -> str: return value.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"') +def _label_str(labels: dict[str, str]) -> str: + """Render Prometheus labels, omitting empty optional values.""" + parts = [f'{key}="{_escape_label(val)}"' for key, val in labels.items() if val] + return "{" + ",".join(parts) + "}" if parts else "" + + @dataclass(frozen=True) class RunSummary: """Aggregate counts from a collection of JUnit XML files.""" @@ -102,6 +108,8 @@ def render_payload( summary: RunSummary, workspace_line_coverage_percent: float = 0.0, source_lines_over_500: int = 0, + branch: str = "", + build_number: str = "", checks: dict[str, str] | None = None, test_cases: list[tuple[str, str]] | None = None, ) -> str: @@ -119,6 +127,8 @@ def render_payload( f'platform_quality_gate_workspace_line_coverage_percent{{suite="{suite}"}} {workspace_line_coverage_percent:.3f}\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" + f'platform_quality_gate_build_info{_label_str({"suite": suite, "branch": branch, "build_number": build_number})} 1\n' ) if checks: payload += "# TYPE bstein_home_quality_gate_checks_total gauge\n" @@ -146,6 +156,8 @@ def publish_quality_metrics( summary: RunSummary, workspace_line_coverage_percent: float = 0.0, source_lines_over_500: int = 0, + branch: str = "", + build_number: str = "", checks: dict[str, str] | None = None, test_cases: list[tuple[str, str]] | None = None, ) -> None: @@ -166,6 +178,8 @@ def publish_quality_metrics( summary=summary, workspace_line_coverage_percent=workspace_line_coverage_percent, source_lines_over_500=source_lines_over_500, + branch=branch, + build_number=build_number, checks=checks, test_cases=test_cases, )