From 16fb4c53b08188666d7c4ed958b0d98f0763eea0 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 19 Aug 2024 10:40:12 +0100 Subject: [PATCH 1/3] docs: Add brief documentation comments to each function --- src/scripts/analyze.ts | 15 +++++++++++++++ src/scripts/behind.ts | 6 ++++++ src/scripts/comment.ts | 14 ++++++++++++++ src/scripts/coverage.ts | 6 ++++++ src/scripts/prevCoverage.ts | 29 +++++++++++++++++------------ src/scripts/push.ts | 3 +++ src/scripts/runTests.ts | 5 +++++ src/scripts/setup.ts | 3 +++ src/scripts/utils.ts | 24 ++++++++++++++++++++++++ 9 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/scripts/analyze.ts b/src/scripts/analyze.ts index f16a186..c665036 100644 --- a/src/scripts/analyze.ts +++ b/src/scripts/analyze.ts @@ -11,6 +11,10 @@ export type analyzeDetails = { file: string; details: string }; export type analyzeErrTypes = "error" | "warning" | "info"; +/** + * Run static analysis on the codebase + * @returns Analysis result as a stepResponse object + */ export const getAnalyze = async (): Promise => { startGroup("Analyzing code"); let response: stepResponse | undefined; @@ -78,6 +82,11 @@ export const getAnalyze = async (): Promise => { return response; }; +/** + * Get the emoji corresponding to the error type + * @param errType - Type of error + * @returns Emoji corresponding to the error type + */ export const getErrEmoji = (errType: analyzeErrTypes) => { switch (errType) { case "error": @@ -89,5 +98,11 @@ export const getErrEmoji = (errType: analyzeErrTypes) => { } }; +/** + * Generate a table row for the error + * @param err - Error details + * @param type - Type of error + * @returns Formatted table row for the error + */ export const generateTableRow = (err: analyzeDetails, type: analyzeErrTypes) => `${getErrEmoji(type)}Error${err.file}${err.details}`; diff --git a/src/scripts/behind.ts b/src/scripts/behind.ts index c5651cd..257c96f 100644 --- a/src/scripts/behind.ts +++ b/src/scripts/behind.ts @@ -4,6 +4,12 @@ import { GitHub } from "@actions/github/lib/utils"; import { stepResponse } from "../main"; import { debug } from "@actions/core"; +/** + * Check if the branch is behind the base branch + * @param octokit - Instance of GitHub client + * @param context - GitHub context + * @returns stepResponse object + */ export const checkBranchStatus = async ( octokit: InstanceType, context: Context diff --git a/src/scripts/comment.ts b/src/scripts/comment.ts index bdfb9e7..20647b5 100644 --- a/src/scripts/comment.ts +++ b/src/scripts/comment.ts @@ -6,6 +6,14 @@ import { debug } from "@actions/core"; const SIGNATURE = `Created with Flutter code quality action`; +/** + * Create a comment for the PR + * @param analyze - Static analysis result + * @param test - Test result + * @param coverage - Coverage result + * @param behindBy - Branch status + * @returns Comment message + */ export const createComment = ( analyze: stepResponse | undefined, test: stepResponse | undefined, @@ -31,6 +39,12 @@ ${SIGNATURE} return output; }; +/** + * Post a comment on the PR + * @param octokit - Instance of GitHub client + * @param commentMessage - Comment message + * @param context - GitHub context + */ export async function postComment(octokit: InstanceType, commentMessage: string, context: Context) { startGroup(`Commenting on PR`); diff --git a/src/scripts/coverage.ts b/src/scripts/coverage.ts index b838f59..f769ad3 100644 --- a/src/scripts/coverage.ts +++ b/src/scripts/coverage.ts @@ -7,6 +7,12 @@ import { getLcovLines } from "./utils"; export const COV_FAILURE = "⚠️ - Coverage check failed"; +/** + * Get the coverage report and compare with the previous coverage + * @param prevCoverage - Previous coverage report + * @param coverageDirectory - Directory to store coverage report + * @returns Coverage report as a stepResponse object + */ export const getCoverage = (prevCoverage: Lcov | undefined, coverageDirectory: string): stepResponse => { startGroup("Checking test coverage"); let response: stepResponse | undefined; diff --git a/src/scripts/prevCoverage.ts b/src/scripts/prevCoverage.ts index b7e38ff..ea042c6 100644 --- a/src/scripts/prevCoverage.ts +++ b/src/scripts/prevCoverage.ts @@ -2,7 +2,7 @@ import { Context } from "@actions/github/lib/context"; import { GitHub } from "@actions/github/lib/utils"; import { exec } from "@actions/exec"; import { Lcov, parse } from "lcov-utils"; -import { COV_FILE, importLcov } from "./utils"; +import { COV_FILE, importLcov, toBuffer } from "./utils"; import { DefaultArtifactClient } from "@actions/artifact"; import { endGroup, startGroup } from "@actions/core"; import { debug } from "@actions/core"; @@ -10,6 +10,13 @@ import AdmZip from "adm-zip"; const ARTIFACT_NAME = "coverage"; +/** + * Retrieve previous coverage report from the base branch + * @param octokit - Instance of GitHub client + * @param context - GitHub context + * @param coverageDirectory - Directory to store coverage report + * @returns Lcov object + */ export const retrievePreviousCoverage = async ( octokit: InstanceType, context: Context, @@ -72,7 +79,7 @@ export const retrievePreviousCoverage = async ( } if (!report) { debug("Artifact not found, will pull coverage from BASE"); - report = await generateOldCoverage(baseSHA, headSHA, coverageDirectory); + report = await generatePreviousCoverage(baseSHA, headSHA, coverageDirectory); } else { try { await exec(`rm ${coverageDirectory}/${COV_FILE}`); @@ -86,7 +93,14 @@ export const retrievePreviousCoverage = async ( throw new Error("Failed to generate coverage report"); }; -const generateOldCoverage = async ( +/** + * Generate coverage report from the base branch + * @param prev_sha - Base branch SHA + * @param current_sha - Current branch SHA + * @param coverage_directory - Directory to store coverage report + * @returns Previous coverage report as Lcov object + */ +const generatePreviousCoverage = async ( prev_sha: string, current_sha: string, coverage_directory: string @@ -107,12 +121,3 @@ const generateOldCoverage = async ( await exec(`git checkout ${current_sha}`); return report; }; - -export const toBuffer = (arrayBuffer: ArrayBuffer) => { - const buffer = Buffer.alloc(arrayBuffer.byteLength); - const view = new Uint8Array(arrayBuffer); - for (let i = 0; i < buffer.length; ++i) { - buffer[i] = view[i]; - } - return buffer; -}; diff --git a/src/scripts/push.ts b/src/scripts/push.ts index 5c1d66f..303261e 100644 --- a/src/scripts/push.ts +++ b/src/scripts/push.ts @@ -3,6 +3,9 @@ import { exec } from "@actions/exec"; import { execSync } from "child_process"; import { debug } from "@actions/core"; +/** + * Push changes to the branch + */ export const push = async () => { startGroup("Check for changes"); let stdout: string = ""; diff --git a/src/scripts/runTests.ts b/src/scripts/runTests.ts index 21184bb..fedaa01 100644 --- a/src/scripts/runTests.ts +++ b/src/scripts/runTests.ts @@ -5,6 +5,11 @@ import { stepResponse } from "../main"; export const TEST_SUCCESS = "✅ - All tests passed"; export const TEST_ERROR = "⚠️ - Error running tests"; +/** + * Run tests and return the result + * @param coverageDir - Directory to store coverage report + * @returns Test result as a stepResponse object + */ export const getTest = async (coverageDir: string): Promise => { startGroup("Running tests"); let response: stepResponse | undefined; diff --git a/src/scripts/setup.ts b/src/scripts/setup.ts index 264d963..f941ca2 100644 --- a/src/scripts/setup.ts +++ b/src/scripts/setup.ts @@ -1,6 +1,9 @@ import { endGroup, startGroup } from "@actions/core"; import { exec } from "@actions/exec"; +/** + * Set up Flutter + */ export const setup = async () => { startGroup("Set up Flutter"); try { diff --git a/src/scripts/utils.ts b/src/scripts/utils.ts index 450101b..ccff21c 100644 --- a/src/scripts/utils.ts +++ b/src/scripts/utils.ts @@ -3,6 +3,11 @@ import { readFileSync } from "fs"; import { Lcov, parse, sum } from "lcov-utils"; export const COV_FILE = "lcov.info"; +/** + * Import the Lcov report + * @param file_directory - Directory containing the Lcov report + * @returns Lcov report + */ export const importLcov = (file_directory: string): Lcov => { startGroup("Retrieving coverage report"); try { @@ -16,4 +21,23 @@ export const importLcov = (file_directory: string): Lcov => { } }; +/** + * Get the total lines covered in the Lcov report + * @param report - Lcov report + * @returns Total lines covered + */ export const getLcovLines = (report: Lcov): number => sum(report).lines; + +/** + * Convert ArrayBuffer to Buffer + * @param arrayBuffer - ArrayBuffer to convert + * @returns Data in Buffer format + */ +export const toBuffer = (arrayBuffer: ArrayBuffer) => { + const buffer = Buffer.alloc(arrayBuffer.byteLength); + const view = new Uint8Array(arrayBuffer); + for (let i = 0; i < buffer.length; ++i) { + buffer[i] = view[i]; + } + return buffer; +}; From 35ac510d9708a457413ba0d3c2ff42ea989e9f04 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 19 Aug 2024 11:14:20 +0100 Subject: [PATCH 2/3] test: Added --local input arg to test functionality locally --- package-lock.json | 9 ++++++++- package.json | 7 +++++-- src/main.ts | 20 ++++++++++++-------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f1cf41..be2de9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,10 @@ "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", "@types/adm-zip": "^0.5.5", + "@types/minimist": "^1.2.5", "adm-zip": "^0.5.14", - "lcov-utils": "^0.5.4" + "lcov-utils": "^0.5.4", + "minimist": "^1.2.8" }, "devDependencies": { "@types/jest": "^29.5.12", @@ -1828,6 +1830,11 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==" + }, "node_modules/@types/node": { "version": "20.14.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", diff --git a/package.json b/package.json index 34bacd6..53c4f7f 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", "@types/adm-zip": "^0.5.5", + "@types/minimist": "^1.2.5", "adm-zip": "^0.5.14", - "lcov-utils": "^0.5.4" + "lcov-utils": "^0.5.4", + "minimist": "^1.2.8" }, "devDependencies": { "@types/jest": "^29.5.12", @@ -23,7 +25,8 @@ "scripts": { "test": "npx jest", "test:coverage": "npx jest --coverage", - "build": "npx @vercel/ncc build src/main.ts -o dist" + "build": "npx @vercel/ncc build src/main.ts -o dist", + "start": "npx tsx src/main.ts --local" }, "overrides": { "graceful-fs": "^4.2.11" diff --git a/src/main.ts b/src/main.ts index 45015dd..6e2570a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,11 +9,12 @@ import { checkBranchStatus } from "./scripts/behind"; import { push } from "./scripts/push"; import { retrievePreviousCoverage } from "./scripts/prevCoverage"; import { Lcov } from "lcov-utils"; +import minimist from "minimist"; export type stepResponse = { output: string; error: boolean }; export const COVERAGE_DIR = ".coverage"; -const run = async () => { +const run = async (isLocal: boolean) => { try { const workingDirectory = getInput("working-directory"); // Check if the working directory is different from the current directory @@ -23,12 +24,12 @@ const run = async () => { const token = process.env.GITHUB_TOKEN || getInput("token"); - const runTests = getBooleanInput("run-tests"); - const runAnalyze = getBooleanInput("run-analyze"); - const runCoverage = getBooleanInput("run-coverage"); - const runPrevCoverage = getBooleanInput("run-prev-coverage"); - const runBehindBy = getBooleanInput("run-behind-by"); - const createComment = getBooleanInput("create-comment"); + const runTests = isLocal ? true : getBooleanInput("run-tests"); + const runAnalyze = isLocal ? true : getBooleanInput("run-analyze"); + const runCoverage = isLocal ? true : getBooleanInput("run-coverage"); + const runPrevCoverage = isLocal ? true : getBooleanInput("run-prev-coverage"); + const runBehindBy = isLocal ? true : getBooleanInput("run-behind-by"); + const createComment = isLocal ? true : getBooleanInput("create-comment"); const octokit = getOctokit(token); let prevCoverage: Lcov | undefined; @@ -39,6 +40,7 @@ const run = async () => { console.error(e); } } + const behindByStr: stepResponse | undefined = runBehindBy ? await checkBranchStatus(octokit, context) : undefined; await setup(); @@ -62,6 +64,8 @@ const run = async () => { } }; -run(); +const argv = minimist(process.argv.slice(2)); + +run(argv.local ?? false); //TODO: Show coverage diff in comment - which files coverage have changed. From 8e38188840f33065757b3dd4af64ad97f18604a0 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 19 Aug 2024 11:15:39 +0100 Subject: [PATCH 3/3] fix: When calculating previous coverage, reset branch correctly --- dist/index.js | 409 +++++++++++++++++++++++++++++++++--- src/scripts/prevCoverage.ts | 12 +- 2 files changed, 389 insertions(+), 32 deletions(-) diff --git a/dist/index.js b/dist/index.js index 125c8ac..e4bca85 100644 --- a/dist/index.js +++ b/dist/index.js @@ -71241,6 +71241,277 @@ var union = baseRest(function(arrays) { module.exports = union; +/***/ }), + +/***/ 13566: +/***/ ((module) => { + +"use strict"; + + +function hasKey(obj, keys) { + var o = obj; + keys.slice(0, -1).forEach(function (key) { + o = o[key] || {}; + }); + + var key = keys[keys.length - 1]; + return key in o; +} + +function isNumber(x) { + if (typeof x === 'number') { return true; } + if ((/^0x[0-9a-f]+$/i).test(x)) { return true; } + return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x); +} + +function isConstructorOrProto(obj, key) { + return (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__'; +} + +module.exports = function (args, opts) { + if (!opts) { opts = {}; } + + var flags = { + bools: {}, + strings: {}, + unknownFn: null, + }; + + if (typeof opts.unknown === 'function') { + flags.unknownFn = opts.unknown; + } + + if (typeof opts.boolean === 'boolean' && opts.boolean) { + flags.allBools = true; + } else { + [].concat(opts.boolean).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + } + + var aliases = {}; + + function aliasIsBoolean(key) { + return aliases[key].some(function (x) { + return flags.bools[x]; + }); + } + + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); + + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + if (aliases[key]) { + [].concat(aliases[key]).forEach(function (k) { + flags.strings[k] = true; + }); + } + }); + + var defaults = opts.default || {}; + + var argv = { _: [] }; + + function argDefined(key, arg) { + return (flags.allBools && (/^--[^=]+$/).test(arg)) + || flags.strings[key] + || flags.bools[key] + || aliases[key]; + } + + function setKey(obj, keys, value) { + var o = obj; + for (var i = 0; i < keys.length - 1; i++) { + var key = keys[i]; + if (isConstructorOrProto(o, key)) { return; } + if (o[key] === undefined) { o[key] = {}; } + if ( + o[key] === Object.prototype + || o[key] === Number.prototype + || o[key] === String.prototype + ) { + o[key] = {}; + } + if (o[key] === Array.prototype) { o[key] = []; } + o = o[key]; + } + + var lastKey = keys[keys.length - 1]; + if (isConstructorOrProto(o, lastKey)) { return; } + if ( + o === Object.prototype + || o === Number.prototype + || o === String.prototype + ) { + o = {}; + } + if (o === Array.prototype) { o = []; } + if (o[lastKey] === undefined || flags.bools[lastKey] || typeof o[lastKey] === 'boolean') { + o[lastKey] = value; + } else if (Array.isArray(o[lastKey])) { + o[lastKey].push(value); + } else { + o[lastKey] = [o[lastKey], value]; + } + } + + function setArg(key, val, arg) { + if (arg && flags.unknownFn && !argDefined(key, arg)) { + if (flags.unknownFn(arg) === false) { return; } + } + + var value = !flags.strings[key] && isNumber(val) + ? Number(val) + : val; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; + + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--') + 1); + args = args.slice(0, args.indexOf('--')); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + var key; + var next; + + if ((/^--.+=/).test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + key = m[1]; + var value = m[2]; + if (flags.bools[key]) { + value = value !== 'false'; + } + setArg(key, value, arg); + } else if ((/^--no-.+/).test(arg)) { + key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false, arg); + } else if ((/^--.+/).test(arg)) { + key = arg.match(/^--(.+)/)[1]; + next = args[i + 1]; + if ( + next !== undefined + && !(/^(-|--)[^-]/).test(next) + && !flags.bools[key] + && !flags.allBools + && (aliases[key] ? !aliasIsBoolean(key) : true) + ) { + setArg(key, next, arg); + i += 1; + } else if ((/^(true|false)$/).test(next)) { + setArg(key, next === 'true', arg); + i += 1; + } else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } else if ((/^-[^-]+/).test(arg)) { + var letters = arg.slice(1, -1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + next = arg.slice(j + 2); + + if (next === '-') { + setArg(letters[j], next, arg); + continue; + } + + if ((/[A-Za-z]/).test(letters[j]) && next[0] === '=') { + setArg(letters[j], next.slice(1), arg); + broken = true; + break; + } + + if ( + (/[A-Za-z]/).test(letters[j]) + && (/-?\d+(\.\d*)?(e-?\d+)?$/).test(next) + ) { + setArg(letters[j], next, arg); + broken = true; + break; + } + + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + setArg(letters[j], arg.slice(j + 2), arg); + broken = true; + break; + } else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); + } + } + + key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if ( + args[i + 1] + && !(/^(-|--)[^-]/).test(args[i + 1]) + && !flags.bools[key] + && (aliases[key] ? !aliasIsBoolean(key) : true) + ) { + setArg(key, args[i + 1], arg); + i += 1; + } else if (args[i + 1] && (/^(true|false)$/).test(args[i + 1])) { + setArg(key, args[i + 1] === 'true', arg); + i += 1; + } else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } + } else { + if (!flags.unknownFn || flags.unknownFn(arg) !== false) { + argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg)); + } + if (opts.stopEarly) { + argv._.push.apply(argv._, args.slice(i + 1)); + break; + } + } + } + + Object.keys(defaults).forEach(function (k) { + if (!hasKey(argv, k.split('.'))) { + setKey(argv, k.split('.'), defaults[k]); + + (aliases[k] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[k]); + }); + } + }); + + if (opts['--']) { + argv['--'] = notFlags.slice(); + } else { + notFlags.forEach(function (k) { + argv._.push(k); + }); + } + + return argv; +}; + + /***/ }), /***/ 46003: @@ -112996,10 +113267,13 @@ ZipStream.prototype.finalize = function() { /***/ }), /***/ 70845: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.COVERAGE_DIR = void 0; const core_1 = __nccwpck_require__(72614); @@ -113012,8 +113286,9 @@ const setup_1 = __nccwpck_require__(59346); const behind_1 = __nccwpck_require__(98890); const push_1 = __nccwpck_require__(13662); const prevCoverage_1 = __nccwpck_require__(39033); +const minimist_1 = __importDefault(__nccwpck_require__(13566)); exports.COVERAGE_DIR = ".coverage"; -const run = async () => { +const run = async (isLocal) => { try { const workingDirectory = (0, core_1.getInput)("working-directory"); // Check if the working directory is different from the current directory @@ -113021,12 +113296,12 @@ const run = async () => { process.chdir(workingDirectory); } const token = process.env.GITHUB_TOKEN || (0, core_1.getInput)("token"); - const runTests = (0, core_1.getBooleanInput)("run-tests"); - const runAnalyze = (0, core_1.getBooleanInput)("run-analyze"); - const runCoverage = (0, core_1.getBooleanInput)("run-coverage"); - const runPrevCoverage = (0, core_1.getBooleanInput)("run-prev-coverage"); - const runBehindBy = (0, core_1.getBooleanInput)("run-behind-by"); - const createComment = (0, core_1.getBooleanInput)("create-comment"); + const runTests = isLocal ? true : (0, core_1.getBooleanInput)("run-tests"); + const runAnalyze = isLocal ? true : (0, core_1.getBooleanInput)("run-analyze"); + const runCoverage = isLocal ? true : (0, core_1.getBooleanInput)("run-coverage"); + const runPrevCoverage = isLocal ? true : (0, core_1.getBooleanInput)("run-prev-coverage"); + const runBehindBy = isLocal ? true : (0, core_1.getBooleanInput)("run-behind-by"); + const createComment = isLocal ? true : (0, core_1.getBooleanInput)("create-comment"); const octokit = (0, github_1.getOctokit)(token); let prevCoverage; if (runPrevCoverage) { @@ -113056,7 +113331,8 @@ const run = async () => { (0, core_1.setFailed)(`Action failed with error ${err}`); } }; -run(); +const argv = (0, minimist_1.default)(process.argv.slice(2)); +run(argv.local ?? false); //TODO: Show coverage diff in comment - which files coverage have changed. @@ -113074,6 +113350,10 @@ const core_1 = __nccwpck_require__(72614); exports.ANALYZE_SUCCESS = "✅ - Static analysis passed"; exports.ANALYZE_FAILURE = "⛔️ - Static analysis failed"; const ANALYZE_PASS_LOG = "No issues found!"; +/** + * Run static analysis on the codebase + * @returns Analysis result as a stepResponse object + */ const getAnalyze = async () => { (0, core_1.startGroup)("Analyzing code"); let response; @@ -113135,6 +113415,11 @@ const getAnalyze = async () => { return response; }; exports.getAnalyze = getAnalyze; +/** + * Get the emoji corresponding to the error type + * @param errType - Type of error + * @returns Emoji corresponding to the error type + */ const getErrEmoji = (errType) => { switch (errType) { case "error": @@ -113146,6 +113431,12 @@ const getErrEmoji = (errType) => { } }; exports.getErrEmoji = getErrEmoji; +/** + * Generate a table row for the error + * @param err - Error details + * @param type - Type of error + * @returns Formatted table row for the error + */ const generateTableRow = (err, type) => `${(0, exports.getErrEmoji)(type)}Error${err.file}${err.details}`; exports.generateTableRow = generateTableRow; @@ -113161,6 +113452,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.checkBranchStatus = void 0; const core_1 = __nccwpck_require__(72614); const core_2 = __nccwpck_require__(72614); +/** + * Check if the branch is behind the base branch + * @param octokit - Instance of GitHub client + * @param context - GitHub context + * @returns stepResponse object + */ const checkBranchStatus = async (octokit, context) => { (0, core_1.startGroup)("Check if branch is behind"); let behindByStr; @@ -113210,6 +113507,14 @@ exports.postComment = postComment; const core_1 = __nccwpck_require__(72614); const core_2 = __nccwpck_require__(72614); const SIGNATURE = `Created with Flutter code quality action`; +/** + * Create a comment for the PR + * @param analyze - Static analysis result + * @param test - Test result + * @param coverage - Coverage result + * @param behindBy - Branch status + * @returns Comment message + */ const createComment = (analyze, test, coverage, behindBy) => { (0, core_1.startGroup)("Building comment"); const isSuccess = !analyze?.error && !test?.error && !coverage?.error && !behindBy?.error; @@ -113230,6 +113535,12 @@ ${SIGNATURE} return output; }; exports.createComment = createComment; +/** + * Post a comment on the PR + * @param octokit - Instance of GitHub client + * @param commentMessage - Comment message + * @param context - GitHub context + */ async function postComment(octokit, commentMessage, context) { (0, core_1.startGroup)(`Commenting on PR`); const pr = { @@ -113294,6 +113605,12 @@ const core_1 = __nccwpck_require__(72614); const core_2 = __nccwpck_require__(72614); const utils_1 = __nccwpck_require__(8730); exports.COV_FAILURE = "⚠️ - Coverage check failed"; +/** + * Get the coverage report and compare with the previous coverage + * @param prevCoverage - Previous coverage report + * @param coverageDirectory - Directory to store coverage report + * @returns Coverage report as a stepResponse object + */ const getCoverage = (prevCoverage, coverageDirectory) => { (0, core_1.startGroup)("Checking test coverage"); let response; @@ -113362,7 +113679,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toBuffer = exports.retrievePreviousCoverage = void 0; +exports.retrievePreviousCoverage = void 0; const exec_1 = __nccwpck_require__(2259); const lcov_utils_1 = __nccwpck_require__(21641); const utils_1 = __nccwpck_require__(8730); @@ -113371,14 +113688,21 @@ const core_1 = __nccwpck_require__(72614); const core_2 = __nccwpck_require__(72614); const adm_zip_1 = __importDefault(__nccwpck_require__(98154)); const ARTIFACT_NAME = "coverage"; +/** + * Retrieve previous coverage report from the base branch + * @param octokit - Instance of GitHub client + * @param context - GitHub context + * @param coverageDirectory - Directory to store coverage report + * @returns Lcov object + */ const retrievePreviousCoverage = async (octokit, context, coverageDirectory) => { (0, core_1.startGroup)("Retrieving previous coverage"); let report; - let baseSHA, headSHA; + let baseSHA, headBranch; try { const pullDetails = await octokit.request(`GET /repos/${context.issue.owner}/${context.issue.repo}/pulls/${context.issue.number}`); baseSHA = pullDetails.data.base.sha; - headSHA = pullDetails.data.head.sha; + headBranch = pullDetails.data.head.ref; } catch (err) { console.error("Failed to get pull details", err); @@ -113397,7 +113721,7 @@ const retrievePreviousCoverage = async (octokit, context, coverageDirectory) => (0, core_2.debug)("Previous coverage report found"); const zipData = (await octokit.request(`GET /repos/${context.issue.owner}/${context.issue.repo}/actions/artifacts/${artifact.id}/zip`)).data; (0, core_2.debug)("Pulled previous coverage report"); - const zip = new adm_zip_1.default((0, exports.toBuffer)(zipData)); + const zip = new adm_zip_1.default((0, utils_1.toBuffer)(zipData)); (0, core_2.debug)("Extracted artifact"); const rawLcov = zip.readAsText(`${coverageDirectory}/${utils_1.COV_FILE}`); (0, core_2.debug)("Parsed lcov"); @@ -113418,7 +113742,7 @@ const retrievePreviousCoverage = async (octokit, context, coverageDirectory) => } if (!report) { (0, core_2.debug)("Artifact not found, will pull coverage from BASE"); - report = await generateOldCoverage(baseSHA, headSHA, coverageDirectory); + report = await generatePreviousCoverage(baseSHA, headBranch, coverageDirectory); } else { try { @@ -113434,7 +113758,14 @@ const retrievePreviousCoverage = async (octokit, context, coverageDirectory) => throw new Error("Failed to generate coverage report"); }; exports.retrievePreviousCoverage = retrievePreviousCoverage; -const generateOldCoverage = async (prev_sha, current_sha, coverage_directory) => { +/** + * Generate coverage report from the base branch + * @param prev_sha - Base branch SHA + * @param current_branch - Current branch name + * @param coverage_directory - Directory to store coverage report + * @returns Previous coverage report as Lcov object + */ +const generatePreviousCoverage = async (prev_sha, current_branch, coverage_directory) => { const artifact = new artifact_1.DefaultArtifactClient(); await (0, exec_1.exec)(`git checkout ${prev_sha}`); await (0, exec_1.exec)(`flutter test --coverage --coverage-path ${coverage_directory}/lcov.info`); @@ -113442,18 +113773,9 @@ const generateOldCoverage = async (prev_sha, current_sha, coverage_directory) => const { id, size } = await artifact.uploadArtifact(ARTIFACT_NAME + "-" + prev_sha, [`${coverage_directory}/${utils_1.COV_FILE}`], ".", {}); (0, core_2.debug)(`Artifact uploaded with id: ${id} and size: ${size}`); await (0, exec_1.exec)(`git reset --hard`); - await (0, exec_1.exec)(`git checkout ${current_sha}`); + await (0, exec_1.exec)(`git checkout ${current_branch}`); return report; }; -const toBuffer = (arrayBuffer) => { - const buffer = Buffer.alloc(arrayBuffer.byteLength); - const view = new Uint8Array(arrayBuffer); - for (let i = 0; i < buffer.length; ++i) { - buffer[i] = view[i]; - } - return buffer; -}; -exports.toBuffer = toBuffer; /***/ }), @@ -113469,6 +113791,9 @@ const core_1 = __nccwpck_require__(72614); const exec_1 = __nccwpck_require__(2259); const child_process_1 = __nccwpck_require__(32081); const core_2 = __nccwpck_require__(72614); +/** + * Push changes to the branch + */ const push = async () => { (0, core_1.startGroup)("Check for changes"); let stdout = ""; @@ -113517,6 +113842,11 @@ const core_1 = __nccwpck_require__(72614); const exec_1 = __nccwpck_require__(2259); exports.TEST_SUCCESS = "✅ - All tests passed"; exports.TEST_ERROR = "⚠️ - Error running tests"; +/** + * Run tests and return the result + * @param coverageDir - Directory to store coverage report + * @returns Test result as a stepResponse object + */ const getTest = async (coverageDir) => { (0, core_1.startGroup)("Running tests"); let response; @@ -113604,6 +113934,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setup = void 0; const core_1 = __nccwpck_require__(72614); const exec_1 = __nccwpck_require__(2259); +/** + * Set up Flutter + */ const setup = async () => { (0, core_1.startGroup)("Set up Flutter"); try { @@ -113627,11 +113960,16 @@ exports.setup = setup; "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getLcovLines = exports.importLcov = exports.COV_FILE = void 0; +exports.toBuffer = exports.getLcovLines = exports.importLcov = exports.COV_FILE = void 0; const core_1 = __nccwpck_require__(72614); const fs_1 = __nccwpck_require__(57147); const lcov_utils_1 = __nccwpck_require__(21641); exports.COV_FILE = "lcov.info"; +/** + * Import the Lcov report + * @param file_directory - Directory containing the Lcov report + * @returns Lcov report + */ const importLcov = (file_directory) => { (0, core_1.startGroup)("Retrieving coverage report"); try { @@ -113646,8 +113984,27 @@ const importLcov = (file_directory) => { } }; exports.importLcov = importLcov; +/** + * Get the total lines covered in the Lcov report + * @param report - Lcov report + * @returns Total lines covered + */ const getLcovLines = (report) => (0, lcov_utils_1.sum)(report).lines; exports.getLcovLines = getLcovLines; +/** + * Convert ArrayBuffer to Buffer + * @param arrayBuffer - ArrayBuffer to convert + * @returns Data in Buffer format + */ +const toBuffer = (arrayBuffer) => { + const buffer = Buffer.alloc(arrayBuffer.byteLength); + const view = new Uint8Array(arrayBuffer); + for (let i = 0; i < buffer.length; ++i) { + buffer[i] = view[i]; + } + return buffer; +}; +exports.toBuffer = toBuffer; /***/ }), diff --git a/src/scripts/prevCoverage.ts b/src/scripts/prevCoverage.ts index ea042c6..6cd7c42 100644 --- a/src/scripts/prevCoverage.ts +++ b/src/scripts/prevCoverage.ts @@ -24,14 +24,14 @@ export const retrievePreviousCoverage = async ( ): Promise => { startGroup("Retrieving previous coverage"); let report: Lcov | undefined; - let baseSHA: string, headSHA: string; + let baseSHA: string, headBranch: string; try { const pullDetails = await octokit.request( `GET /repos/${context.issue.owner}/${context.issue.repo}/pulls/${context.issue.number}` ); baseSHA = pullDetails.data.base.sha; - headSHA = pullDetails.data.head.sha; + headBranch = pullDetails.data.head.ref; } catch (err) { console.error("Failed to get pull details", err); throw err; @@ -79,7 +79,7 @@ export const retrievePreviousCoverage = async ( } if (!report) { debug("Artifact not found, will pull coverage from BASE"); - report = await generatePreviousCoverage(baseSHA, headSHA, coverageDirectory); + report = await generatePreviousCoverage(baseSHA, headBranch, coverageDirectory); } else { try { await exec(`rm ${coverageDirectory}/${COV_FILE}`); @@ -96,13 +96,13 @@ export const retrievePreviousCoverage = async ( /** * Generate coverage report from the base branch * @param prev_sha - Base branch SHA - * @param current_sha - Current branch SHA + * @param current_branch - Current branch name * @param coverage_directory - Directory to store coverage report * @returns Previous coverage report as Lcov object */ const generatePreviousCoverage = async ( prev_sha: string, - current_sha: string, + current_branch: string, coverage_directory: string ): Promise => { const artifact = new DefaultArtifactClient(); @@ -118,6 +118,6 @@ const generatePreviousCoverage = async ( debug(`Artifact uploaded with id: ${id} and size: ${size}`); await exec(`git reset --hard`); - await exec(`git checkout ${current_sha}`); + await exec(`git checkout ${current_branch}`); return report; };