GitHub Actions覆盖率报告MongoDB存储、查看及报错排查
问题解决与实现方案
一、当前报错的解决办法
报错提示rootDirectory undefined,核心问题是代码参数使用错误+逻辑bug,直接修改store-coverage.js如下:
const fs = require('fs'); const archiver = require('archiver'); const MongoClient = require('mongodb').MongoClient; const uri = process.env.MONGODB_URI; const client = new MongoClient(uri); // 用GitHub Run ID作为唯一标识,避免同秒多次运行冲突 const uniqueId = process.env.GITHUB_RUN_ID || new Date().toISOString(); async function storeCoverageReport() { try { await client.connect(); const db = client.db('coverage-reports'); const collection = db.collection('reports'); const rootDirectory = 'coverage'; // 先校验目录是否存在 if (!fs.existsSync(rootDirectory)) { throw new Error(`覆盖率目录 ${rootDirectory} 不存在,请检查测试命令是否生成覆盖率`); } const coverageReportBuffer = await compressCoverageFolder(rootDirectory); // 存入MongoDB,附带元信息方便后续查询 const result = await collection.insertOne({ _id: uniqueId, runId: process.env.GITHUB_RUN_ID, branch: process.env.GITHUB_REF_NAME, createdAt: new Date(), coverageReport: coverageReportBuffer, }); console.log(`覆盖率报告已存储,ID: ${result.insertedId}`); } catch (err) { console.error('存储失败:', err); process.exit(1); } finally { await client.close(); } } // 修复压缩逻辑,用Promise正确封装流处理 async function compressCoverageFolder(rootDirectory) { return new Promise((resolve, reject) => { const archive = archiver('zip', { zlib: { level: 9 } }); const chunks = []; archive.on('data', (chunk) => chunks.push(chunk)); archive.on('end', () => resolve(Buffer.concat(chunks))); archive.on('error', reject); archive.directory(rootDirectory, false); archive.finalize(); }); } storeCoverageReport();
同时调整工作流步骤:
- 将
Install MongoDB dependencies步骤改为安装所有依赖:- name: Install dependencies for coverage storage run: npm install mongodb archiver - 确保
Run tests命令生成覆盖率(比如Jest需要加--coverage参数):- name: Run tests run: npm run test -- --coverage --json --outputFile=test-results.json continue-on-error: true
二、完整实现方案(存储+浏览器查看)
1. 存储流程
- 测试运行生成
coverage目录 - 压缩目录为ZIP格式Buffer
- 连同GitHub Run ID、分支名、时间戳等元数据存入MongoDB
- 保留工作流中的
actions/upload-artifact作为备份
2. 浏览器查看报告的实现
需要一个简单的Web服务读取MongoDB中的ZIP文件,解压后提供静态访问:
const express = require('express'); const MongoClient = require('mongodb').MongoClient; const extract = require('extract-zip'); const fs = require('fs'); const path = require('path'); const os = require('os'); const app = express(); const uri = process.env.MONGODB_URI; const client = new MongoClient(uri); // 列出所有报告元数据 app.get('/reports', async (req, res) => { try { await client.connect(); const reports = await client.db('coverage-reports').collection('reports') .find({}, { projection: { _id: 1, branch: 1, createdAt: 1 } }) .sort({ createdAt: -1 }).toArray(); res.json(reports); } catch (err) { res.status(500).send(err.message); } finally { await client.close(); } }); // 查看指定ID的报告 app.get('/reports/:id', async (req, res) => { const reportId = req.params.id; try { await client.connect(); const report = await client.db('coverage-reports').collection('reports').findOne({ _id: reportId }); if (!report) return res.status(404).send('报告不存在'); // 创建临时目录存放解压后的文件 const tempDir = path.join(os.tmpdir(), `cov-${reportId}`); if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir); // 写入ZIP文件并解压 const zipPath = path.join(tempDir, 'report.zip'); fs.writeFileSync(zipPath, report.coverageReport); await extract(zipPath, { dir: tempDir }); // 提供静态文件访问,重定向到index.html app.use(`/reports/${reportId}`, express.static(tempDir)); res.redirect(`/reports/${reportId}/lcov-report/index.html`); } catch (err) { res.status(500).send(err.message); } finally { await client.close(); } }); app.listen(3000, () => console.log('覆盖率报告服务运行在3000端口'));
将这个服务部署到你的EC2或服务器,配置好MongoDB连接即可通过浏览器访问。
三、更优方案推荐
替代MongoDB的存储方案
- GitHub Pages托管:每次运行后把
lcov-report目录推送到gh-pages分支,用Run ID作为子目录名(如/coverage/{runId}/index.html),无需自建服务 - AWS S3存储:将ZIP文件存在S3,元数据存在DynamoDB,S3支持直接静态托管,访问更高效
- GitHub Pages托管:每次运行后把
工作流优化
- 拆分测试和部署为独立Job,测试Job生成覆盖率后,部署Job仅在通过率达标时运行
- 用
actions/cache缓存npm依赖,加快构建速度 - 用
jest-junit生成JUnit格式报告,方便GitHub展示测试结果
报告体验优化
- 集成到PR评论:用
actions/github-script在PR中自动评论覆盖率变化和报告链接 - 使用专业工具:Codecov、Coveralls等工具自动处理报告存储、历史对比、告警,无需自行开发
- 集成到PR评论:用
内容的提问来源于stack exchange,提问作者Vaibhav Mishra
相关产品推荐
相关产品推荐

