Wires code-coverage collection into the Build & test (.NET) CI job and publishes a human-readable summary. This is a bounded increment of #15 (scope item 4: "Coverage — collect with coverlet and publish a summary; decide on a floor later"). #15 stays open as an increments tracker; this PR refs it, it does not close it.
Changes
ErsatzTV.Core.Tests, ErsatzTV.Tests — add coverlet.collector (already referenced by the other four *.Tests projects) so coverage is uniform across all six.
.gitea/workflows/docker-build.yml — the Test step now runs --collect:"XPlat Code Coverage" --results-directory ./coverage; a new Coverage summary step merges the per-project Cobertura reports with ReportGenerator (TextSummary → log, MarkdownSummaryGithub → job step summary). continue-on-error: true keeps it purely informational — a missing report or transient tool install never blocks a build. No coverage floor is enforced yet.
.gitignore — ignore /coverage/.
docs/ci-cd.md — document the coverage step under the test job.
Verification (local, worktree)
dotnet build of both changed test projects: 0 errors.
Ran dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage on a test project → Cobertura emitted; reportgenerator … -reporttypes:TextSummary;MarkdownSummaryGithub produced both summaries. Full flow proven end-to-end (bash, matching the CI shell).
Scope notes
No .cs changed → the fix-as-you-touch formatting gate (#311) is a no-op here.
Pure CI/tooling + docs; no server-state effect, so per the independent-review skip rubric an independent review pass is skippable — I'll still run a cold-context review over the diff before merge.
Rule promotions (suggestion→warning/error) remain out of scope for this increment (higher-risk; see #25 for the razor Sonar burn-down).
## What
Wires code-coverage collection into the `Build & test (.NET)` CI job and publishes a human-readable summary. This is a **bounded increment of #15** (scope item 4: "Coverage — collect with coverlet and publish a summary; decide on a floor later"). #15 stays open as an increments tracker; this PR **refs** it, it does not close it.
## Changes
- **`ErsatzTV.Core.Tests`, `ErsatzTV.Tests`** — add `coverlet.collector` (already referenced by the other four `*.Tests` projects) so coverage is uniform across all six.
- **`.gitea/workflows/docker-build.yml`** — the `Test` step now runs `--collect:"XPlat Code Coverage" --results-directory ./coverage`; a new **Coverage summary** step merges the per-project Cobertura reports with ReportGenerator (`TextSummary` → log, `MarkdownSummaryGithub` → job step summary). `continue-on-error: true` keeps it purely informational — a missing report or transient tool install never blocks a build. **No coverage floor is enforced yet.**
- **`.gitignore`** — ignore `/coverage/`.
- **`docs/ci-cd.md`** — document the coverage step under the `test` job.
## Verification (local, worktree)
- `dotnet build` of both changed test projects: **0 errors**.
- Ran `dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage` on a test project → Cobertura emitted; `reportgenerator … -reporttypes:TextSummary;MarkdownSummaryGithub` produced both summaries. Full flow proven end-to-end (bash, matching the CI shell).
## Scope notes
- **No `.cs` changed** → the fix-as-you-touch formatting gate (#311) is a no-op here.
- Pure CI/tooling + docs; **no server-state effect**, so per the independent-review skip rubric an independent review pass is skippable — I'll still run a cold-context review over the diff before merge.
- Rule promotions (suggestion→warning/error) remain out of scope for this increment (higher-risk; see #25 for the razor Sonar burn-down).
refs #15
#15 scope item 4 ("collect with coverlet and publish a summary; decide on
a floor later"). coverlet.collector was already referenced by four of the
six test projects but the CI Test step never passed --collect and nothing
summarized the result.
- Add coverlet.collector to ErsatzTV.Core.Tests and ErsatzTV.Tests so
coverage is uniform across all *.Tests projects.
- CI Test step now runs --collect:"XPlat Code Coverage" --results-directory
./coverage, then a best-effort Coverage summary step merges the per-project
Cobertura reports with ReportGenerator (TextSummary to the log,
MarkdownSummaryGithub to the job step summary). No floor is enforced yet;
continue-on-error keeps it purely informational.
- gitignore /coverage/; document the step in docs/ci-cd.md (test job).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cold-review should-fix on PR #370: `dotnet tool install` errors under set -e
if the tool is already present (retried step / cached runner image), which
would silently degrade the continue-on-error summary step to a no-op. Use
`dotnet tool update` (install-or-update) pinned to 5.5.10 for reproducible
output. Also order coverlet.collector after CliWrap to keep the ItemGroup
alphabetical (nit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Initial diff (2090f786): MERGEABLE with one should-fix (idempotent + pinned ReportGenerator install) and a cosmetic nit — both applied in the follow-up commit a886fd28.
Fix commit (2090f786..a886fd28): re-reviewed. dotnet tool update … --version 5.5.10 confirmed idempotent (installs-if-absent, exits 0 when already present — unlike install under set -e); 5.5.10 verified as a real published NuGet version; csproj reorder purely cosmetic, CPM preserved (no inline Version=); YAML parses clean.
Independent-review skip rubric: this is a pure CI/tooling + docs leaf with no server-state effect, so an independent pass was technically skippable — ran it anyway (cheap) and it earned its keep by catching the install-idempotency gap.
Independent cold-context review (two passes, review-only scope):
- **Initial diff** (`2090f786`): MERGEABLE with one should-fix (idempotent + pinned ReportGenerator install) and a cosmetic nit — both applied in the follow-up commit `a886fd28`.
- **Fix commit** (`2090f786..a886fd28`): re-reviewed. `dotnet tool update … --version 5.5.10` confirmed idempotent (installs-if-absent, exits 0 when already present — unlike `install` under `set -e`); `5.5.10` verified as a real published NuGet version; csproj reorder purely cosmetic, CPM preserved (no inline `Version=`); YAML parses clean.
Independent-review skip rubric: this is a pure CI/tooling + docs leaf with **no server-state effect**, so an independent pass was technically skippable — ran it anyway (cheap) and it earned its keep by catching the install-idempotency gap.
Review-verdict: MERGEABLE @ a886fd2824db97439a52d75440b1efd7c99423d9
The shared Build & test job OOM-killed (exit 137) with coverage enabled: the
whole-solution Architecture.Tests process instruments both EF migration folders
(~2.59M generated lines vs ~200k authored), ballooning coverlet's in-memory
hit-count arrays past the runner's limit.
Add coverlet.runsettings excluding **/Migrations/*.cs (+ generated/compiler
attributes) and pass it via --settings on the Test step. This cuts the
instrumented surface ~126x (2,510,185 -> 19,963 coverable lines in the
Architecture.Tests process, verified locally) and makes the reported percentage
reflect authored code. Documented in docs/ci-cd.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update: fixed an OOM the initial coverage change introduced
The CI monitor (armed at PR-open) caught the gating Build & test job OOM-killing (exit 137) on the previous head. Root cause: coverage instrumentation of the whole-solution ErsatzTV.Architecture.Tests process was covering both EF migration folders — ~2.59M generated lines vs ~200k authored — blowing coverlet's in-memory hit-count arrays past the shared runner's limit (intermittent: passed on one run, OOM'd on another for the same sha under memory pressure).
Fix (e78aa01c): added coverlet.runsettings excluding **/Migrations/*.cs + generated/compiler attributes + [*]*.Migrations.*, passed via --settings on the Test step. Verified locally: coverable lines in the Architecture.Tests process dropped 2,510,185 → 19,963 (~126×). Bonus: the reported percentage now reflects authored code, not generated migrations.
Independent cold re-review of the fix commit: verified valid runsettings XML (xmllint), all 1100 migration files sit flat under Migrations/ (glob catches them), no hand-written code lives there (exclusion doesn't hide authored code), and --collect … --settings … is a valid combination.
## Update: fixed an OOM the initial coverage change introduced
The CI monitor (armed at PR-open) caught the gating **Build & test** job OOM-killing (`exit 137`) on the previous head. Root cause: coverage instrumentation of the whole-solution `ErsatzTV.Architecture.Tests` process was covering both EF migration folders — **~2.59M generated lines vs ~200k authored** — blowing coverlet's in-memory hit-count arrays past the shared runner's limit (intermittent: passed on one run, OOM'd on another for the same sha under memory pressure).
**Fix** (`e78aa01c`): added `coverlet.runsettings` excluding `**/Migrations/*.cs` + generated/compiler attributes + `[*]*.Migrations.*`, passed via `--settings` on the Test step. Verified locally: coverable lines in the Architecture.Tests process dropped **2,510,185 → 19,963 (~126×)**. Bonus: the reported percentage now reflects authored code, not generated migrations.
Independent cold re-review of the fix commit: verified valid runsettings XML (`xmllint`), all 1100 migration files sit flat under `Migrations/` (glob catches them), no hand-written code lives there (exclusion doesn't hide authored code), and `--collect … --settings …` is a valid combination.
Review-verdict: MERGEABLE @ e78aa01c05552d2e403662e248674df00920862e
timothy
merged commit 3654fad163 into main2026-07-16 19:29:35 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Wires code-coverage collection into the
Build & test (.NET)CI job and publishes a human-readable summary. This is a bounded increment of #15 (scope item 4: "Coverage — collect with coverlet and publish a summary; decide on a floor later"). #15 stays open as an increments tracker; this PR refs it, it does not close it.Changes
ErsatzTV.Core.Tests,ErsatzTV.Tests— addcoverlet.collector(already referenced by the other four*.Testsprojects) so coverage is uniform across all six..gitea/workflows/docker-build.yml— theTeststep now runs--collect:"XPlat Code Coverage" --results-directory ./coverage; a new Coverage summary step merges the per-project Cobertura reports with ReportGenerator (TextSummary→ log,MarkdownSummaryGithub→ job step summary).continue-on-error: truekeeps it purely informational — a missing report or transient tool install never blocks a build. No coverage floor is enforced yet..gitignore— ignore/coverage/.docs/ci-cd.md— document the coverage step under thetestjob.Verification (local, worktree)
dotnet buildof both changed test projects: 0 errors.dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverageon a test project → Cobertura emitted;reportgenerator … -reporttypes:TextSummary;MarkdownSummaryGithubproduced both summaries. Full flow proven end-to-end (bash, matching the CI shell).Scope notes
.cschanged → the fix-as-you-touch formatting gate (#311) is a no-op here.refs #15
Independent cold-context review (two passes, review-only scope):
2090f786): MERGEABLE with one should-fix (idempotent + pinned ReportGenerator install) and a cosmetic nit — both applied in the follow-up commita886fd28.2090f786..a886fd28): re-reviewed.dotnet tool update … --version 5.5.10confirmed idempotent (installs-if-absent, exits 0 when already present — unlikeinstallunderset -e);5.5.10verified as a real published NuGet version; csproj reorder purely cosmetic, CPM preserved (no inlineVersion=); YAML parses clean.Independent-review skip rubric: this is a pure CI/tooling + docs leaf with no server-state effect, so an independent pass was technically skippable — ran it anyway (cheap) and it earned its keep by catching the install-idempotency gap.
Review-verdict: MERGEABLE @
a886fd2824Update: fixed an OOM the initial coverage change introduced
The CI monitor (armed at PR-open) caught the gating Build & test job OOM-killing (
exit 137) on the previous head. Root cause: coverage instrumentation of the whole-solutionErsatzTV.Architecture.Testsprocess was covering both EF migration folders — ~2.59M generated lines vs ~200k authored — blowing coverlet's in-memory hit-count arrays past the shared runner's limit (intermittent: passed on one run, OOM'd on another for the same sha under memory pressure).Fix (
e78aa01c): addedcoverlet.runsettingsexcluding**/Migrations/*.cs+ generated/compiler attributes +[*]*.Migrations.*, passed via--settingson the Test step. Verified locally: coverable lines in the Architecture.Tests process dropped 2,510,185 → 19,963 (~126×). Bonus: the reported percentage now reflects authored code, not generated migrations.Independent cold re-review of the fix commit: verified valid runsettings XML (
xmllint), all 1100 migration files sit flat underMigrations/(glob catches them), no hand-written code lives there (exclusion doesn't hide authored code), and--collect … --settings …is a valid combination.Review-verdict: MERGEABLE @
e78aa01c05