This repository was archived by the owner on Jun 18, 2026. It is now read-only.
Add CleanroomEnvironmentDriftAdvisor: per-zone ISO 14644 drift watcher #669
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Code Coverage | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| branches: [master] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| coverage: | |
| name: Test Coverage | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run tests with coverage | |
| run: npx jest --verbose --coverage --coverageReporters=text --coverageReporters=lcov --coverageReporters=json-summary | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| files: ./coverage/lcov.info | |
| flags: unittests | |
| fail_ci_if_error: false | |
| verbose: true | |
| - name: Coverage analysis | |
| id: coverage | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const summary = require('./coverage/coverage-summary.json'); | |
| const total = summary.total; | |
| // Overall metrics | |
| const metrics = { | |
| statements: total.statements.pct, | |
| branches: total.branches.pct, | |
| functions: total.functions.pct, | |
| lines: total.lines.pct | |
| }; | |
| // Badge color | |
| const pct = metrics.lines; | |
| let color = 'red'; | |
| if (pct >= 90) color = 'brightgreen'; | |
| else if (pct >= 80) color = 'green'; | |
| else if (pct >= 70) color = 'yellowgreen'; | |
| else if (pct >= 60) color = 'yellow'; | |
| else if (pct >= 50) color = 'orange'; | |
| // Find files with low coverage (< 50% lines) | |
| const lowCovFiles = []; | |
| for (const [file, data] of Object.entries(summary)) { | |
| if (file === 'total') continue; | |
| if (data.lines.pct < 50 && data.lines.total > 0) { | |
| lowCovFiles.push({ | |
| file: file.replace(process.cwd() + '/', ''), | |
| lines: data.lines.pct, | |
| branches: data.branches.pct, | |
| functions: data.functions.pct, | |
| uncovered: data.lines.total - data.lines.covered | |
| }); | |
| } | |
| } | |
| lowCovFiles.sort((a, b) => a.lines - b.lines); | |
| // Find files with zero coverage | |
| const zeroCovFiles = lowCovFiles.filter(f => f.lines === 0); | |
| const partialCovFiles = lowCovFiles.filter(f => f.lines > 0); | |
| // Write outputs | |
| const append = (k, v) => fs.appendFileSync(process.env.GITHUB_OUTPUT, k + '=' + v + '\n'); | |
| append('lines_pct', pct); | |
| append('badge_color', color); | |
| append('statements_pct', metrics.statements); | |
| append('branches_pct', metrics.branches); | |
| append('functions_pct', metrics.functions); | |
| append('low_cov_count', lowCovFiles.length); | |
| // Write detailed report for step summary | |
| const report = { | |
| metrics, | |
| lowCovFiles: lowCovFiles.slice(0, 20), | |
| zeroCovCount: zeroCovFiles.length, | |
| partialCovCount: partialCovFiles.length, | |
| totalFiles: Object.keys(summary).length - 1, | |
| coveredAbove80: Object.entries(summary) | |
| .filter(([k, v]) => k !== 'total' && v.lines.total > 0 && v.lines.pct >= 80).length | |
| }; | |
| fs.writeFileSync('/tmp/coverage-report.json', JSON.stringify(report, null, 2)); | |
| " | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-report | |
| path: coverage/ | |
| retention-days: 30 | |
| - name: Coverage summary | |
| run: | | |
| node -e " | |
| const report = require('/tmp/coverage-report.json'); | |
| const m = report.metrics; | |
| const lines = []; | |
| lines.push('## 📊 Code Coverage Report'); | |
| lines.push(''); | |
| lines.push('| Metric | Coverage |'); | |
| lines.push('|--------|----------|'); | |
| lines.push('| Statements | ' + m.statements + '% |'); | |
| lines.push('| Branches | ' + m.branches + '% |'); | |
| lines.push('| Functions | ' + m.functions + '% |'); | |
| lines.push('| Lines | ' + m.lines + '% |'); | |
| lines.push(''); | |
| lines.push('**Files:** ' + report.totalFiles + ' tracked · ' + report.coveredAbove80 + ' above 80% · ' + report.zeroCovCount + ' with zero coverage'); | |
| if (report.lowCovFiles.length > 0) { | |
| lines.push(''); | |
| lines.push('<details>'); | |
| lines.push('<summary>⚠️ ' + report.lowCovFiles.length + ' files below 50% line coverage</summary>'); | |
| lines.push(''); | |
| lines.push('| File | Lines | Branches | Functions | Uncovered Lines |'); | |
| lines.push('|------|-------|----------|-----------|-----------------|'); | |
| for (const f of report.lowCovFiles) { | |
| lines.push('| \`' + f.file + '\` | ' + f.lines + '% | ' + f.branches + '% | ' + f.functions + '% | ' + f.uncovered + ' |'); | |
| } | |
| lines.push(''); | |
| lines.push('</details>'); | |
| } | |
| require('fs').appendFileSync(process.env.GITHUB_STEP_SUMMARY, lines.join('\n') + '\n'); | |
| " | |
| - name: PR coverage comment | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const report = JSON.parse(fs.readFileSync('/tmp/coverage-report.json', 'utf8')); | |
| const m = report.metrics; | |
| let body = `## 📊 Coverage Report\n\n`; | |
| body += `| Metric | Coverage |\n|--------|----------|\n`; | |
| body += `| Statements | ${m.statements}% |\n`; | |
| body += `| Branches | ${m.branches}% |\n`; | |
| body += `| Functions | ${m.functions}% |\n`; | |
| body += `| Lines | ${m.lines}% |\n\n`; | |
| body += `**${report.totalFiles}** files tracked · **${report.coveredAbove80}** above 80%`; | |
| if (report.lowCovFiles.length > 0) { | |
| body += `\n\n<details><summary>⚠️ ${report.lowCovFiles.length} files below 50% coverage</summary>\n\n`; | |
| body += `| File | Lines | Uncovered |\n|------|-------|-----------|\n`; | |
| for (const f of report.lowCovFiles.slice(0, 15)) { | |
| body += `| \`${f.file}\` | ${f.lines}% | ${f.uncovered} |\n`; | |
| } | |
| body += `\n</details>`; | |
| } | |
| // Upsert: find existing bot comment and update, or create new | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| per_page: 50 | |
| }); | |
| const botComment = comments.find(c => | |
| c.user.type === 'Bot' && c.body.includes('📊 Coverage Report') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body | |
| }); | |
| } |