diff --git a/action.yml b/action.yml index 345e2f4..529b08a 100644 --- a/action.yml +++ b/action.yml @@ -1,11 +1,51 @@ -name: 'Your name here' +name: 'Run Clippy with reviewdog' description: 'Provide a description here' author: 'Your name or organization here' inputs: - milliseconds: # change this + github_token: + description: "GITHUB_TOKEN." required: true - description: 'input description here' - default: 'default value if applicable' + default: ${{ github.token }} + tool_name: + description: "Tool name to use for reviewdog reporter" + required: false + default: "clippy" + level: + description: "Report level for reviewdog [info,warning,error]" + required: false + default: "error" + reporter: + description: "Reporter of reviewdog command [github-pr-check,github-pr-review,github-check]." + required: false + default: "github-pr-check" + filter_mode: + description: | + Filtering for the reviewdog command [added,diff_context,file,nofilter]. + Default is added. + required: false + default: "added" + fail_on_error: + description: | + Exit code for reviewdog when errors are found [true,false] + Default is `false`. + required: false + default: "false" + reviewdog_flags: + description: "Additional reviewdog flags" + required: false + default: "" + workdir: + description: "Working directory relative to the root directory." + required: false + default: "." + reviewdog_version: + description: "the version of reviewdog" + required: false + default: latest + cache: + deprecation: "enable cache" + default: true + required: false runs: using: 'node16' main: 'dist/index.js' diff --git a/package-lock.json b/package-lock.json index 6db17d6..6b030d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "@actions/core": "^1.10.0" + "@actions/core": "1.10.0", + "@actions/exec": "1.1.1", + "@actions/http-client": "2.0.1", + "@actions/tool-cache": "2.0.1" }, "devDependencies": { "@types/node": "^18.11.0", @@ -34,6 +37,14 @@ "uuid": "^8.3.2" } }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, "node_modules/@actions/http-client": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", @@ -42,6 +53,33 @@ "tunnel": "^0.0.6" } }, + "node_modules/@actions/io": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", + "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" + }, + "node_modules/@actions/tool-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.1.tgz", + "integrity": "sha512-iPU+mNwrbA8jodY8eyo/0S/QqCKDajiR8OxWTnSk/SnYg0sj8Hp4QcUEVC1YFpHWXtrfbQrE13Jz4k4HXJQKcA==", + "dependencies": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.0", + "@actions/http-client": "^2.0.1", + "@actions/io": "^1.1.1", + "semver": "^6.1.0", + "uuid": "^3.3.2" + } + }, + "node_modules/@actions/tool-cache/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -9328,7 +9366,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -10301,6 +10338,14 @@ "uuid": "^8.3.2" } }, + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, "@actions/http-client": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", @@ -10309,6 +10354,31 @@ "tunnel": "^0.0.6" } }, + "@actions/io": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", + "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" + }, + "@actions/tool-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.1.tgz", + "integrity": "sha512-iPU+mNwrbA8jodY8eyo/0S/QqCKDajiR8OxWTnSk/SnYg0sj8Hp4QcUEVC1YFpHWXtrfbQrE13Jz4k4HXJQKcA==", + "requires": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.0", + "@actions/http-client": "^2.0.1", + "@actions/io": "^1.1.1", + "semver": "^6.1.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -17239,8 +17309,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "shebang-command": { "version": "2.0.0", diff --git a/package.json b/package.json index 81dc760..2a63f48 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,10 @@ "author": "", "license": "MIT", "dependencies": { - "@actions/core": "^1.10.0" + "@actions/core": "1.10.0", + "@actions/exec": "1.1.1", + "@actions/http-client": "2.0.1", + "@actions/tool-cache": "2.0.1" }, "devDependencies": { "@types/node": "^18.11.0", diff --git a/src/installer.ts b/src/installer.ts new file mode 100644 index 0000000..d95d53b --- /dev/null +++ b/src/installer.ts @@ -0,0 +1,78 @@ +import * as path from "path"; +import * as core from "@actions/core"; +import * as tc from "@actions/tool-cache"; +import * as http from "@actions/http-client"; + +export async function installReviewdog(tag: string, directory: string): Promise { + const owner = "reviewdog"; + const repo = "reviewdog"; + const version = await tagToVersion(tag, owner, repo); + + // get the os information + let platform = process.platform.toString(); + let ext = ""; + switch (platform) { + case "darwin": + platform = "Darwin"; + break; + case "linux": + platform = "Linux"; + break; + case "win32": + platform = "Windows"; + ext = ".exe"; + break; + default: + throw new Error(`unsupported platform: ${platform}`); + } + + // get the arch information + let arch: string = process.arch; + switch (arch) { + case "x64": + arch = "x86_64"; + break; + case "arm64": + break; + case "x32": + arch = "i386"; + break; + default: + throw new Error(`unsupported arch: ${arch}`); + } + + const url = `https://github.com/${owner}/${repo}/releases/download/v${version}/reviewdog_${version}_${platform}_${arch}.tar.gz`; + core.info(`downloading from ${url}`); + const archivePath = await tc.downloadTool(url); + + core.info(`extracting`); + const extractedDir = await tc.extractTar(archivePath, directory); + return path.join(extractedDir, `reviewdog${ext}`); +} + +async function tagToVersion(tag: string, owner: string, repo: string): Promise { + core.info(`finding a release for ${tag}`); + + interface Release { + tag_name: string; + } + const url = `https://github.com/${owner}/${repo}/releases/${tag}`; + const client = new http.HttpClient("clippy-action/v1"); + const headers = { [http.Headers.Accept]: "application/json" }; + const response = await client.getJson(url, headers); + + if (response.statusCode != http.HttpCodes.OK) { + core.error(`${url} returns unexpected HTTP status code: ${response.statusCode}`); + } + if (!response.result) { + throw new Error( + `unable to find '${tag}' - use 'latest' or see https://github.com/${owner}/${repo}/releases for details` + ); + } + let realTag = response.result.tag_name; + + // if version starts with 'v', remove it + realTag = realTag.replace(/^v/, ""); + + return realTag; + } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 98325c4..dc87b8f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,19 +1,84 @@ +import { promises as fs } from "fs"; +import * as os from "os"; +import * as path from "path"; + import * as core from '@actions/core' -import {wait} from './wait' +import * as exec from '@actions/exec'; + +import * as installer from "./installer" async function run(): Promise { + const runnerTmpdir = process.env["RUNNER_TEMP"] || os.tmpdir(); + const tmpdir = await fs.mkdtemp(path.join(runnerTmpdir, "reviewdog-")); + try { - const ms: string = core.getInput('milliseconds') - core.debug(`Waiting ${ms} milliseconds ...`) // debug is only output if you set the secret `ACTIONS_STEP_DEBUG` to true + const reviewdogVersion = core.getInput("reviewdog_version") || "latest"; + const toolName = core.getInput("tool_name") || "golangci"; + const level = core.getInput("level") || "error"; + const reporter = core.getInput("reporter") || "github-pr-check"; + const filterMode = core.getInput("filter_mode") || "added"; + const failOnError = core.getInput("fail_on_error") || "false"; + const reviewdogFlags = core.getInput("reviewdog_flags"); + const workdir = core.getInput("workdir") || "."; + const cwd = path.relative(process.env["GITHUB_WORKSPACE"] || process.cwd(), workdir); - core.debug(new Date().toTimeString()) - await wait(parseInt(ms, 10)) - core.debug(new Date().toTimeString()) + await core.group("Installing Rust ...", async () => { + // TODO + }); - core.setOutput('time', new Date().toTimeString()) + const reviewdog = await core.group( + "🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog", + async () => { + return await installer.installReviewdog(reviewdogVersion, tmpdir); + } + ); + + const code = await core.group("Running Clippy with reviewdog 🐶 ...", async (): Promise => { + const output = await exec.getExecOutput( + "cargo", + ["clippy", "--output-format", "short"], + { + cwd, + ignoreReturnCode: true, + } + ); + + process.env["REVIEWDOG_GITHUB_API_TOKEN"] = core.getInput("github_token"); + return await exec.exec( + reviewdog, + [ + "-f=clippy", + `-name=${toolName}`, + `-reporter=${reporter}`, + `-filter-mode=${filterMode}`, + `-fail-on-error=${failOnError}`, + `-level=${level}`, + ...parse(reviewdogFlags), + ], + { + cwd, + input: Buffer.from(output.stdout, "utf-8"), + ignoreReturnCode: true, + } + ); + }); + + if (code != 0) { + core.setFailed(`reviewdog exited with status code: ${code}`); + } } catch (error) { if (error instanceof Error) core.setFailed(error.message) } } +function parse(flags: string): string[] { + flags = flags.trim(); + if (flags === "") { + return []; + } + + // TODO: need to simulate bash? + return flags.split(/\s+/); +} + run() diff --git a/src/wait.ts b/src/wait.ts deleted file mode 100644 index b169d9a..0000000 --- a/src/wait.ts +++ /dev/null @@ -1,9 +0,0 @@ -export async function wait(milliseconds: number): Promise { - return new Promise(resolve => { - if (isNaN(milliseconds)) { - throw new Error('milliseconds not a number') - } - - setTimeout(() => resolve('done!'), milliseconds) - }) -}