Skip to content

Commit 058c548

Browse files
marius-bughiuclaude
andcommitted
ci(coverage): replace ReportGenerator with an in-repo report generator
ReportGenerator's free HTML report renders a "Method coverage: Feature is only available for sponsors" upsell row that can't be cleanly suppressed. Drop the dependency and render our own. scripts/coverage_report.py reads the Cobertura XML coverlet already produces and emits: - index.html styled like the rest of the Celerity site (per-file line/branch bars, uncovered-line ranges linked to source, lowest-coverage-first) - badge.svg (line-coverage shields-style badge, self-generated) - summary.md for the PR comment It also enforces the coverage floor and writes the run summary, folding the old separate gate step into one script. No third-party branding. The coverage workflow now runs the script instead of the ReportGenerator action + inline gate; the README badge points at coverage/badge.svg; the testing guide documents the generator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent de65c0f commit 058c548

4 files changed

Lines changed: 436 additions & 41 deletions

File tree

.github/workflows/coverage.yml

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,16 @@ jobs:
5252
--settings coverage.runsettings
5353
--results-directory ./TestResults/coverage
5454
55-
- name: Generate report
56-
uses: danielpalme/ReportGenerator-GitHub-Action@v5.5.10
57-
with:
58-
reports: 'src/TestResults/coverage/**/coverage.cobertura.xml'
59-
targetdir: 'coveragereport'
60-
reporttypes: 'Html;MarkdownSummaryGithub;Badges;Cobertura;TextSummary'
61-
title: 'Celerity coverage'
55+
# Renders the HTML report, badge, and PR summary, writes the run summary,
56+
# and fails the job if coverage is below the floor — all in one script, so
57+
# the report carries the project's own styling and no third-party upsell.
58+
- name: Generate report and enforce floor
59+
run: >
60+
python3 scripts/coverage_report.py
61+
--input "src/TestResults/coverage/**/coverage.cobertura.xml"
62+
--outdir coveragereport
63+
--min-line "$MIN_LINE_COVERAGE"
64+
--min-branch "$MIN_BRANCH_COVERAGE"
6265
6366
- name: Upload coverage artifact
6467
if: always()
@@ -69,39 +72,15 @@ jobs:
6972
if-no-files-found: warn
7073
retention-days: 14
7174

