@@ -84,74 +84,103 @@ 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 -c "import json; report=json.load(open('diff-cover.json', encoding='utf-8')); num_changed_lines=report.get('num_changed_lines', 0); total_num_lines=report.get('total_num_lines', 0); total_percent_covered=report.get('total_percent_covered', 'N/A'); print('N/A' if num_changed_lines > 0 and total_num_lines == 0 else total_percent_covered)")
93+ else
94+ DIFF_PCT="N/A"
95+ fi
96+
97+ if [ "$DIFF_PCT" = "N/A" ]; then
98+ DIFF_DISPLAY="N/A"
99+ else
100+ DIFF_DISPLAY="${DIFF_PCT}%"
101+ fi
102+
91103 echo "diff_pct=${DIFF_PCT}" >> "$GITHUB_OUTPUT"
104+ echo "diff_display=${DIFF_DISPLAY}" >> "$GITHUB_OUTPUT"
92105 echo "$DIFF_OUTPUT"
93106
94- if [ "$DIFF_EXIT" -ne 0 ]; then
107+ if [ "$DIFF_EXIT" -ne 0 ] || [ "$DIFF_PCT" = "N/A" ] ; then
95108 echo "diff_status=fail" >> "$GITHUB_OUTPUT"
109+
110+ if [ "$DIFF_PCT" = "N/A" ]; then
111+ echo "❌ Changed-line coverage could not be computed because diff-cover found changed lines without matching coverage information."
112+ fi
96113 else
97114 echo "diff_status=pass" >> "$GITHUB_OUTPUT"
98115 fi
99116
100117 # ── Generate per-package coverage table ──────────────────────────────
101118 - name : Generate coverage report markdown
119+ if : always()
102120 run : |
103121 {
104122 echo "## 📊 Code Coverage Report"
105123 echo ""
106124
107125 TOTAL="${{ steps.total_coverage.outputs.total }}"
108126 STATUS="${{ steps.total_coverage.outputs.status }}"
127+ [ -n "$TOTAL" ] || TOTAL="N/A"
109128 [ "$STATUS" = "pass" ] && ICON="✅" || ICON="❌"
110129
111130 echo "| Metric | Coverage | Threshold | Status |"
112131 echo "|--------|----------|-----------|--------|"
113132 echo "| Overall | **${TOTAL}%** | ${{ env.THRESHOLD_TOTAL }}% | ${ICON} |"
114133
115134 if [ "${{ github.event_name }}" = "pull_request" ]; then
116- DIFF_PCT ="${{ steps.diff_coverage.outputs.diff_pct }}"
135+ DIFF_DISPLAY ="${{ steps.diff_coverage.outputs.diff_display }}"
117136 DIFF_STATUS="${{ steps.diff_coverage.outputs.diff_status }}"
137+ [ -n "$DIFF_DISPLAY" ] || DIFF_DISPLAY="N/A"
118138 [ "$DIFF_STATUS" = "pass" ] && DIFF_ICON="✅" || DIFF_ICON="❌"
119- echo "| Changed lines | **${DIFF_PCT}% ** | ${{ env.THRESHOLD_DIFF }}% | ${DIFF_ICON} |"
139+ echo "| Changed lines | **${DIFF_DISPLAY} ** | ${{ env.THRESHOLD_DIFF }}% | ${DIFF_ICON} |"
120140 fi
121141
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>"
142+ if [ -f coverage.out ]; then
143+ echo ""
144+ echo "<details>"
145+ echo "<summary>📦 Per-package breakdown</summary>"
146+ echo ""
147+ echo '```'
148+ go tool cover -func=coverage.out | grep -v "^total:" | \
149+ awk '{printf "%-80s %s\n", $1, $3}' | sort
150+ echo ""
151+ go tool cover -func=coverage.out | grep "^total:"
152+ echo '```'
153+ echo ""
154+ echo "</details>"
155+ else
156+ echo ""
157+ echo "Coverage artifacts were not generated because the workflow failed before coverage collection completed."
158+ fi
134159 } > coverage-report.md
135160
136161 # ── Write step summary ────────────────────────────────────────────────
137162 - name : Write job summary
163+ if : always()
138164 run : cat coverage-report.md >> "$GITHUB_STEP_SUMMARY"
139165
140166 # ── Save PR number so the comment workflow can find the right PR ──────
141167 - name : Save PR number
142- if : github.event_name == 'pull_request'
168+ if : always() && github.event_name == 'pull_request'
143169 run : echo "${{ github.event.pull_request.number }}" > pr-number.txt
144170
145171 # ── Upload artifacts (report + pr-number for comment workflow) ────────
146172 - name : Upload coverage artifacts
173+ if : always()
147174 uses : actions/upload-artifact@v4
148175 with :
149176 name : coverage-report
150177 path : |
151178 coverage.out
152179 coverage.xml
180+ diff-cover.json
153181 coverage-report.md
154182 pr-number.txt
183+ if-no-files-found : warn
155184 retention-days : 14
156185
157186 - name : Upload coverage to Codecov
@@ -176,6 +205,6 @@ jobs:
176205 github.event_name == 'pull_request' &&
177206 steps.diff_coverage.outputs.diff_status == 'fail'
178207 run : |
179- echo "❌ Changed-line coverage ${{ steps.diff_coverage.outputs.diff_pct }}% is below the required ${{ env.THRESHOLD_DIFF }}%"
208+ echo "❌ Changed-line coverage ${{ steps.diff_coverage.outputs.diff_display }} is below the required ${{ env.THRESHOLD_DIFF }}%"
180209 exit 1
181210
0 commit comments