Skip to content

chore: migrate from nyc to c8 and enhance coverage reporting #6

chore: migrate from nyc to c8 and enhance coverage reporting

chore: migrate from nyc to c8 and enhance coverage reporting #6

Workflow file for this run

name: Coverage Report
on:
pull_request:
branches:
- main
- v11.0.0
jobs:
coverage-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: npm
- run: npm ci
- name: Run base branch tests for comparison
run: |
git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1
git checkout FETCH_HEAD
echo "Running tests on base branch for comparison..."
if npm run test:unit:justTest:ci 2>/dev/null && [ -f coverage/lcov.info ]; then
mv coverage/lcov.info coverage-base.lcov
echo "Base branch coverage saved"
else
echo "Base branch coverage failed, will skip comparison"
touch coverage-base-failed
fi
git checkout -
- name: Wait for test workflow to complete
id: wait-tests
uses: actions/github-script@v7
with:
script: |
const maxWaitTime = 30 * 60 * 1000; // 30 minutes
const pollInterval = 30 * 1000; // 30 seconds
const startTime = Date.now();
// Find the Tests workflow run for this PR
while (Date.now() - startTime < maxWaitTime) {
const runs = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'ci.yml',
event: 'pull_request',
head_sha: context.sha,
});
const testRun = runs.data.workflow_runs[0];
if (testRun) {
console.log(`Test workflow status: ${testRun.status}, conclusion: ${testRun.conclusion}`);
if (testRun.status === 'completed') {
if (testRun.conclusion === 'success' || testRun.conclusion === 'failure') {
console.log('Test workflow completed, artifact should be available');
core.setOutput('tests_completed', 'true');
core.setOutput('run_id', testRun.id);
return;
} else {
core.setFailed(`Test workflow completed with conclusion: ${testRun.conclusion}`);
return;
}
}
}
// Wait before polling again
console.log('Waiting for test workflow to complete...');
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
core.setFailed('Timeout waiting for test workflow to complete');
- name: Download coverage artifact
if: steps.wait-tests.outputs.tests_completed == 'true'
uses: actions/download-artifact@v7
with:
name: coverage-report
path: coverage/
run-id: ${{ steps.wait-tests.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Generate coverage comparison and comment
if: steps.wait-tests.outputs.tests_completed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
if [ -f coverage/lcov.info ]; then
# Parse current coverage from lcov.info
LINES_FOUND=$(grep -o "LF:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
LINES_HIT=$(grep -o "LH:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
FUNCS_FOUND=$(grep -o "FNF:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
FUNCS_HIT=$(grep -o "FNH:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
BRANCHES_FOUND=$(grep -o "BRF:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
BRANCHES_HIT=$(grep -o "BRH:[0-9]*" coverage/lcov.info | cut -d: -f2 | awk '{s+=$1} END {print s}')
LINES_PCT=$(awk "BEGIN {printf \"%.2f\", ($LINES_HIT/$LINES_FOUND)*100}")
FUNCS_PCT=$(awk "BEGIN {printf \"%.2f\", ($FUNCS_HIT/$FUNCS_FOUND)*100}")
BRANCHES_PCT=$(awk "BEGIN {printf \"%.2f\", ($BRANCHES_HIT/$BRANCHES_FOUND)*100}")
# Check if we have base branch coverage for comparison
BASE_COVERAGE=""
if [ -f coverage-base.lcov ] && [ ! -f coverage-base-failed ]; then
echo "Using pre-computed base branch coverage..."
BASE_LINES_FOUND=$(grep -o "LF:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_LINES_HIT=$(grep -o "LH:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_FUNCS_FOUND=$(grep -o "FNF:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_FUNCS_HIT=$(grep -o "FNH:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_BRANCHES_FOUND=$(grep -o "BRF:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_BRANCHES_HIT=$(grep -o "BRH:[0-9]*" coverage-base.lcov | cut -d: -f2 | awk '{s+=$1} END {print s}')
BASE_LINES_PCT=$(awk "BEGIN {printf \"%.2f\", ($BASE_LINES_HIT/$BASE_LINES_FOUND)*100}")
BASE_FUNCS_PCT=$(awk "BEGIN {printf \"%.2f\", ($BASE_FUNCS_HIT/$BASE_FUNCS_FOUND)*100}")
BASE_BRANCHES_PCT=$(awk "BEGIN {printf \"%.2f\", ($BASE_BRANCHES_HIT/$BASE_BRANCHES_FOUND)*100}")
LINES_DIFF=$(awk "BEGIN {printf \"%.2f\", $LINES_PCT - $BASE_LINES_PCT}")
FUNCS_DIFF=$(awk "BEGIN {printf \"%.2f\", $FUNCS_PCT - $BASE_FUNCS_PCT}")
BRANCHES_DIFF=$(awk "BEGIN {printf \"%.2f\", $BRANCHES_PCT - $BASE_BRANCHES_PCT}")
# Add emoji indicators
LINES_INDICATOR=$(awk "BEGIN {if ($LINES_DIFF > 0) print \"🟒 +\"; else if ($LINES_DIFF < 0) print \"πŸ”΄ \"; else print \"\"}")
FUNCS_INDICATOR=$(awk "BEGIN {if ($FUNCS_DIFF > 0) print \"🟒 +\"; else if ($FUNCS_DIFF < 0) print \"πŸ”΄ \"; else print \"\"}")
BRANCHES_INDICATOR=$(awk "BEGIN {if ($BRANCHES_DIFF > 0) print \"🟒 +\"; else if ($BRANCHES_DIFF < 0) print \"πŸ”΄ \"; else print \"\"}")
BASE_COVERAGE=$(cat <<EOFTABLE
### Compared to base branch (\`${BASE_REF}\`)
| Metric | Base | Current | Change |
|--------|------|---------|--------|
| Lines | ${BASE_LINES_PCT}% | ${LINES_PCT}% | ${LINES_INDICATOR}${LINES_DIFF}% |
| Functions | ${BASE_FUNCS_PCT}% | ${FUNCS_PCT}% | ${FUNCS_INDICATOR}${FUNCS_DIFF}% |
| Branches | ${BASE_BRANCHES_PCT}% | ${BRANCHES_PCT}% | ${BRANCHES_INDICATOR}${BRANCHES_DIFF}% |
EOFTABLE
)
fi
# Create comment body using printf to avoid YAML pipe issues
printf -v COMMENT_BODY '%s\n' \
"## πŸ“Š Coverage Report" \
"" \
"| Metric | Coverage |" \
"|--------|----------|" \
"| Lines | ${LINES_PCT}% (${LINES_HIT}/${LINES_FOUND}) |" \
"| Functions | ${FUNCS_PCT}% (${FUNCS_HIT}/${FUNCS_FOUND}) |" \
"| Branches | ${BRANCHES_PCT}% (${BRANCHES_HIT}/${BRANCHES_FOUND}) |${BASE_COVERAGE}" \
"" \
"<details>" \
"<summary>View full coverage report</summary>" \
"" \
"Download the coverage artifact from the [workflow run](${{ github.event.workflow_run.html_url }}) to see the full HTML report." \
"</details>"
# Delete old coverage comments
gh pr view ${PR_NUMBER} --json comments --jq '.comments[] | select(.body | startswith("## πŸ“Š Coverage Report")) | .id' | \
xargs -I {} gh api -X DELETE /repos/${{ github.repository }}/issues/comments/{} || true
# Post new comment
gh pr comment ${PR_NUMBER} --body "$COMMENT_BODY"
fi