72-
- name: Enforce coverage floor
73-
run: |
74-
python3 - <<'PY'
75-
import os, sys, xml.etree.ElementTree as ET
76-
root = ET.parse('coveragereport/Cobertura.xml').getroot()
77-
line = float(root.get('line-rate')) * 100
78-
branch = float(root.get('branch-rate')) * 100
79-
min_line = float(os.environ['MIN_LINE_COVERAGE'])
80-
min_branch = float(os.environ['MIN_BRANCH_COVERAGE'])
81-
summary = (
82-
f"### Coverage\n\n"
83-
f"| Metric | Value | Floor |\n|---|---:|---:|\n"
84-
f"| Line | {line:.2f}% | {min_line:.0f}% |\n"
85-
f"| Branch | {branch:.2f}% | {min_branch:.0f}% |\n"
86-
)
87-
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as f:
88-
f.write(summary)
89-
print(f"Line coverage: {line:.2f}% (floor {min_line:.0f}%)")
90-
print(f"Branch coverage: {branch:.2f}% (floor {min_branch:.0f}%)")
91-
if line < min_line or branch < min_branch:
92-
print("::error::Coverage dropped below the configured floor.")
93-
sys.exit(1)
94-
PY
95-
9675
- name: Comment coverage on PR
97-
if: github.event_name == 'pull_request'
76+
if: always() && github.event_name == 'pull_request'
9877
uses: actions/github-script@v7
9978
with:
10079
github-token: ${{ secrets.GITHUB_TOKEN }}
10180
script: |
10281
const fs = require('fs');
10382
const marker = '<!-- celerity-coverage-comment -->';
104-
const summary = fs.readFileSync('coveragereport/SummaryGithub.md', 'utf8');
83+
const summary = fs.readFileSync('coveragereport/summary.md', 'utf8');
10584
const body = `${marker}\n${summary}`;
10685
10786
const { data: comments } = await github.rest.issues.listComments({

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Celerity
2-
[![NuGet version (Celerity.Collections)](https://img.shields.io/nuget/v/Celerity.Collections.svg?style=flat-square)](https://www.nuget.org/packages/Celerity.Collections/) [![NuGet version (Celerity.Collections)](https://img.shields.io/nuget/vpre/Celerity.Collections.svg?style=flat-square)](https://www.nuget.org/packages/Celerity.Collections/) [![Live benchmarks](https://img.shields.io/badge/benchmarks-live-0d6e6e?style=flat-square)](https://marius-bughiu.github.io/Celerity/dev/bench/) [![Coverage](https://marius-bughiu.github.io/Celerity/coverage/badge_linecoverage.svg)](https://marius-bughiu.github.io/Celerity/coverage/)
2+
[![NuGet version (Celerity.Collections)](https://img.shields.io/nuget/v/Celerity.Collections.svg?style=flat-square)](https://www.nuget.org/packages/Celerity.Collections/) [![NuGet version (Celerity.Collections)](https://img.shields.io/nuget/vpre/Celerity.Collections.svg?style=flat-square)](https://www.nuget.org/packages/Celerity.Collections/) [![Live benchmarks](https://img.shields.io/badge/benchmarks-live-0d6e6e?style=flat-square)](https://marius-bughiu.github.io/Celerity/dev/bench/) [![Coverage](https://marius-bughiu.github.io/Celerity/coverage/badge.svg)](https://marius-bughiu.github.io/Celerity/coverage/)
33

44
Celerity is a .NET library that provides specialized high-performance collections optimized for specific use cases. It includes data structures designed for better speed or memory efficiency compared to standard .NET collections. The package supports configurable load factors, multiple built-in hash functions, and allows users to define custom hash functions for fine-tuned performance.
55

docs/testing.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,21 +119,21 @@ dotnet test Celerity.Tests/Celerity.Tests.csproj \
119119
--settings coverage.runsettings \
120120
--results-directory ./TestResults/coverage
121121

122-
# 2. render an HTML report (one-time tool install)
123-
dotnet tool install -g dotnet-reportgenerator-globaltool
124-
reportgenerator \
125-
-reports:"./TestResults/coverage/**/coverage.cobertura.xml" \
126-
-targetdir:coveragereport \
127-
-reporttypes:"Html;TextSummary"
122+
# 2. render the HTML report + badge (pure Python, no extra tooling)
123+
python3 ../scripts/coverage_report.py \
124+
--input "./TestResults/coverage/**/coverage.cobertura.xml" \
125+
--outdir ../coveragereport --min-line 95 --min-branch 90
128126

129127
# 3. open coveragereport/index.html
130128
```
131129

130+
The report is rendered by [`scripts/coverage_report.py`](../scripts/coverage_report.py) — a small generator that reads the Cobertura XML coverlet produces and emits an `index.html` styled like the rest of the Celerity site, a `badge.svg`, and a `summary.md`. It exists so the report carries the project's own look and no third-party "sponsors only" upsell; there is no dependency on ReportGenerator.
131+
132132
### CI gate
133133

134134
The `coverage` workflow (`.github/workflows/coverage.yml`) runs on every PR and on `main`:
135135

136-
- Collects coverage, renders the report with ReportGenerator, and uploads it as a build artifact.
136+
- Collects coverage, renders the report + badge with `scripts/coverage_report.py`, and uploads it as a build artifact.
137137
- **Fails the build** if line coverage drops below `MIN_LINE_COVERAGE` (95%) or branch coverage below `MIN_BRANCH_COVERAGE` (90%). The suite sits far above these (~99.9% line) — the floor guards against silent regressions; it is not the target.
138138
- Posts a coverage summary comment on the PR.
139139
- On `main`, publishes the HTML report to `gh-pages` under [`/coverage`](https://marius-bughiu.github.io/Celerity/coverage/) and refreshes the README badge.

0 commit comments

Comments
 (0)