Skip to content

Commit

Permalink
Add coverage report, cache node modules
Browse files Browse the repository at this point in the history
  • Loading branch information
huntharo committed May 16, 2024
1 parent 53e96aa commit eff32f0
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 28 deletions.
33 changes: 33 additions & 0 deletions .github/actions/configure-nodejs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: 'Configure Node.js'
description: 'Install Node.js and install Node.js modules or restore cache'

inputs:
node-version:
description: 'NodeJS Version'
default: '18'
lookup-only:
description: 'If true, only checks if cache entry exists and skips download. Does not change save cache behavior'
default: 'false'

runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

- name: Restore Node Modules from Cache
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
packages/**/node_modules
!node_modules/.cache
key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'package-lock.json', '**/package-lock.json') }}
lookup-only: ${{ inputs.lookup-only }}

- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
shell: bash
run: npm ci
41 changes: 41 additions & 0 deletions .github/actions/coverage-report/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: 'Parse Coverage and Post Comment'
description: 'Parses a coverage report and posts a comment on a PR'
inputs:
lcov-file:
description: 'Path to the lcov.info file'
required: true
title:
description: 'Title of the comment'
default: 'Code Coverage Report'

runs:
using: 'composite'
steps:
- name: Parse Coverage
shell: bash
if: github.event_name == 'pull_request'
id: parse
run: |
./scripts/parse-coverage.js ${{ inputs.lcov-file }} > coverage-summary.txt
echo "coverage-summary<<EOF" >> $GITHUB_OUTPUT
cat coverage-summary.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Find Coverage Comment
if: github.event_name == 'pull_request'
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: '### 📊 ${{ inputs.title }}'

- name: Post Coverage Comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
edit-mode: replace
issue-number: ${{ github.event.pull_request.number }}
body: |
### 📊 ${{ inputs.title }}
${{ steps.parse.outputs.coverage-summary }}
37 changes: 29 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,33 @@ on:
- main

