When you run tests in a GitHub Actions workflow, a coverage report will be generated. Now every time i run a test and a new coverage report is generated, I want to store the coverage folder in mongodb, and view the report of each generated coverage by opening the index.html on the browser, which is in Icov-report folder. To be able to view different reports, I will be using unique id like time stamp or run_id.
How do i do it? If there is any other better approach, please do suggest.
For now, this is my approach to store coverage in mongodb:-
store-coverage.js
const fs = require('fs');
const archiver = require('archiver');
const MongoClient = require('mongodb').MongoClient;
const core = require('@actions/core');
const {DefaultArtifactClient} = require('@actions/artifact');
const artifact = new DefaultArtifactClient();
const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri);
const uniqueId = new Date().toISOString;
async function storeCoverageReport() {
try {
await client.connect();
const db = client.db('coverage-reports');
const collection = db.collection('reports');
const rootDirectory = 'coverage'; // Path to the coverage folder
const {id, size} = await artifact.uploadArtifact('coverage-report', {rootDirectory});
console.log(`upload id and size: ${id}, ${size}`);
const coverageReportBuffer = await compressCoverageFolder(rootDirectory);
const result = await collection.insertOne({
_id: uniqueId,
coverageReport: coverageReportBuffer,
});
console.log(`Coverage report stored with ID: ${result.insertedId}`);
} catch (err) {
console.error('Error storing coverage report:', err);
} finally {
await client.close();
}
}
async function compressCoverageFolder(rootDirectory) {
const coverageReportArchive = archiver('zip');
const coverageReportBuffer = [];
coverageReportArchive.on('data', (chunk) => {
coverageReportBuffer.push(chunk);
});
coverageReportArchive.on('end', () => {
return Buffer.concat(coverageReportBuffer);
});
coverageReportArchive.directory(rootDirectory, false);
await coverageReportArchive.finalize();
return coverageReportArchive.pointer();
}
storeCoverageReport();
CD workflow
name: CD Workflow
on:
push:
branches:
- main
- staging
env:
EC2_HOST: ${{ github.ref == 'refs/heads/main' && secrets.EC2_HOST || secrets.STAGING_EC2_HOST }}
EC2_USER: ${{ github.ref == 'refs/heads/main' && secrets.EC2_USER || secrets.STAGING_EC2_USER }}
EC2_KEY: ${{ github.ref == 'refs/heads/main' && secrets.SSH_PRIVATE_KEY || secrets.STAGING_SSH_PRIVATE_KEY }}
ACTIONS_RUNTIME_TOKEN: ${{ secrets.USER_PAT }}
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test -- --json --outputFile=test-results.json
continue-on-error: true # Continue even if tests fail to calculate coverage
- name: Calculate test pass rate
id: test_pass_rate
run: |
node -e 'const fs = require("fs");
const testResults = JSON.parse(fs.readFileSync("test-results.json", "utf8"));
const totalTests = testResults.numTotalTests;
const passedTests = testResults.numPassedTests;
const passRate = (passedTests / totalTests) * 100;
console.log(`Test Pass Rate: ${passRate.toFixed(2)}%`);
console.log(`::set-output name=pass_rate::${passRate.toFixed(2)}`);'
- name: Check pass rate
run: |
if (( $(echo "${{ steps.test_pass_rate.outputs.pass_rate }}" | cut -d'.' -f1) < 90 )); then
echo "Test pass rate is less than 90% in main branch."
exit 1
fi
- name: Archive coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
if-no-files-found: warn
- name: Install MongoDB dependencies
run: npm install mongodb
- name: Store coverage report in MongoDB
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}
run: |
node .github/workflows/store-coverage.js
- name: Login to Docker
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Pull latest code into EC2, push latest code to container and Restart Server
if: steps.test_pass_rate.outputs.pass_rate >= 90
run: |
echo "$EC2_KEY" > ec2_key.pem
chmod 600 ec2_key.pem
ssh -o StrictHostKeyChecking=no -i ec2_key.pem $EC2_USER@$EC2_HOST <<EOF
# sudo usermod -aG docker $USER
# newgrp docker
# docker tag ondc-sellerapp-server omgvaibhav/ondc-sellerapp-server
# docker push omgvaibhav/ondc-sellerapp-server:latest
cd ~/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp
git pull
npm ci
# use pm2 reload server for zero-downtime
pm2 restart server
EOF
Error:
Run node .github/workflows/store-coverage.js
Artifact name is valid!
Error storing coverage report: Error: The provided rootDirectory undefined does not exist
at validateRootDirectory (/home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/upload/upload-zip-specification.js:37:15)
at /home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/upload/upload-artifact.js:49:62
at Generator.next (<anonymous>)
at /home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/upload/upload-artifact.js:31:71
at new Promise (<anonymous>)
at __awaiter (/home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/upload/upload-artifact.js:27:12)
at uploadArtifact (/home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/upload/upload-artifact.js:47:12)
at DefaultArtifactClient.<anonymous> (/home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/client.js:42:61)
at Generator.next (<anonymous>)
at /home/***/actions-runner/_work/ONDC-SellerApp/ONDC-SellerApp/node_modules/@actions/artifact/lib/internal/client.js:8:71
From the error message, It seems that there is a wrong argument passed to the validateRootDirectory() function.
From what I could find by a quick look at the source:
When using
uploadArtifact(artifactName, filesToUpload, rootDirectory, options)
, the 3rd argument has to be the rootDirectory as a native string.So
seems to be the cause of your issue.