1313
1414Run via ``uv run --no-project --with 'coverage[toml]' python .ci/coverage_report.py``;
1515coverage settings are read from ``[tool.coverage.*]`` in ``pyproject.toml``.
16+
17+ The script also enforces a regression floor on the *combined* total
18+ (``MIN_TOTAL_COVERAGE``): a dispatch whose total drops below it fails this job.
19+ The threshold lives here rather than in ``[tool.coverage.report] fail_under``
20+ on purpose — pytest-cov reads that key, and each per-job ``--cov`` run measures
21+ only a slice of the package, so a config-level ``fail_under`` would fail every
22+ partial run. Enforcing here gates the combined total only.
1623"""
1724
1825from __future__ import annotations
2734
2835logger = logging .getLogger ("coverage_report" )
2936
37+ # Minimum acceptable combined coverage (%). Bump this as coverage improves to
38+ # ratchet the floor up; keep it a few points below the current total so normal
39+ # churn doesn't trip it.
40+ MIN_TOTAL_COVERAGE = 85.0
41+
3042
3143def main () -> int :
32- """Combine coverage data, emit reports, and write the GitHub step summary."""
44+ """Combine coverage data, emit reports, write the GitHub step summary, gate the total ."""
3345 logging .basicConfig (level = logging .INFO , format = "%(message)s" , stream = sys .stderr )
3446
3547 cov = coverage .Coverage ()
@@ -41,15 +53,22 @@ def main() -> int:
4153 cov .html_report ()
4254
4355 logger .info ("Total coverage: %.2f%%" , total )
56+ passed = total >= MIN_TOTAL_COVERAGE
4457
4558 summary_path = os .environ .get ("GITHUB_STEP_SUMMARY" )
4659 if summary_path :
4760 compact = io .StringIO ()
4861 cov .report (file = compact , show_missing = False )
49- body = f"### Test coverage: { total :.2f} %\n \n ```\n { compact .getvalue ()} ```\n "
62+ gate_icon = "✅" if passed else "❌"
63+ gate_line = f"{ gate_icon } Gate: { total :.2f} % vs { MIN_TOTAL_COVERAGE :.2f} % minimum\n "
64+ body = f"### Test coverage: { total :.2f} %\n \n { gate_line } \n ```\n { compact .getvalue ()} ```\n "
5065 with Path (summary_path ).open ("a" , encoding = "utf-8" ) as fh :
5166 fh .write (body )
5267
68+ if not passed :
69+ logger .error ("Coverage %.2f%% is below the required minimum of %.2f%%" , total , MIN_TOTAL_COVERAGE )
70+ return 1
71+
5372 return 0
5473
5574
0 commit comments