1+ name : Coverage Delta
2+
3+ on :
4+ pull_request :
5+ branches : [main]
6+
7+ jobs :
8+ compare-coverage :
9+ runs-on : ubuntu-latest
10+ steps :
11+ - uses : actions/setup-node@v3
12+ with :
13+ node-version : 24
14+
15+ - name : Checkout Main
16+ uses : actions/checkout@v4
17+ with :
18+ ref : main
19+
20+ - name : Install & Test (Main)
21+ run : |
22+ npm ci
23+ npm run coverage
24+ mv ./coverage/coverage-summary.json ./coverage-main-summary.json
25+
26+ - name : Checkout PR Branch
27+ uses : actions/checkout@v4
28+ with :
29+ clean : true
30+
31+ - name : Install & Test (PR)
32+ run : |
33+ npm ci
34+ npm run coverage
35+
36+ - name : Report Coverage Delta
37+ uses : actions/github-script@v6
38+ with :
39+ script : |
40+ const fs = require('fs');
41+
42+ const loadSummary = (path) => {
43+ try {
44+ return JSON.parse(fs.readFileSync(path, 'utf8'));
45+ } catch (error) {
46+ console.log(`Could not load ${path}: ${error.message}`);
47+ return null;
48+ }
49+ };
50+
51+ const mainSummary = loadSummary('./coverage-main-summary.json');
52+ const prSummary = loadSummary('./coverage/coverage-summary.json');
53+
54+ if (!mainSummary || !prSummary) {
55+ core.setFailed('Could not load coverage summaries for comparison.');
56+ return;
57+ }
58+
59+ const metrics = ['lines', 'statements', 'functions', 'branches'];
60+
61+ const getPct = (summary, metric) => summary.total[metric].pct;
62+
63+ let markdown = `### 🧪 Code Coverage Delta\n\n`;
64+ markdown += `| Metric | Main | PR | Delta |\n`;
65+ markdown += `| :--- | :---: | :---: | :---: |\n`;
66+
67+ metrics.forEach(metric => {
68+ const oldPct = getPct(mainSummary, metric);
69+ const newPct = getPct(prSummary, metric);
70+ const diff = (newPct - oldPct).toFixed(2);
71+
72+ let icon = '';
73+ if (diff > 0) icon = '🟢';
74+ else if (diff < 0) icon = '🔴';
75+ else icon = '⚪️';
76+
77+ // Format with + sign for positive numbers
78+ const diffStr = diff > 0 ? `+${diff}%` : `${diff}%`;
79+
80+ markdown += `| **${metric}** | ${oldPct}% | ${newPct}% | ${icon} ${diffStr} |\n`;
81+ });
82+
83+ markdown += `\n_Generated by c8 and GitHub Actions_`;
84+
85+ const { owner, repo } = context.repo;
86+ const issue_number = context.issue.number;
87+
88+ const comments = await github.rest.issues.listComments({
89+ owner,
90+ repo,
91+ issue_number,
92+ });
93+
94+ const botComment = comments.data.find(c =>
95+ c.body.includes('### 🧪 Code Coverage Delta') &&
96+ c.user.type === 'Bot'
97+ );
98+
99+ if (botComment) {
100+ await github.rest.issues.updateComment({
101+ owner,
102+ repo,
103+ comment_id: botComment.id,
104+ body: markdown
105+ });
106+ } else {
107+ await github.rest.issues.createComment({
108+ owner,
109+ repo,
110+ issue_number,
111+ body: markdown
112+ });
113+ }
0 commit comments