66
77permissions :
88 contents : read
9- pull-requests : write
10- checks : write
9+ actions : write
1110
1211jobs :
1312 lint :
@@ -179,150 +178,6 @@ jobs:
179178 client/coverage/coverage-summary.json
180179 client/coverage/lcov.info
181180
182- coverage-comment :
183- name : Post Coverage Comment
184- runs-on : ubuntu-latest
185- needs : [test-backend, test-frontend]
186- if : github.event_name == 'pull_request' && always()
187-
188- steps :
189- - name : Checkout code
190- uses : actions/checkout@v3
191-
192- - name : Download backend coverage
193- uses : actions/download-artifact@v4
194- with :
195- name : backend-coverage
196- path : backend-coverage
197- continue-on-error : true
198-
199- - name : Download frontend coverage
200- uses : actions/download-artifact@v4
201- with :
202- name : frontend-coverage
203- path : frontend-coverage
204- continue-on-error : true
205-
206- - name : Generate Coverage Report Comment
207- id : coverage
208- uses : actions/github-script@v6
209- with :
210- github-token : ${{ secrets.GITHUB_TOKEN }}
211- script : |
212- const fs = require('fs');
213-
214- // Read coverage summaries
215- let backendCoverage = { total: { lines: { pct: 0 }, statements: { pct: 0 }, functions: { pct: 0 }, branches: { pct: 0 } } };
216- let frontendCoverage = { total: { lines: { pct: 0 }, statements: { pct: 0 }, functions: { pct: 0 }, branches: { pct: 0 } } };
217- let backendAvailable = false;
218- let frontendAvailable = false;
219-
220- try {
221- const backendSummary = fs.readFileSync('backend-coverage/coverage-summary.json', 'utf8');
222- backendCoverage = JSON.parse(backendSummary);
223- backendAvailable = true;
224- } catch (e) {
225- console.log('Could not read backend coverage:', e);
226- }
227-
228- try {
229- const frontendSummary = fs.readFileSync('frontend-coverage/coverage-summary.json', 'utf8');
230- frontendCoverage = JSON.parse(frontendSummary);
231- frontendAvailable = true;
232- } catch (e) {
233- console.log('Could not read frontend coverage:', e);
234- }
235-
236- // Format coverage badge color
237- function getBadgeColor(percentage) {
238- if (percentage >= 80) return '🟢';
239- if (percentage >= 70) return '🟡';
240- return '🔴';
241- }
242-
243- // Format percentage
244- function formatPct(value) {
245- return typeof value === 'number' ? value.toFixed(2) : '0.00';
246- }
247-
248- // Create comment body
249- const backendTotal = backendCoverage.total || backendCoverage;
250- const frontendTotal = frontendCoverage.total || frontendCoverage;
251-
252- let comment = `## 📊 Test Coverage Report\n\n`;
253-
254- if (backendAvailable) {
255- comment += `### Backend Coverage
256- | Type | Coverage | Status |
257- |------|----------|--------|
258- | Lines | ${formatPct(backendTotal.lines.pct)}% | ${getBadgeColor(backendTotal.lines.pct)} |
259- | Statements | ${formatPct(backendTotal.statements.pct)}% | ${getBadgeColor(backendTotal.statements.pct)} |
260- | Functions | ${formatPct(backendTotal.functions.pct)}% | ${getBadgeColor(backendTotal.functions.pct)} |
261- | Branches | ${formatPct(backendTotal.branches.pct)}% | ${getBadgeColor(backendTotal.branches.pct)} |
262-
263- `;
264- } else {
265- comment += `### Backend Coverage\n⚠️ Coverage data not available (tests may have failed)\n\n`;
266- }
267-
268- if (frontendAvailable) {
269- comment += `### Frontend Coverage
270- | Type | Coverage | Status |
271- |------|----------|--------|
272- | Lines | ${formatPct(frontendTotal.lines.pct)}% | ${getBadgeColor(frontendTotal.lines.pct)} |
273- | Statements | ${formatPct(frontendTotal.statements.pct)}% | ${getBadgeColor(frontendTotal.statements.pct)} |
274- | Functions | ${formatPct(frontendTotal.functions.pct)}% | ${getBadgeColor(frontendTotal.functions.pct)} |
275- | Branches | ${formatPct(frontendTotal.branches.pct)}% | ${getBadgeColor(frontendTotal.branches.pct)} |
276-
277- `;
278- } else {
279- comment += `### Frontend Coverage\n⚠️ Coverage data not available (tests may have failed)\n\n`;
280- }
281-
282- // Check if thresholds are met
283- const backendMeetsThreshold = backendAvailable && backendTotal.lines.pct >= 70;
284- const frontendMeetsThreshold = frontendAvailable && frontendTotal.lines.pct >= 70;
285-
286- comment += `### Coverage Requirements\n`;
287- comment += `- **Minimum threshold:** 70% line coverage\n`;
288- comment += `- **Backend:** ${backendMeetsThreshold ? '✅ Passes' : backendAvailable ? '❌ Fails (below 70%)' : '⚠️ Not available'}\n`;
289- comment += `- **Frontend:** ${frontendMeetsThreshold ? '✅ Passes' : frontendAvailable ? '❌ Fails (below 70%)' : '⚠️ Not available'}\n\n`;
290-
291- if (!backendMeetsThreshold || !frontendMeetsThreshold) {
292- comment += `> ⚠️ **This PR cannot be merged until both frontend and backend coverage meet the 70% threshold.**\n\n`;
293- }
294-
295- comment += `---\n*Coverage report generated for commit ${context.sha.substring(0, 7)}*`;
296-
297- // Find existing comment
298- const { data: comments } = await github.rest.issues.listComments({
299- owner: context.repo.owner,
300- repo: context.repo.repo,
301- issue_number: context.issue.number,
302- });
303-
304- const botComment = comments.find(comment =>
305- comment.user.type === 'Bot' &&
306- comment.body.includes('Test Coverage Report')
307- );
308-
309- // Update or create comment
310- if (botComment) {
311- await github.rest.issues.updateComment({
312- owner: context.repo.owner,
313- repo: context.repo.repo,
314- comment_id: botComment.id,
315- body: comment
316- });
317- } else {
318- await github.rest.issues.createComment({
319- owner: context.repo.owner,
320- repo: context.repo.repo,
321- issue_number: context.issue.number,
322- body: comment
323- });
324- }
325-
326181 # This job is required for branch protection rules
327182 # It will only succeed if all tests and linting pass
328183 check-all :
@@ -352,4 +207,4 @@ jobs:
352207 fi
353208
354209 echo ""
355- echo "✅ All checks passed successfully!"
210+ echo "✅ All checks passed successfully!"
0 commit comments