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 - - - ${errorString.join("")} - ${warningString.join("")} - ${infoString.join("")} -
TypeFile nameDetails
-
- `; - 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 - - - ${arr.join("")} -
File Name%Passing?
-
`; - 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";