From 64d9e77fd7c99a081543e9e14aceaa3a199ea199 Mon Sep 17 00:00:00 2001 From: monsieurleberre Date: Sat, 11 Jul 2026 23:17:51 +0200 Subject: [PATCH] fix(coverage): fail coverage-output loud when expected coverage artifact is missing The coverage-output job in csharp-ci.yaml and scala-ci.yaml downloaded the coverage-percent artifact with continue-on-error: true and defaulted to an empty coverage output when the file was absent, conflating 'no coverage shard configured' (empty is correct) with 'coverage shard uploaded but the download failed' (blank/stale badge on a green run). The job now derives COVERAGE_EXPECTED from the normalized matrix: when a shard set coverage: true the download runs without continue-on-error and the read step rejects an empty value; when none did, the download step is skipped and the empty output stays intentional. Closes #25 --- .github/workflows/csharp-ci.yaml | 10 ++++++++-- .github/workflows/scala-ci.yaml | 10 ++++++++-- CHANGELOG.md | 4 ++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/csharp-ci.yaml b/.github/workflows/csharp-ci.yaml index 2b17680..b6d5aad 100644 --- a/.github/workflows/csharp-ci.yaml +++ b/.github/workflows/csharp-ci.yaml @@ -421,16 +421,18 @@ jobs: coverage-output: name: coverage-output - needs: build-and-test + needs: [normalize, build-and-test] if: ${{ success() }} runs-on: ubuntu-latest permissions: contents: read + env: + COVERAGE_EXPECTED: ${{ contains(fromJSON(needs.normalize.outputs.matrix).*.coverage, true) }} outputs: coverage: ${{ steps.read.outputs.coverage }} steps: - name: Download coverage value - continue-on-error: true + if: ${{ env.COVERAGE_EXPECTED == 'true' }} uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ inputs.artifact-prefix }}-coverage-percent @@ -444,6 +446,10 @@ jobs: if [ -f coverage/coverage-percent.txt ]; then coverage=$(tr -d '[:space:]' < coverage/coverage-percent.txt) fi + if [ "$COVERAGE_EXPECTED" = "true" ] && [ -z "$coverage" ]; then + echo "::error::the coverage shard uploaded a coverage-percent artifact but no coverage value reached coverage-output — failing instead of emitting a blank coverage badge" + exit 1 + fi echo "coverage=$coverage" >> "$GITHUB_OUTPUT" matrix-output: diff --git a/.github/workflows/scala-ci.yaml b/.github/workflows/scala-ci.yaml index 3bee5e4..b6b457d 100644 --- a/.github/workflows/scala-ci.yaml +++ b/.github/workflows/scala-ci.yaml @@ -351,16 +351,18 @@ jobs: coverage-output: name: coverage-output - needs: build-and-test + needs: [normalize, build-and-test] if: ${{ success() }} runs-on: ubuntu-latest permissions: contents: read + env: + COVERAGE_EXPECTED: ${{ contains(fromJSON(needs.normalize.outputs.matrix).*.coverage, true) }} outputs: coverage: ${{ steps.read.outputs.coverage }} steps: - name: Download coverage value - continue-on-error: true + if: ${{ env.COVERAGE_EXPECTED == 'true' }} uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ inputs.artifact-prefix }}-coverage-percent @@ -374,6 +376,10 @@ jobs: if [ -f coverage/coverage-percent.txt ]; then coverage=$(tr -d '[:space:]' < coverage/coverage-percent.txt) fi + if [ "$COVERAGE_EXPECTED" = "true" ] && [ -z "$coverage" ]; then + echo "::error::the coverage shard uploaded a coverage-percent artifact but no coverage value reached coverage-output — failing instead of emitting a blank coverage badge" + exit 1 + fi echo "coverage=$coverage" >> "$GITHUB_OUTPUT" matrix-output: diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e87d2..7f06b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fix `csharp-ci.yaml` and `scala-ci.yaml` silently emitting an empty `coverage` output when the coverage-percent artifact download failed. The `coverage-output` job downloaded the artifact with `continue-on-error: true` and defaulted to empty when the file was absent, conflating "no shard set `coverage: true`" (empty is correct) with "the coverage shard uploaded the artifact but the download failed" (throttling, retention, infra) — the latter fed a blank or stale coverage badge from an otherwise green run. The job now derives whether a coverage shard was configured from the normalized matrix: when one was, the artifact download must succeed and yield a non-empty value or the job fails loud; when none was, the download step is skipped and the empty output stays intentional. Consumers whose coverage shard uploads normally see no change. (#25) + ## [2.3.4] - 2026-07-10 ### Fixed