Skip to content

Commit 05243c6

Browse files
atulmguptaCopilot
andcommitted
ci: publish detailed coverage reports for backend + frontend
Adds first-class coverage reporting to the CI workflow. Previously only a raw coverage.out was uploaded for backend; frontend coverage was configured locally but never wired into CI. Backend (.github/workflows/ci.yml backend job): - Test step now uses -covermode=atomic (race-safe and required for accurate concurrent coverage) - New 'Generate backend coverage reports' step runs after the test step (if: always() so it still publishes when a downstream step like Build fails) and produces: - coverage.txt (per-function, from 'go tool cover -func') - coverage.html (browsable annotated source, from 'go tool cover -html') - coverage-by-package.txt (statement coverage averaged per package, sorted high-to-low) via an awk roll-up - GITHUB_STEP_SUMMARY now shows the total-statements percent and a collapsible top-50 per-package table directly in the Actions run UI - Upload-artifact step renamed coverage → backend-coverage and now includes all four files Frontend (.github/workflows/ci.yml frontend job + web/vite.config.ts): - vitest config now emits html and json-summary reporters in addition to text + lcov - Test step now runs 'npx vitest run --coverage --reporter=verbose' so coverage/ is populated on every CI run - New 'Generate frontend coverage report' step parses coverage/coverage-summary.json with a tiny inline node script and writes a 4-row Markdown table (statements/branches/functions/lines) to GITHUB_STEP_SUMMARY - New 'Upload coverage' step publishes web/coverage/ (which includes the v8 HTML report at coverage/index.html, lcov.info for external tools, and the JSON summaries) as the frontend-coverage artifact .gitignore: adds coverage.txt, coverage-by-package.txt, web/coverage/ so local runs of the new reports don't accidentally get committed. Verified locally (Windows + Git for Windows awk/sort/grep): - go tool cover -func + the awk rollup produces correct per-package percentages against a subset coverage.out - vitest run --coverage produces coverage/coverage-summary.json with the expected 'total' shape - The node summary script renders the markdown table cleanly - The HTML reports (coverage.html and web/coverage/index.html) render correctly How to consume after this lands: - Quick read: open the CI run → 'Summary' tab shows backend total + frontend table inline - Deep dive: download the backend-coverage or frontend-coverage artifact, open coverage.html or coverage/index.html in a browser - External tools: lcov.info inside frontend-coverage is the standard format for Codecov/Coveralls/SonarQube if you choose to wire one of those up later Net change: 3 files, +82/-5. No behavioural change to gating — tests still pass/fail on their own merits; coverage publishing is metadata-only and uses if: always() so it never masks test failures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fa7440a commit 05243c6

3 files changed

Lines changed: 82 additions & 5 deletions

File tree

