@@ -134,6 +134,35 @@ jobs:
134134 REL_EFF_FMT=$(fmt_debt "$(get reliability_remediation_effort)")
135135 SEC_EFF_FMT=$(fmt_debt "$(get security_remediation_effort)")
136136
137+ # ===== Backend-Coverage direkt aus coverage.xml =====
138+ COV_FILE="project/backend/coverage.xml"
139+ if [ -f "${COV_FILE}" ]; then
140+ COV_DATA=$(python3 - "${COV_FILE}" <<'PYEOF'
141+ import sys, xml.etree.ElementTree as ET
142+ root = ET.parse(sys.argv[1]).getroot()
143+ lr = float(root.get('line-rate', '0'))
144+ br = float(root.get('branch-rate', '0'))
145+ lv = int(root.get('lines-valid', '0'))
146+ lc = int(root.get('lines-covered', '0'))
147+ bv = int(root.get('branches-valid', '0'))
148+ bc = int(root.get('branches-covered', '0'))
149+ total = lv + bv
150+ covered = lc + bc
151+ overall = (covered / total * 100) if total > 0 else 0.0
152+ uncov_lines = lv - lc
153+ uncov_branches = bv - bc
154+ print(f"{overall:.1f} {lr*100:.1f} {br*100:.1f} {lv} {lc} {bv} {bc} {uncov_lines} {uncov_branches}")
155+ PYEOF
156+ )
157+ read -r COV_OVERALL COV_LINE COV_BRANCH LINES_VALID LINES_COVERED BRANCHES_VALID BRANCHES_COVERED UNCOVERED_LINES UNCOVERED_BRANCHES <<< "${COV_DATA}"
158+ echo "Backend-Coverage aus coverage.xml: ${COV_OVERALL}% (Line ${COV_LINE}%, Branch ${COV_BRANCH}%)"
159+ else
160+ echo "::warning::coverage.xml nicht gefunden bei ${COV_FILE}"
161+ COV_OVERALL="-"; COV_LINE="-"; COV_BRANCH="-"
162+ LINES_VALID="-"; LINES_COVERED="-"; BRANCHES_VALID="-"; BRANCHES_COVERED="-"
163+ UNCOVERED_LINES="-"; UNCOVERED_BRANCHES="-"
164+ fi
165+
137166 # ===== Summary schreiben =====
138167 {
139168 echo "# SonarCloud – Voller Projekt-Report"
@@ -158,16 +187,22 @@ jobs:
158187 echo "| Statements | $(get statements) |"
159188 echo ""
160189
161- # --- Coverage ---
162- echo "## Test-Coverage"
190+ # --- Coverage (aus pytest-cov coverage.xml, nicht aus Sonar-API) ---
191+ echo "## Test-Coverage (Backend / Python)"
192+ echo ""
193+ echo "_Werte direkt aus \`backend/coverage.xml\` von pytest-cov gelesen._"
163194 echo ""
164195 echo "| Metrik | Wert |"
165196 echo "|---|---:|"
166- echo "| Coverage (gesamt) | $(get coverage)% |"
167- echo "| Line Coverage | $(get line_coverage)% |"
168- echo "| Branch Coverage | $(get branch_coverage)% |"
169- echo "| Deckbare Zeilen | $(get lines_to_cover) |"
170- echo "| Ungedeckte Zeilen | $(get uncovered_lines) |"
197+ echo "| Coverage (gesamt) | ${COV_OVERALL}% |"
198+ echo "| Line Coverage | ${COV_LINE}% |"
199+ echo "| Branch Coverage | ${COV_BRANCH}% |"
200+ echo "| Deckbare Zeilen | ${LINES_VALID} |"
201+ echo "| Gedeckte Zeilen | ${LINES_COVERED} |"
202+ echo "| Ungedeckte Zeilen | ${UNCOVERED_LINES} |"
203+ echo "| Deckbare Branches | ${BRANCHES_VALID} |"
204+ echo "| Gedeckte Branches | ${BRANCHES_COVERED} |"
205+ echo "| Ungedeckte Branches | ${UNCOVERED_BRANCHES} |"
171206 echo ""
172207
173208 # --- Duplikate ---
@@ -233,22 +268,38 @@ jobs:
233268
234269 {
235270 echo ""
236- echo "## Top-10 Dateien mit schlechtester Coverage"
271+ echo "## Top-10 Dateien mit schlechtester Coverage (Backend)"
272+ echo ""
273+ echo "_Aus \`backend/coverage.xml\`, sortiert aufsteigend nach Coverage._"
237274 echo ""
238- echo "| Datei | Coverage | Ungedeckte Zeilen | LoC |"
275+ echo "| Datei | Coverage | Gedeckte / Deckbare | Ungedeckte Zeilen |"
239276 echo "|---|---:|---:|---:|"
240277 } >> "$OUT"
241278
242- curl -s -u "${SONAR_TOKEN}:" \
243- "${SONAR_HOST}/api/measures/component_tree?component=${KEY}&metricKeys=coverage,uncovered_lines,ncloc&qualifiers=FIL&ps=10&s=metric&metricSort=coverage&asc=true" \
244- | jq -r '.components[]?
245- | select(.measures[]? | select(.metric=="coverage"))
246- | (.path // .key) as $p
247- | ((.measures[]? | select(.metric=="coverage") | .value) // "-") as $cov
248- | ((.measures[]? | select(.metric=="uncovered_lines") | .value) // "-") as $unc
249- | ((.measures[]? | select(.metric=="ncloc") | .value) // "-") as $n
250- | "| " + $p + " | " + $cov + "% | " + $unc + " | " + $n + " |"' \
251- >> "$OUT"
279+ if [ -f "${COV_FILE}" ]; then
280+ python3 - "${COV_FILE}" >> "$OUT" <<'PYEOF'
281+ import sys, xml.etree.ElementTree as ET
282+ root = ET.parse(sys.argv[1]).getroot()
283+ rows = []
284+ for cls in root.iter('class'):
285+ fname = cls.get('filename', '')
286+ lines = cls.find('lines')
287+ if lines is None:
288+ continue
289+ total = len(lines.findall('line'))
290+ if total == 0:
291+ continue
292+ covered = sum(1 for l in lines.findall('line') if int(l.get('hits', '0')) > 0)
293+ uncov = total - covered
294+ pct = covered / total * 100
295+ rows.append((pct, fname, covered, total, uncov))
296+ rows.sort(key=lambda r: (r[0], -r[3]))
297+ for pct, fname, cov, total, uncov in rows[:10]:
298+ print(f"| {fname} | {pct:.1f}% | {cov} / {total} | {uncov} |")
299+ PYEOF
300+ else
301+ echo "_coverage.xml nicht gefunden._" >> "$OUT"
302+ fi
252303
253304 {
254305 echo ""
0 commit comments