Skip to content
Merged
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
409 changes: 383 additions & 26 deletions dist/index.js

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion package-lock.json

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

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
Expand Down
20 changes: 12 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -39,6 +40,7 @@ const run = async () => {
console.error(e);
}
}

const behindByStr: stepResponse | undefined = runBehindBy ? await checkBranchStatus(octokit, context) : undefined;
await setup();

Expand All @@ -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.
15 changes: 15 additions & 0 deletions src/scripts/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<stepResponse> => {
startGroup("Analyzing code");
let response: stepResponse | undefined;
Expand Down Expand Up @@ -78,6 +82,11 @@ export const getAnalyze = async (): Promise<stepResponse> => {
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":
Expand All @@ -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) =>
`<tr><td>${getErrEmoji(type)}</td><td>Error</td><td>${err.file}</td><td>${err.details}</td></tr>`;
6 changes: 6 additions & 0 deletions src/scripts/behind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof GitHub>,
context: Context
Expand Down
14 changes: 14 additions & 0 deletions src/scripts/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import { debug } from "@actions/core";

const SIGNATURE = `<sub>Created with <a href='https://github.com/ZebraDevs/flutter-code-quality'>Flutter code quality action</a></sub>`;

/**
* 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,
Expand All @@ -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<typeof GitHub>, commentMessage: string, context: Context) {
startGroup(`Commenting on PR`);

Expand Down
6 changes: 6 additions & 0 deletions src/scripts/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
37 changes: 21 additions & 16 deletions src/scripts/prevCoverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@ 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";
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<typeof GitHub>,
context: Context,
coverageDirectory: string
): Promise<Lcov> => {
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;
Expand Down Expand Up @@ -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}`);
Expand All @@ -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<Lcov> => {
const artifact = new DefaultArtifactClient();
Expand All @@ -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;
};
3 changes: 3 additions & 0 deletions src/scripts/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand Down
5 changes: 5 additions & 0 deletions src/scripts/runTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<stepResponse> => {
startGroup("Running tests");
let response: stepResponse | undefined;
Expand Down
3 changes: 3 additions & 0 deletions src/scripts/setup.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
24 changes: 24 additions & 0 deletions src/scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
};