Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions package-lock.json

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

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.13",
"@vercel/ncc": "^0.38.4",
"esmock": "^2.7.3"
"@vercel/ncc": "^0.38.4"
}
}
24 changes: 20 additions & 4 deletions src/health-score.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@
import retrieveCodeCoverage from './score_components/coverage.js';
import findProblematicComments from './score_components/find-problematic-comments.js';

/**
* @typedef {object} HealthScoreDeps
* @property {import('@api/codecov')} codecov Codecov API client
* @property {import('./get-sha.js')} getSHA Function to get commit SHA
* @property {import('node:child_process')} childProcess Node.js child_process module
* @property {import('node:fs')} fs Node.js fs module
*/

const hs = {
/**
* @description Compiles the health score components
* @param {import('@actions/core')} core `@actions/core` GitHub Actions core helper utility
* @param {import('@actions/github')} github `@actions/github` GitHub Actions core helper utility
* @returns {Promise<import('./types.js').HealthScore>} score Health score details object
* @param {HealthScoreDeps} deps External dependencies
* @returns {Promise<import('./types.js').HealthScore>} Health score details object
*/
compile: async function compileScore(core, github) {
compile: async function compileScore(core, github, deps) {
// TODO: wire up action outputs

Check warning on line 23 in src/health-score.js

View workflow job for this annotation

GitHub Actions / Health Score

src/health-score.js#L23

Problematic comment ("TODO") identified
const extensionInput = core.getInput('extension');
const includeInput = core.getInput('include');
const excludeInput = core.getInput('exclude');
Expand All @@ -21,13 +30,20 @@
const excludes = parseYamlArray(excludeInput);

let com = '';
const misses = await hs.coverage(core, github); // uncovered LoC
const misses = await hs.coverage(core, github, deps.codecov, deps.getSHA); // uncovered LoC
if (extensions.length === 0) {
core.error('Extensions not specified');
} else {
if (includes.length === 0)
core.warning('Directories to be included not specified');
com = hs.grep(core, extensions, includes, excludes); // to-do et al comments
com = hs.grep(
core,
deps.childProcess,
deps.fs,
extensions,
includes,
excludes,
); // to-do et al comments
}
return {
comments: com,
Expand Down
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import childProcess from 'node:child_process';
import fs from 'node:fs';
import * as core from '@actions/core';
import * as github from '@actions/github';
import codecov from '@api/codecov';
import getSHA from './get-sha.js';
import hs from './health-score.js';

const deps = { childProcess, fs, codecov, getSHA };

const startTime = new Date();
hs.compile(core, github)
hs.compile(core, github, deps)
.then((score) => hs.report(startTime, core, github, score))
.then(console.log)
.catch((err) => {
Expand Down
14 changes: 9 additions & 5 deletions src/score_components/coverage.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import codecov from '@api/codecov';
import getSHA from '../get-sha.js';

/**
* @description Compiles the health score components
* @description Retrieves code coverage data from Codecov API
* @param {import('@actions/core')} core `@actions/core` GitHub Actions core helper utility
* @param {import('@actions/github')} github `@actions/github` GitHub Actions core helper utility
* @param {import('@api/codecov')} codecov Codecov API client
* @param {import('../get-sha.js')} getSHA Function to get commit SHA
* @returns {Promise<number>} Number of uncovered lines of code, or 0 in the case of no codecov token specified
*/
export default async function retrieveCodeCoverage(core, github) {
export default async function retrieveCodeCoverage(
core,
github,
codecov,
getSHA,
) {
// See if we can get a coverage overview for this commit from codecov
const codecovToken = core.getInput('codecov_token');
const maxAttempts =
Expand Down
17 changes: 13 additions & 4 deletions src/score_components/find-problematic-comments.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import child_process from 'node:child_process';
import fs from 'node:fs';

const CommentType = Object.freeze({
TODO: 'TODO',
FIXME: 'FIXME',
HACK: 'HACK',
});

/**
* @description Searches for problematic comments (TODO, FIXME, HACK) in the codebase

Check warning on line 8 in src/score_components/find-problematic-comments.js

View workflow job for this annotation

GitHub Actions / Health Score

src/score_components/find-problematic-comments.js#L8

Problematic comment ("TODO", "HACK", "FIXME") identified
* @param {import('@actions/core')} core `@actions/core` GitHub Actions core helper utility
* @param {import('node:child_process')} childProcess Node.js child_process module
* @param {import('node:fs')} fs Node.js fs module
* @param {string[]} ext File extensions to search
* @param {string[]} include Directories to include
* @param {string[]} exclude Directories to exclude
* @returns {Array<{path: string, line_no: number, comment: string, commentType: string|null}>} Array of problematic comments found
*/
export default function grepForProblematicComments(
core,
childProcess,
fs,
ext,
include,
exclude,
Expand Down Expand Up @@ -45,13 +54,13 @@
find += ` -not -path "*/${ex}"`;
}
}
const commentPattern = `\\s*(//|/\\*|\\*).*\\b(${CommentType.TODO}|${CommentType.HACK}|${CommentType.FIXME})\\b`;

Check warning on line 57 in src/score_components/find-problematic-comments.js

View workflow job for this annotation

GitHub Actions / Health Score

src/score_components/find-problematic-comments.js#L57

Problematic comment ("TODO", "HACK", "FIXME") identified

// Modify the grep command to search within comments only
find += ` -exec sh -c 'grep -EHn "${commentPattern}" "$0"' {} \\;`;
let output = '';
try {
output = child_process.execSync(find).toString().trim();
output = childProcess.execSync(find).toString().trim();
} catch (e) {
core.error(e);
core.error('child_process execSync failed to execute');
Expand All @@ -60,7 +69,7 @@
.split('\n')
.filter(Boolean)
.map((line) => {
// example line output = "./src/report.js:47: // TODO: handle API call erroring out"

Check warning on line 72 in src/score_components/find-problematic-comments.js

View workflow job for this annotation

GitHub Actions / Health Score

src/score_components/find-problematic-comments.js#L72

Problematic comment ("TODO", "HACK", "FIXME") identified
const [path, lineNo, ...data] = line.split(':');

const lineData = data.join(':');
Expand Down
20 changes: 10 additions & 10 deletions test/health-score-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('health-score', () => {
exclude: 'test',
};
mocks.github.context = contextValue;
await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);
assert.ok(
mocks.core.warning.mock.calls.some((c) =>
c.arguments[0].includes('Directories to be included not specified'),
Expand All @@ -47,7 +47,7 @@ describe('health-score', () => {
exclude: '',
};
mocks.github.context = contextValue;
await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);
assert.ok(
mocks.core.error.mock.calls.some((c) =>
c.arguments[0].includes('Extensions not specified'),
Expand All @@ -66,10 +66,10 @@ describe('health-score', () => {
mocks._grepReturns = [''];
mocks._coverageReturns = 0;

await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);

assert.equal(mocks.grep.mock.callCount(), 1);
const [core, exts, includes, excludes] =
const [core, _childProcess, _fs, exts, includes, excludes] =
mocks.grep.mock.calls[0].arguments;
assert.equal(core, mocks.core);
assert.deepEqual(exts, ['js']);
Expand All @@ -92,10 +92,10 @@ describe('health-score', () => {
mocks._grepReturns = [''];
mocks._coverageReturns = 0;

await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);

assert.equal(mocks.grep.mock.callCount(), 1);
const [_core, exts, includes, excludes] =
const [_core, _childProcess, _fs, exts, includes, excludes] =
mocks.grep.mock.calls[0].arguments;
assert.deepEqual(exts, ['js', 'ts']);
assert.deepEqual(includes, ['src', 'lib']);
Expand All @@ -114,10 +114,10 @@ describe('health-score', () => {
mocks._grepReturns = [''];
mocks._coverageReturns = 0;

await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);

assert.equal(mocks.grep.mock.callCount(), 1);
const [_core, exts, includes, excludes] =
const [_core, _childProcess, _fs, exts, includes, excludes] =
mocks.grep.mock.calls[0].arguments;
assert.deepEqual(exts, ['js', 'ts']);
assert.deepEqual(includes, ['src', 'lib']);
Expand All @@ -136,10 +136,10 @@ describe('health-score', () => {
mocks._grepReturns = [''];
mocks._coverageReturns = 0;

await hs.compile(mocks.core, mocks.github);
await hs.compile(mocks.core, mocks.github, mocks.deps);

assert.equal(mocks.grep.mock.callCount(), 1);
const [_core, exts, includes, excludes] =
const [_core, _childProcess, _fs, exts, includes, excludes] =
mocks.grep.mock.calls[0].arguments;
assert.deepEqual(exts, ['js', 'ts']);
assert.deepEqual(includes, ['src', 'lib']);
Expand Down
24 changes: 11 additions & 13 deletions test/score_components/coverage-test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import assert from 'node:assert';
import { beforeEach, describe, it, mock } from 'node:test';
import esmock from 'esmock';
import cov from '../../src/score_components/coverage.js';

describe('score component: code coverage', () => {
let cov;
let mockCodecov;
let mockCore;
let mockGithub;
let mockGetSHA;

beforeEach(async () => {
beforeEach(() => {
mockCodecov = {
auth: mock.fn(),
repos_commits_retrieve: mock.fn(),
Expand All @@ -30,18 +30,16 @@ describe('score component: code coverage', () => {
},
};

cov = await esmock('../../src/score_components/coverage.js', {
'@api/codecov': { default: mockCodecov },
});
mockGetSHA = mock.fn(() => 'abcd1234');
});

it('should export a function', () => {
assert.equal(typeof cov.default, 'function');
assert.equal(typeof cov, 'function');
});

it('should return 0 if no `codecov_token` input provided', async () => {
mockCore.getInput.mock.mockImplementation(() => '');
const res = await cov.default(mockCore, mockGithub);
const res = await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
assert.equal(res, 0);
});

Expand All @@ -59,7 +57,7 @@ describe('score component: code coverage', () => {
},
}),
);
const res = await cov.default(mockCore, mockGithub);
const res = await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
assert.equal(res, 1337);
});

Expand All @@ -81,7 +79,7 @@ describe('score component: code coverage', () => {
}
return Promise.resolve({ data: {} });
});
const res = await cov.default(mockCore, mockGithub);
const res = await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
assert.strictEqual(res, 42);
});

Expand All @@ -104,7 +102,7 @@ describe('score component: code coverage', () => {
});

const startTime = performance.now();
const res = await cov.default(mockCore, mockGithub);
const res = await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
const endTime = performance.now();
assert.equal(res, 42);
assert.equal(mockCodecov.repos_commits_retrieve.mock.callCount(), 2);
Expand Down Expand Up @@ -137,7 +135,7 @@ describe('score component: code coverage', () => {
return Promise.resolve({ data: { totals: { misses: 42 } } });
});

const res = await cov.default(mockCore, mockGithub);
const res = await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
assert.equal(res, 0);
assert.equal(mockCodecov.repos_commits_retrieve.mock.callCount(), 1);
assert.ok(
Expand All @@ -161,7 +159,7 @@ describe('score component: code coverage', () => {
Promise.resolve({ data: {} }),
);

await cov.default(mockCore, mockGithub);
await cov(mockCore, mockGithub, mockCodecov, mockGetSHA);
assert.ok(
mockCore.error.mock.calls.some((c) =>
c.arguments[0].includes('Reached maximum attempts'),
Expand Down
Loading
Loading