Files
eSIM-Tools/.github/workflows/quality-gate.yml
Abner 1b36a7e1c1 feat: 新增完整的 GitHub Actions 工作流自动化系统
- 添加质量守卫工作流,负责代码推送时的质量检查、安全扫描、单元测试和构建验证
- 实现 CI 自动修复工作流,当质量检查失败时自动尝试重新运行各项检查
- 引入质量自动改进工作流,检测评级不足时自动应用代码格式化和 ESLint 修复
- 建立清理工作流历史定时任务,自动删除三天前的工作流运行记录和附件
- 设计智能评分机制,根据各项检查结果计算综合质量分数并生成评级报告
- 集成 Artifacts 管理,自动上传质量报告、修复结果和改进补丁供后续分析使用
2025-11-24 20:13:33 +08:00

162 lines
4.8 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: 质量守卫
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
jobs:
quality-gate:
name: 质量与构建校验
runs-on: ubuntu-latest
steps:
- name: 拉取仓库代码
uses: actions/checkout@v4
- name: 安装 Node.js 环境
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: 安装依赖
run: |
if ! npm ci; then
echo '检测到锁文件与环境不一致,改用 npm install 同步依赖~'
npm install
fi
- name: 运行质量检查脚本
id: quality
run: npm run quality-check
continue-on-error: true
- name: 运行安全检查脚本
id: security
run: npm run security-check
continue-on-error: true
- name: 单元测试
id: test
run: npm test
continue-on-error: true
- name: 构建生产产物
id: build
run: npm run build
continue-on-error: true
- name: 汇总质量评分
if: always()
id: rating
env:
QUALITY_OUTCOME: ${{ steps.quality.outcome }}
SECURITY_OUTCOME: ${{ steps.security.outcome }}
TEST_OUTCOME: ${{ steps.test.outcome }}
BUILD_OUTCOME: ${{ steps.build.outcome }}
run: |
node <<'NODE'
const fs = require('fs');
const qualityOutcome = process.env.QUALITY_OUTCOME ?? 'success';
const securityOutcome = process.env.SECURITY_OUTCOME ?? 'success';
const testOutcome = process.env.TEST_OUTCOME ?? 'success';
const buildOutcome = process.env.BUILD_OUTCOME ?? 'success';
const threshold = 90;
const breakdown = [];
let score = 100;
if (qualityOutcome !== 'success') {
breakdown.push({ metric: 'quality-check', penalty: 30 });
score -= 30;
}
if (securityOutcome !== 'success') {
breakdown.push({ metric: 'security-check', penalty: 25 });
score -= 25;
}
if (testOutcome !== 'success') {
breakdown.push({ metric: 'test', penalty: 25 });
score -= 25;
}
if (buildOutcome !== 'success') {
breakdown.push({ metric: 'build', penalty: 20 });
score -= 20;
}
score = Math.max(0, score);
let rating = 'F';
if (score >= 95) rating = 'S';
else if (score >= 90) rating = 'A';
else if (score >= 80) rating = 'B';
else if (score >= 70) rating = 'C';
else if (score >= 60) rating = 'D';
const payload = {
timestamp: new Date().toISOString(),
threshold,
score,
rating,
breakdown,
outcomes: {
quality: qualityOutcome,
security: securityOutcome,
test: testOutcome,
build: buildOutcome
}
};
fs.writeFileSync('quality-report.json', JSON.stringify(payload, null, 2));
fs.writeFileSync('quality-summary.txt', `rating=${payload.rating}\nscore=${payload.score}\nthreshold=${payload.threshold}`);
console.log(`质量评级:${payload.rating}(得分 ${payload.score}/100`);
console.log('扣分明细:', JSON.stringify(breakdown, null, 2));
if (process.env.GITHUB_OUTPUT) {
const needsImprove = payload.score < threshold ? 'yes' : 'no';
const lines = [
`rating=${payload.rating}`,
`score=${payload.score}`,
`threshold=${threshold}`,
`need_improvement=${needsImprove}`
];
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${lines.join('\n')}\n`);
}
NODE
- name: 上传质量报告
if: always()
uses: actions/upload-artifact@v4
with:
name: quality-report
path: |
quality-report.json
quality-summary.txt
retention-days: 7
- name: 若质量检查失败则终止
if: steps.quality.outcome == 'failure'
run: |
echo 'quality-check 未通过,请修复后重试。'
exit 1
- name: 若安全检查失败则终止
if: steps.security.outcome == 'failure'
run: |
echo 'security-check 未通过,请修复后重试。'
exit 1
- name: 若测试失败则终止
if: steps.test.outcome == 'failure'
run: |
echo 'Jest 测试未通过,请修复后重试。'
exit 1
- name: 若构建失败则终止
if: steps.build.outcome == 'failure'
run: |
echo '构建失败,请查看日志。'
exit 1