diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 2a03005..52e0f6c 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -25,5 +25,5 @@ jobs:
git config --global user.name "github-actions"
git config --global user.email "github-actions@github.com"
git add -A
- git commit -m '[automated commit] lint format and import sort'
+ git commit -m 'ci(automated commit): lint format and import sort'
git push
diff --git a/README.md b/README.md
index 09b3b6d..145fe22 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,27 @@
# flutter-code-quality
-### Making changes
+An action that runs on PRs to format and test Flutter repos.
-Use ncc to output, not tsc or others.
-`ncc build src/main.ts`
+### Usage
+
+```yml
+name: Pull Request
+
+on:
+ pull_request:
+
+jobs:
+ code-quality:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ repository: ${{github.event.pull_request.head.repo.full_name}}
+ ref: ${{ github.head_ref }}
+ - uses: subosito/flutter-action@v2
+ with:
+ cache: true
+ - uses: ZebraDevs/flutter-code-quality@main
+ with:
+ token: ${{secrets.GITHUB_TOKEN}}
+```
diff --git a/build/build.js b/build/build.js
deleted file mode 100644
index 78c70cb..0000000
--- a/build/build.js
+++ /dev/null
@@ -1,265 +0,0 @@
-System.register("analyze", ["@actions/core", "node:child_process"], function (exports_1, context_1) {
- "use strict";
- var core_1, node_child_process_1, analyze;
- var __moduleName = context_1 && context_1.id;
- return {
- setters: [
- function (core_1_1) {
- core_1 = core_1_1;
- },
- function (node_child_process_1_1) {
- node_child_process_1 = node_child_process_1_1;
- }
- ],
- execute: function () {
- exports_1("analyze", analyze = () => {
- try {
- node_child_process_1.execSync("dart analyze", { encoding: "utf-8" });
- return "✅ - Static analysis passed";
- }
- catch (error) {
- if (error.stdout) {
- const stdout = error.stdout;
- const arr = stdout.trim().split("\n");
- const issuesList = arr.slice(2, -2).map((e) => e
- .split("-")
- .slice(0, -1)
- .map((e) => e.trim()));
- const errors = [];
- const warnings = [];
- const infos = [];
- issuesList.forEach((e) => {
- if (e[0].toLowerCase() == "error") {
- errors.push(e);
- }
- else if (e[0].toLowerCase() == "warning") {
- warnings.push(e);
- }
- else {
- infos.push(e);
- }
- });
- const errorString = errors.map((e) => {
- return `
- ⛔️ | Error | ${e[1]} | ${e[2]} |
-
`;
- });
- const warningString = warnings.map((e) => {
- return `
- ⚠️ | Warning | ${e[1]} | ${e[2]} |
-
`;
- });
- const infoString = infos.map((e) => {
- return `
- ℹ️ | Info | ${e[1]} | ${e[2]} |
-
`;
- });
- const issuesFound = arr.at(-1);
- let output = `⛔️ - Static analysis failed; ${issuesFound}
- See details
-
- | Type | File name | Details |
- ${errorString.join("")}
- ${warningString.join("")}
- ${infoString.join("")}
-
-
- `;
- output = output.replace(/(\r\n|\n|\r)/gm, "");
- return output;
- core_1.default.setOutput("err", "true");
- core_1.default.info("⛔️");
- }
- }
- return "";
- });
- }
- };
-});
-System.register("coverage", ["lcov-utils", "node:fs"], function (exports_2, context_2) {
- "use strict";
- var lcov_utils_1, node_fs_1, coverage, getOldCoverage;
- var __moduleName = context_2 && context_2.id;
- return {
- setters: [
- function (lcov_utils_1_1) {
- lcov_utils_1 = lcov_utils_1_1;
- },
- function (node_fs_1_1) {
- node_fs_1 = node_fs_1_1;
- }
- ],
- execute: function () {
- exports_2("coverage", coverage = async (oldCoverage) => {
- try {
- const contents = node_fs_1.readFileSync("coverage/lcov.info", "utf8");
- const lcov = lcov_utils_1.parse(contents);
- const digest = lcov_utils_1.sum(lcov);
- const totalPercent = digest.lines;
- let percentOutput;
- const arr = Object.values(lcov).map((e) => {
- const fileName = e.sf;
- const percent = Math.round((e.lh / e.lf) * 1000) / 10;
- const passing = percent > 96 ? "✅" : "⛔️";
- return `${fileName} | ${percent}% | ${passing} |
`;
- });
- if (oldCoverage != undefined) {
- if (oldCoverage > totalPercent) {
- percentOutput = totalPercent + `% (🔻 down from ` + oldCoverage + `)`;
- }
- else if (oldCoverage < totalPercent) {
- percentOutput = totalPercent + `% (👆 up from ` + oldCoverage + `)`;
- }
- else {
- percentOutput = totalPercent + `% (no change)`;
- }
- }
- else {
- percentOutput = totalPercent + "%";
- }
- const str = `📈 - Code coverage: ${percentOutput}
-
- See details
-
- File Name | % | Passing? |
- ${arr.join("")}
-
- `;
- return str;
- }
- catch (error) {
- return "⚠️ - Coverage check failed";
- }
- });
- exports_2("getOldCoverage", getOldCoverage = () => {
- const contents = node_fs_1.readFileSync("coverage/lcov.info", "utf8");
- const lcov = lcov_utils_1.parse(contents);
- const digest = lcov_utils_1.sum(lcov);
- return digest.lines;
- });
- }
- };
-});
-System.register("test", ["node:child_process"], function (exports_3, context_3) {
- "use strict";
- var node_child_process_2, test;
- var __moduleName = context_3 && context_3.id;
- return {
- setters: [
- function (node_child_process_2_1) {
- node_child_process_2 = node_child_process_2_1;
- }
- ],
- execute: function () {
- exports_3("test", test = () => {
- try {
- node_child_process_2.execSync("flutter test --coverage --reporter json", { encoding: "utf-8" });
- return "✅ - All tests passed";
- }
- catch (error) {
- if (error.stdout) {
- const stdout = error.stdout;
- const objStr = "[" + stdout.split("\n").join(",").slice(0, -1) + "]";
- const obj = JSON.parse(objStr);
- let failIds = [];
- obj.forEach((element) => {
- if (element.type == "testDone" &&
- element.result.toLowerCase() == "error") {
- failIds.push(element.testID);
- }
- });
- let initialString = "";
- if (failIds.length > 1) {
- initialString = `${failIds.length} tests failed`;
- }
- else if (failIds.length == 1) {
- initialString = `${failIds.length} test failed`;
- }
- const errorString = [];
- failIds.forEach((e1) => {
- const allEntries = obj.filter((e) => (e.hasOwnProperty("testID") && e.testID == e1) ||
- (e.hasOwnProperty("test") &&
- e.test.hasOwnProperty("id") &&
- e.test.id == e1));
- const entry1 = allEntries.find((e) => e.hasOwnProperty("test") && e.test.hasOwnProperty("id"));
- let testName = "Error getting test name";
- if (entry1) {
- testName = entry1.test.name.split("/test/").slice(-1);
- }
- const entry2 = allEntries.find((e) => e.hasOwnProperty("stackTrace") && e.stackTrace.length > 1);
- const entry3 = allEntries.find((e) => e.hasOwnProperty("message") &&
- e.message.length > 1 &&
- e.message.includes("EXCEPTION CAUGHT BY FLUTTER"));
- const entry4 = allEntries.find((e) => e.hasOwnProperty("error") && e.error.length > 1);
- let testDetails = "Unable to get test details. Run flutter test to replicate";
- if (entry2) {
- testDetails = entry2.stackTrace;
- }
- else if (entry3) {
- testDetails = entry3.message;
- }
- else if (entry4) {
- testDetails = entry4.error;
- }
- errorString.push("" +
- testName +
- "
`" +
- testDetails +
- "` ");
- });
- let output = `⛔️ - ${initialString}
- See details
- ${errorString.join("")}
-
- `;
- return output;
- }
- }
- return "";
- });
- }
- };
-});
-System.register("main", ["node:child_process", "@actions/core", "analyze", "coverage", "test", "@actions/github"], function (exports_4, context_4) {
- "use strict";
- var node_child_process_3, core_2, analyze_1, coverage_1, test_js_1, github_1, run;
- var __moduleName = context_4 && context_4.id;
- return {
- setters: [
- function (node_child_process_3_1) {
- node_child_process_3 = node_child_process_3_1;
- },
- function (core_2_1) {
- core_2 = core_2_1;
- },
- function (analyze_1_1) {
- analyze_1 = analyze_1_1;
- },
- function (coverage_1_1) {
- coverage_1 = coverage_1_1;
- },
- function (test_js_1_1) {
- test_js_1 = test_js_1_1;
- },
- function (github_1_1) {
- github_1 = github_1_1;
- }
- ],
- execute: function () {
- run = async () => {
- const myToken = core_2.getInput("token");
- const octokit = github_1.getOctokit(myToken);
- const theContext = github_1.context;
- core_2.startGroup("Set up Flutter");
- node_child_process_3.execSync("flutter pub get");
- node_child_process_3.execSync("dart format . -l 120");
- node_child_process_3.execSync("dart fix --apply");
- core_2.endGroup();
- const oldCoverage = coverage_1.getOldCoverage();
- const analyzeStr = analyze_1.analyze();
- const testStr = test_js_1.test();
- coverage_1.coverage(oldCoverage);
- };
- }
- };
-});
diff --git a/dist/index.js b/dist/index.js
index 5898e19..8a53ad7 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -30695,6 +30695,53 @@ const getOldCoverage = () => {
exports.getOldCoverage = getOldCoverage;
+/***/ }),
+
+/***/ 7523:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.push = void 0;
+const core_1 = __nccwpck_require__(2614);
+const exec_1 = __nccwpck_require__(2259);
+const child_process_1 = __nccwpck_require__(2081);
+const push = async () => {
+ (0, core_1.startGroup)("Check for changes");
+ let stdout = "";
+ try {
+ await (0, exec_1.exec)("git status --porcelain", [], {
+ listeners: { stdout: (data) => (stdout += data.toString()) },
+ });
+ }
+ catch (e) {
+ console.error("Unable to check if there are changes", e);
+ }
+ (0, core_1.endGroup)();
+ /// If `stdout` is empty, there are no changes
+ if (stdout != "") {
+ try {
+ (0, core_1.startGroup)("Push changes");
+ await (0, exec_1.exec)('git config --global user.name "github-actions"');
+ await (0, exec_1.exec)('git config --global user.email "github-actions@github.com"');
+ await (0, exec_1.exec)("git add -A");
+ (0, child_process_1.execSync)(`git commit -m 'chore(automated): Lint commit and format' `);
+ await (0, exec_1.exec)("git push -f");
+ console.log("Changes pushed onto branch");
+ }
+ catch (e) {
+ console.error("Unable to push changes", e);
+ (0, core_1.setFailed)("Unable to push changes to branch");
+ }
+ finally {
+ (0, core_1.endGroup)();
+ }
+ }
+};
+exports.push = push;
+
+
/***/ }),
/***/ 7046:
@@ -33096,10 +33143,8 @@ const github_1 = __nccwpck_require__(8686);
const comment_1 = __nccwpck_require__(9498);
const setup_1 = __nccwpck_require__(7046);
const behind_1 = __nccwpck_require__(8058);
+const push_1 = __nccwpck_require__(7523);
const run = async () => {
- // const comment = `Test comment, ${Date.now().toLocaleString("en_GB")}
- // Created with Flutter code quality action
- // }`;
const token = process.env.GITHUB_TOKEN || (0, core_1.getInput)("token");
const octokit = (0, github_1.getOctokit)(token);
const behindByStr = await (0, behind_1.checkBranchStatus)(octokit, github_1.context);
@@ -33110,6 +33155,7 @@ const run = async () => {
const coverageStr = await (0, coverage_1.getCoverage)(oldCoverage);
const comment = (0, comment_1.createComment)(analyzeStr, testStr, coverageStr, behindByStr);
(0, comment_1.postComment)(octokit, comment, github_1.context);
+ await (0, push_1.push)();
};
run();
diff --git a/src/analyze.ts b/src/analyze.ts
index abb1905..a0284b7 100644
--- a/src/analyze.ts
+++ b/src/analyze.ts
@@ -1,6 +1,11 @@
import { exec } from "@actions/exec";
+
import { endGroup, startGroup } from "@actions/core";
-import { analyzeDetails, analyzeErrTypes, stepResponse } from "./types";
+import { stepResponse } from "./main";
+
+export type analyzeDetails = { file: string; details: string };
+
+export type analyzeErrTypes = "error" | "warning" | "info";
export const getAnalyze = async (): Promise => {
startGroup("Analyzing code");
diff --git a/src/behind.ts b/src/behind.ts
index 0b44dc0..0cdd5e4 100644
--- a/src/behind.ts
+++ b/src/behind.ts
@@ -1,7 +1,7 @@
import { endGroup, startGroup } from "@actions/core";
import { Context } from "@actions/github/lib/context";
import { GitHub } from "@actions/github/lib/utils";
-import { stepResponse } from "./types";
+import { stepResponse } from "./main";
export const checkBranchStatus = async (
octokit: InstanceType,
diff --git a/src/comment.ts b/src/comment.ts
index e01c5db..c7f0db9 100644
--- a/src/comment.ts
+++ b/src/comment.ts
@@ -1,7 +1,7 @@
import { endGroup, startGroup } from "@actions/core";
import { Context } from "@actions/github/lib/context";
import { GitHub } from "@actions/github/lib/utils";
-import { stepResponse } from "./types";
+import { stepResponse } from "./main";
const SIGNATURE = `Created with Flutter code quality action`;
diff --git a/src/coverage.ts b/src/coverage.ts
index 1cacb38..fa308ee 100644
--- a/src/coverage.ts
+++ b/src/coverage.ts
@@ -1,7 +1,7 @@
import { Lcov, LcovDigest, parse, sum } from "lcov-utils";
import { readFileSync } from "node:fs";
import { endGroup, startGroup } from "@actions/core";
-import { stepResponse } from "./types";
+import { stepResponse } from "./main";
export const getCoverage = (oldCoverage: number | undefined): stepResponse => {
startGroup("Checking test coverage");
diff --git a/src/main.ts b/src/main.ts
index 3feec3d..67526dc 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,13 +6,11 @@ import { getOctokit, context } from "@actions/github";
import { createComment, postComment } from "./comment";
import { setup } from "./setup";
import { checkBranchStatus } from "./behind";
-import { stepResponse } from "./types";
+import { push } from "./push";
-const run = async () => {
- // const comment = `Test comment, ${Date.now().toLocaleString("en_GB")}
- // Created with Flutter code quality action
- // }`;
+export type stepResponse = { output: string; error: boolean };
+const run = async () => {
const token = process.env.GITHUB_TOKEN || getInput("token");
const octokit = getOctokit(token);
const behindByStr = await checkBranchStatus(octokit, context);
@@ -23,6 +21,7 @@ const run = async () => {
const coverageStr: stepResponse = await getCoverage(oldCoverage);
const comment = createComment(analyzeStr, testStr, coverageStr, behindByStr);
postComment(octokit, comment, context);
+ await push();
};
run();
diff --git a/src/push.ts b/src/push.ts
new file mode 100644
index 0000000..54b3b22
--- /dev/null
+++ b/src/push.ts
@@ -0,0 +1,34 @@
+import { endGroup, setFailed, setOutput, startGroup } from "@actions/core";
+import { exec } from "@actions/exec";
+import { execSync } from "child_process";
+
+export const push = async () => {
+ startGroup("Check for changes");
+ let stdout: string = "";
+ try {
+ await exec("git status --porcelain", [], {
+ listeners: { stdout: (data) => (stdout += data.toString()) },
+ });
+ } catch (e) {
+ console.error("Unable to check if there are changes", e);
+ }
+ endGroup();
+
+ /// If `stdout` is empty, there are no changes
+ if (stdout != "") {
+ try {
+ startGroup("Push changes");
+ await exec('git config --global user.name "github-actions"');
+ await exec('git config --global user.email "github-actions@github.com"');
+ await exec("git add -A");
+ execSync(`git commit -m 'chore(automated): Lint commit and format' `);
+ await exec("git push -f");
+ console.log("Changes pushed onto branch");
+ } catch (e) {
+ console.error("Unable to push changes", e);
+ setFailed("Unable to push changes to branch");
+ } finally {
+ endGroup();
+ }
+ }
+};
diff --git a/src/test.ts b/src/test.ts
index 5e42c38..68abafd 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -1,6 +1,6 @@
import { endGroup, startGroup } from "@actions/core";
import { exec } from "@actions/exec";
-import { stepResponse } from "./types";
+import { stepResponse } from "./main";
export const getTest = async (): Promise => {
startGroup("Running tests");
diff --git a/src/types.ts b/src/types.ts
deleted file mode 100644
index 550ecc0..0000000
--- a/src/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type stepResponse = { output: string; error: boolean };
-
-export type analyzeDetails = { file: string; details: string };
-
-export type analyzeErrTypes = "error" | "warning" | "info";