.github/workflows/ci.yml

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,44 @@ jobs:
5555
DATABASE_USER: test
5656
DATABASE_PASS: test
5757
DATABASE_NAME: teslasync_test
58-
run: go test -race -coverprofile=coverage.out ./...
58+
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...
59+
60+
- name: Generate backend coverage reports
61+
if: always() && hashFiles('coverage.out') != ''
62+
run: |
63+
go tool cover -func=coverage.out > coverage.txt
64+
go tool cover -html=coverage.out -o coverage.html
65+
# Per-package roll-up (statement coverage averaged across the package's funcs)
66+
awk '
67+
/^total:/ { next }
68+
{
69+
n = split($1, parts, "/")
70+
file = parts[n]; sub(/:.*$/, "", file)
71+
pkg = parts[1]
72+
for (i = 2; i < n; i++) pkg = pkg "/" parts[i]
73+
gsub("%", "", $3)
74+
sum[pkg] += $3
75+
cnt[pkg]++
76+
}
77+
END {
78+
for (p in sum) printf "%6.2f%% %s\n", sum[p]/cnt[p], p
79+
}
80+
' coverage.txt | sort -nr > coverage-by-package.txt
81+
TOTAL_LINE=$(grep '^total:' coverage.txt || echo "total: (statements) 0.0%")
82+
{
83+
echo "## 📊 Backend Coverage"
84+
echo ""
85+
echo "**$TOTAL_LINE**"
86+
echo ""
87+
echo "Full HTML + per-function reports are attached to this run as the **backend-coverage** artifact."
88+
echo ""
89+
echo "<details><summary>Per-package coverage (top 50)</summary>"
90+
echo ""
91+
echo '```'
92+
head -n 50 coverage-by-package.txt
93+
echo '```'
94+
echo "</details>"
95+
} >> "$GITHUB_STEP_SUMMARY"
5996
6097
- name: Architecture test (no forbidden import edges)
6198
run: |
@@ -91,10 +128,15 @@ jobs:
91128
CGO_ENABLED=0 go build -ldflags="-s -w" -o export-worker ./cmd/export-worker
92129
93130
- name: Upload coverage
131+
if: always()
94132
uses: actions/upload-artifact@v4
95133
with:
96-
name: coverage
97-
path: coverage.out
134+
name: backend-coverage
135+
path: |
136+
coverage.out
137+
coverage.html
138+
coverage.txt
139+
coverage-by-package.txt
98140
99141
frontend:
100142
name: Frontend (lint + test + build)
@@ -115,7 +157,37 @@ jobs:
115157
- name: Lint
116158
run: npm run lint
117159
- name: Test
118-
run: npx vitest run --reporter=verbose
160+
run: npx vitest run --coverage --reporter=verbose
161+
162+
- name: Generate frontend coverage report
163+
if: always() && hashFiles('web/coverage/coverage-summary.json') != ''
164+
run: |
165+
node -e '
166+
const fs = require("fs");
167+
const path = "coverage/coverage-summary.json";
168+
if (!fs.existsSync(path)) { process.exit(0); }
169+
const t = JSON.parse(fs.readFileSync(path, "utf8")).total;
170+
const out = [];
171+
out.push("## 📊 Frontend Coverage");
172+
out.push("");
173+
out.push("| Metric | Covered | Total | % |");
174+
out.push("|--------|--------:|------:|--:|");
175+
for (const k of ["statements","branches","functions","lines"]) {
176+
const m = t[k];
177+
out.push(`| ${k} | ${m.covered} | ${m.total} | ${m.pct}% |`);
178+
}
179+
out.push("");
180+
out.push("Browsable HTML report attached as the **frontend-coverage** artifact (open `coverage/index.html`).");
181+
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, out.join("\n") + "\n");
182+
'
183+
184+
- name: Upload coverage
185+
if: always()
186+
uses: actions/upload-artifact@v4
187+
with:
188+
name: frontend-coverage
189+
path: web/coverage/
190+
119191
- name: Build
120192
run: npm run build
121193

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ vendor/
2222
coverage/
2323
coverage.out
2424
coverage.html
25+
coverage.txt
26+
coverage-by-package.txt
2527

2628
# Node
2729
node_modules/
@@ -58,6 +60,9 @@ docs/.vitepress/dist/
5860
test-results/
5961
coverage.out
6062
coverage.html
63+
coverage.txt
64+
coverage-by-package.txt
65+
web/coverage/
6166
*.test.log
6267

6368
PROMPT_COMMANDS.txt

web/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export default defineConfig({
139139
setupFiles: ['./src/test-setup.ts'],
140140
include: ['src/**/*.test.{ts,tsx}'],
141141
coverage: {
142-
reporter: ['text', 'lcov'],
142+
reporter: ['text', 'lcov', 'html', 'json-summary'],
143143
include: ['src/**/*.{ts,tsx}'],
144144
exclude: ['src/**/*.test.{ts,tsx}', 'src/main.tsx'],
145145
},

0 commit comments

Comments
 (0)