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/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.
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..6cd7c42 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,
@@ -17,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;
@@ -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, headBranch, coverageDirectory);
} else {
try {
await exec(`rm ${coverageDirectory}/${COV_FILE}`);
@@ -86,9 +93,16 @@ 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_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();
@@ -104,15 +118,6 @@ const generateOldCoverage = 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;
};
-
-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;
+};