Skip to content

Refactor/operation scaffolding #3

Refactor/operation scaffolding

Refactor/operation scaffolding #3

name: PR Quality Gate
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
quality-gate:
name: Quality Gate
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run all quality checks
run: |
npm run lint
npm run typecheck
npm run test:coverage
continue-on-error: true
- name: Check coverage thresholds
id: coverage-check
run: |
if [ -f coverage/coverage-summary.json ]; then
node -e "
const fs = require('fs');
const coverage = JSON.parse(fs.readFileSync('coverage/coverage-summary.json', 'utf8'));
const total = coverage.total;
const failed = [];
if (total.lines.pct < 80) failed.push('lines: ' + total.lines.pct.toFixed(2) + '%');
if (total.functions.pct < 80) failed.push('functions: ' + total.functions.pct.toFixed(2) + '%');
if (total.branches.pct < 75) failed.push('branches: ' + total.branches.pct.toFixed(2) + '%');
if (total.statements.pct < 80) failed.push('statements: ' + total.statements.pct.toFixed(2) + '%');
if (failed.length > 0) {
console.error('❌ Coverage thresholds not met:', failed.join(', '));
process.exit(1);
} else {
console.log('✅ All coverage thresholds met!');
}
"
else
echo "⚠️ No coverage report found"
exit 1
fi
continue-on-error: true
- name: Comment PR with results
uses: actions/github-script@v7
if: always()
with:
script: |
const fs = require('fs');
let coverageComment = '## 📊 Quality Gate Results\n\n';
try {
if (fs.existsSync('coverage/coverage-summary.json')) {
const coverage = JSON.parse(fs.readFileSync('coverage/coverage-summary.json', 'utf8'));
const total = coverage.total;
const linesPassed = total.lines.pct >= 80;
const functionsPassed = total.functions.pct >= 80;
const branchesPassed = total.branches.pct >= 75;
const statementsPassed = total.statements.pct >= 80;
const allPassed = linesPassed && functionsPassed && branchesPassed && statementsPassed;
coverageComment += '### Code Coverage\n\n';
coverageComment += `| Metric | Coverage | Threshold | Status |\n`;
coverageComment += `|--------|----------|-----------|--------|\n`;
coverageComment += `| Lines | ${total.lines.pct.toFixed(2)}% | 80% | ${linesPassed ? '✅' : '❌'} |\n`;
coverageComment += `| Functions | ${total.functions.pct.toFixed(2)}% | 80% | ${functionsPassed ? '✅' : '❌'} |\n`;
coverageComment += `| Branches | ${total.branches.pct.toFixed(2)}% | 75% | ${branchesPassed ? '✅' : '❌'} |\n`;
coverageComment += `| Statements | ${total.statements.pct.toFixed(2)}% | 80% | ${statementsPassed ? '✅' : '❌'} |\n\n`;
if (allPassed) {
coverageComment += '✅ **All coverage thresholds met!**\n';
} else {
coverageComment += '❌ **Some coverage thresholds not met**\n';
}
} else {
coverageComment += '⚠️ No coverage report generated\n';
}
} catch (error) {
coverageComment += `⚠️ Error reading coverage report: ${error.message}\n`;
}
coverageComment += '\n---\n';
coverageComment += '*This comment is automatically generated by the PR Quality Gate workflow*';
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Quality Gate Results')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: coverageComment
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: coverageComment
});
}