jobs:
build:
check-access:
runs-on: ubuntu-latest
outputs:
has-token-access: ${{ steps.check.outputs.has-token-access }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- id: check
run: |
echo "has-token-access=$(if [[ '${{ github.event.pull_request.head.repo.fork }}' != 'true' && '${{ github.actor }}' != 'dependabot[bot]' ]]; then echo 'true'; else echo 'false'; fi)" >> $GITHUB_OUTPUT
- name: Use Node.js 16
uses: actions/setup-node@v3
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
node-version: 16
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

- name: Install Modules
run: npm ci
build:
needs:
- install-deps
- check-access
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- uses: ./.github/actions/configure-nodejs

- name: Build
run: npm run build
Expand All @@ -31,3 +45,10 @@ jobs:

- name: Test
run: npm run test

- name: Upload code coverage
if: github.event_name == 'pull_request' && needs.check-access.outputs.has-token-access == 'true'
uses: ./.github/actions/coverage-report
with:
lcov-file: coverage/lcov.info
title: Node.js Code Coverage Report
20 changes: 12 additions & 8 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ on:
workflow_dispatch:

jobs:
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

build:
needs:
- install-deps
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Use Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16
uses: actions/checkout@v4

- name: Install Modules
run: npm ci
- uses: ./.github/actions/configure-nodejs

- name: Build Docs
run: npm run build:docs
Expand Down
20 changes: 12 additions & 8 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ on:
types: [published]

jobs:
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/configure-nodejs
with:
lookup-only: 'true' # We only want to lookup from the cache - if a hit, this job does nothing

build:
needs:
- install-deps
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Use Node.js 16
uses: actions/setup-node@v3
with:
node-version: 16
- uses: ./.github/actions/configure-nodejs

- name: Use the Release Tag Version
run: |
npm version from-git --allow-same-version --no-git-tag-version
- name: Install Modules
run: npm ci

- name: Build
run: npm run build

Expand Down
6 changes: 2 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module.exports = {
// "lcov",
// "clover"
// ],
coverageReporters: ['html', 'text'],
coverageReporters: ['lcov', 'html', 'text'],

// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
Expand Down Expand Up @@ -130,9 +130,7 @@ module.exports = {
// roots: [
// "<rootDir>"
// ],
roots: [
'<rootDir>src/',
],
roots: ['<rootDir>src/'],

// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
Expand Down
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"jest": "29.5.0",
"lcov-parse": "1.0.0",
"prettier": "2.8.8",
"ts-jest": "29.1.0",
"ts-node": "10.9.1",
Expand Down
126 changes: 126 additions & 0 deletions scripts/parse-coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env node
const lcovParse = require("lcov-parse");
const fs = require("fs");

const lcovPath = process.argv[2];
const needsImprovementBelow = parseFloat(
process.argv.length >= 4 ? process.argv[3] : "90"
);
const poorBelow = parseFloat(process.argv[4] >= 5 ? process.argv[4] : "50");

if (!lcovPath || isNaN(needsImprovementBelow) || isNaN(poorBelow)) {
console.error(
"Please provide the path to the lcov.info file and the 'needs-improvement-below' and 'poor-below' percentages as command-line arguments."
);
process.exit(1);
}

if (!fs.existsSync(lcovPath)) {
console.error(
`The file ${lcovPath} does not exist. Please provide the path to the lcov.info file.`
);
process.exit(1);
}

const outputFormat = "markdown";

if (outputFormat === "markdown") {
console.log(
"| File | Lines | Lines Hit / Found | Uncovered Lines | Branches |"
);
console.log("| --- | --- | --- | --- | --- |");
}

function shortenPath(path, maxLength) {
if (path.length <= maxLength) {
return path;
}

const start = path.substring(0, maxLength / 2 - 2); // -2 for the '..' in the middle
const end = path.substring(path.length - maxLength / 2, path.length);

return `${start}..${end}`;
}

function getEmoji(lineCoverage, needsImprovementBelow, poorBelow) {
if (lineCoverage >= needsImprovementBelow) {
return "✅"; // white-check emoji
} else if (
lineCoverage < needsImprovementBelow &&
lineCoverage >= poorBelow
) {
return "🟡"; // yellow-ball emoji
} else {
return "❌"; // red-x emoji
}
}

lcovParse(lcovPath, function (err, data) {
if (err) {
console.error(err);
} else {
let totalLinesHit = 0;
let totalLinesFound = 0;
let totalBranchesHit = 0;
let totalBranchesFound = 0;

data.forEach((file) => {
totalLinesHit += file.lines.hit;
totalLinesFound += file.lines.found;
totalBranchesHit += file.branches.hit;
totalBranchesFound += file.branches.found;
const relativePath = shortenPath(
file.file.replace(process.cwd(), ""),
50
);
const lineCoverage = ((file.lines.hit / file.lines.found) * 100).toFixed(
1
);
const branchCoverage = (
(file.branches.hit / file.branches.found) *
100
).toFixed(1);
let emoji = getEmoji(lineCoverage, needsImprovementBelow, poorBelow);
if (outputFormat === "markdown") {
console.log(
`| ${relativePath} | ${emoji}&nbsp;${lineCoverage}% | ${
file.lines.hit
}&nbsp;/&nbsp;${file.lines.found} | ${
file.lines.found - file.lines.hit
} | ${file.branches.found === 0 ? "-" : `${branchCoverage}%`} |`
);
} else {
console.log(
`${emoji} File: ${relativePath}, Line Coverage: ${lineCoverage}%, Branch Coverage: ${branchCoverage}%`
);
}
});

const overallLineCoverage = (
(totalLinesHit / totalLinesFound) *
100
).toFixed(1);
const overallBranchCoverage = (
(totalBranchesHit / totalBranchesFound) *
100
).toFixed(1);
const totalUncoveredLines = totalLinesFound - totalLinesHit;
const overallEmoji = getEmoji(
overallLineCoverage,
needsImprovementBelow,
poorBelow
);

if (outputFormat === "markdown") {
console.log(
`| Overall | ${overallEmoji}&nbsp;${overallLineCoverage}% | ${totalLinesHit}&nbsp;/&nbsp;${totalLinesFound} | ${totalUncoveredLines} | ${
totalBranchesFound === 0 ? "-" : `${overallBranchCoverage}%`
} |`
);
} else {
console.log(
`Overall Line Coverage: ${overallLineCoverage}%, Overall Branch Coverage: ${overallBranchCoverage}%`
);
}
}
});

0 comments on commit eff32f0

Please sign in to comment.