@@ -84,74 +84,118 @@ jobs:
8484 BASE=origin/${{ github.base_ref }}
8585 DIFF_OUTPUT=$(diff-cover coverage.xml \
8686 --compare-branch="${BASE}" \
87+ --format json:diff-cover.json \
8788 --fail-under=${{ env.THRESHOLD_DIFF }} \
8889 2>&1) && DIFF_EXIT=0 || DIFF_EXIT=$?
8990
90- DIFF_PCT=$(echo "$DIFF_OUTPUT" | grep -oP 'Diff coverage is \K[0-9.]+' || echo "N/A")
91+ if [ -f diff-cover.json ]; then
92+ DIFF_PCT=$(python3 - <<'PY'
93+ import json
94+
95+ with open('diff-cover.json', encoding='utf-8') as f :
96+ report = json.load(f)
97+
98+ num_changed_lines = report.get('num_changed_lines', 0)
99+ total_num_lines = report.get('total_num_lines', 0)
100+ total_percent_covered = report.get('total_percent_covered', 'N/A')
101+
102+ if num_changed_lines > 0 and total_num_lines == 0 :
103+ print('N/A')
104+ else :
105+ print(total_percent_covered)
106+ PY
107+ )
108+ else
109+ DIFF_PCT="N/A"
110+ fi
111+
112+ if [ "$DIFF_PCT" = "N/A" ]; then
113+ DIFF_DISPLAY="N/A"
114+ else
115+ DIFF_DISPLAY="${DIFF_PCT}%"
116+ fi
117+
91118 echo "diff_pct=${DIFF_PCT}" >> "$GITHUB_OUTPUT"
119+ echo "diff_display=${DIFF_DISPLAY}" >> "$GITHUB_OUTPUT"
92120 echo "$DIFF_OUTPUT"
93121
94- if [ "$DIFF_EXIT" -ne 0 ]; then
122+ if [ "$DIFF_EXIT" -ne 0 ] || [ "$DIFF_PCT" = "N/A" ] ; then
95123 echo "diff_status=fail" >> "$GITHUB_OUTPUT"
124+
125+ if [ "$DIFF_PCT" = "N/A" ]; then
126+ echo "❌ Changed-line coverage could not be computed because diff-cover found changed lines without matching coverage information."
127+ fi
96128 else
97129 echo "diff_status=pass" >> "$GITHUB_OUTPUT"
98130 fi
99131
100132 # ── Generate per-package coverage table ──────────────────────────────
101133 - name : Generate coverage report markdown
134+ if : always()
102135 run : |
103136 {
104137 echo "## 📊 Code Coverage Report"
105138 echo ""
106139
107140 TOTAL="${{ steps.total_coverage.outputs.total }}"
108141 STATUS="${{ steps.total_coverage.outputs.status }}"
142+ [ -n "$TOTAL" ] || TOTAL="N/A"
109143 [ "$STATUS" = "pass" ] && ICON="✅" || ICON="❌"
110144
111145 echo "| Metric | Coverage | Threshold | Status |"
112146 echo "|--------|----------|-----------|--------|"
113147 echo "| Overall | **${TOTAL}%** | ${{ env.THRESHOLD_TOTAL }}% | ${ICON} |"
114148
115149 if [ "${{ github.event_name }}" = "pull_request" ]; then
116- DIFF_PCT ="${{ steps.diff_coverage.outputs.diff_pct }}"
150+ DIFF_DISPLAY ="${{ steps.diff_coverage.outputs.diff_display }}"
117151 DIFF_STATUS="${{ steps.diff_coverage.outputs.diff_status }}"
152+ [ -n "$DIFF_DISPLAY" ] || DIFF_DISPLAY="N/A"
118153 [ "$DIFF_STATUS" = "pass" ] && DIFF_ICON="✅" || DIFF_ICON="❌"
119- echo "| Changed lines | **${DIFF_PCT}% ** | ${{ env.THRESHOLD_DIFF }}% | ${DIFF_ICON} |"
154+ echo "| Changed lines | **${DIFF_DISPLAY} ** | ${{ env.THRESHOLD_DIFF }}% | ${DIFF_ICON} |"
120155 fi
121156
122- echo ""
123- echo "<details>"
124- echo "<summary>📦 Per-package breakdown</summary>"
125- echo ""
126- echo '```'
127- go tool cover -func=coverage.out | grep -v "^total:" | \
128- awk '{printf "%-80s %s\n", $1, $3}' | sort
129- echo ""
130- go tool cover -func=coverage.out | grep "^total:"
131- echo '```'
132- echo ""
133- echo "</details>"
157+ if [ -f coverage.out ]; then
158+ echo ""
159+ echo "<details>"
160+ echo "<summary>📦 Per-package breakdown</summary>"
161+ echo ""
162+ echo '```'
163+ go tool cover -func=coverage.out | grep -v "^total:" | \
164+ awk '{printf "%-80s %s\n", $1, $3}' | sort
165+ echo ""
166+ go tool cover -func=coverage.out | grep "^total:"
167+ echo '```'
168+ echo ""
169+ echo "</details>"
170+ else
171+ echo ""
172+ echo "Coverage artifacts were not generated because the workflow failed before coverage collection completed."
173+ fi
134174 } > coverage-report.md
135175
136176 # ── Write step summary ────────────────────────────────────────────────
137177 - name : Write job summary
178+ if : always()
138179 run : cat coverage-report.md >> "$GITHUB_STEP_SUMMARY"
139180
140181 # ── Save PR number so the comment workflow can find the right PR ──────
141182 - name : Save PR number
142- if : github.event_name == 'pull_request'
183+ if : always() && github.event_name == 'pull_request'
143184 run : echo "${{ github.event.pull_request.number }}" > pr-number.txt
144185
145186 # ── Upload artifacts (report + pr-number for comment workflow) ────────
146187 - name : Upload coverage artifacts
188+ if : always()
147189 uses : actions/upload-artifact@v4
148190 with :
149191 name : coverage-report
150192 path : |
151193 coverage.out
152194 coverage.xml
195+ diff-cover.json
153196 coverage-report.md
154197 pr-number.txt
198+ if-no-files-found : warn
155199 retention-days : 14
156200
157201 - name : Upload coverage to Codecov
@@ -176,6 +220,6 @@ jobs:
176220 github.event_name == 'pull_request' &&
177221 steps.diff_coverage.outputs.diff_status == 'fail'
178222 run : |
179- echo "❌ Changed-line coverage ${{ steps.diff_coverage.outputs.diff_pct }}% is below the required ${{ env.THRESHOLD_DIFF }}%"
223+ echo "❌ Changed-line coverage ${{ steps.diff_coverage.outputs.diff_display }} is below the required ${{ env.THRESHOLD_DIFF }}%"
180224 exit 1
181225
0 commit comments