mirror of
https://github.com/giraffate/clippy-action.git
synced 2024-11-21 15:39:32 +01:00
Initial implementation
This commit is contained in:
parent
9137cd7113
commit
b165bbc18d
5 changed files with 209 additions and 143 deletions
|
@ -1,29 +1,6 @@
|
|||
import {wait} from '../src/wait'
|
||||
import * as process from 'process'
|
||||
import * as cp from 'child_process'
|
||||
import * as path from 'path'
|
||||
import {expect, test} from '@jest/globals'
|
||||
|
||||
test('throws invalid number', async () => {
|
||||
const input = parseInt('foo', 10)
|
||||
await expect(wait(input)).rejects.toThrow('milliseconds not a number')
|
||||
})
|
||||
|
||||
test('wait 500 ms', async () => {
|
||||
const start = new Date()
|
||||
await wait(500)
|
||||
const end = new Date()
|
||||
var delta = Math.abs(end.getTime() - start.getTime())
|
||||
expect(delta).toBeGreaterThan(450)
|
||||
})
|
||||
|
||||
// shows how the runner will run a javascript action with env / stdout protocol
|
||||
test('test runs', () => {
|
||||
process.env['INPUT_MILLISECONDS'] = '500'
|
||||
const np = process.execPath
|
||||
const ip = path.join(__dirname, '..', 'lib', 'main.js')
|
||||
const options: cp.ExecFileSyncOptions = {
|
||||
env: process.env
|
||||
}
|
||||
console.log(cp.execFileSync(np, [ip], options).toString())
|
||||
const x = 3
|
||||
expect(x).toBe(3)
|
||||
})
|
||||
|
|
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
BIN
dist/index.js.map
generated
vendored
Binary file not shown.
109
src/installer.ts
109
src/installer.ts
|
@ -1,78 +1,87 @@
|
|||
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";
|
||||
import * as core from '@actions/core'
|
||||
import * as http from '@actions/http-client'
|
||||
import * as path from 'path'
|
||||
import * as tc from '@actions/tool-cache'
|
||||
|
||||
export async function installReviewdog(tag: string, directory: string): Promise<string> {
|
||||
const owner = "reviewdog";
|
||||
const repo = "reviewdog";
|
||||
const version = await tagToVersion(tag, owner, repo);
|
||||
export async function installReviewdog(
|
||||
tag: string,
|
||||
directory: string
|
||||
): Promise<string> {
|
||||
const owner = 'reviewdog'
|
||||
const repo = 'reviewdog'
|
||||
const version = await tagToVersion(tag, owner, repo)
|
||||
|
||||
// get the os information
|
||||
let platform = process.platform.toString();
|
||||
let ext = "";
|
||||
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;
|
||||
case 'darwin':
|
||||
platform = 'Darwin'
|
||||
break
|
||||
case 'linux':
|
||||
platform = 'Linux'
|
||||
break
|
||||
case 'win32':
|
||||
platform = 'Windows'
|
||||
ext = '.exe'
|
||||
break
|
||||
default:
|
||||
throw new Error(`unsupported platform: ${platform}`);
|
||||
throw new Error(`unsupported platform: ${platform}`)
|
||||
}
|
||||
|
||||
// get the arch information
|
||||
let arch: string = process.arch;
|
||||
let arch: string = process.arch
|
||||
switch (arch) {
|
||||
case "x64":
|
||||
arch = "x86_64";
|
||||
break;
|
||||
case "arm64":
|
||||
break;
|
||||
case "x32":
|
||||
arch = "i386";
|
||||
break;
|
||||
case 'x64':
|
||||
arch = 'x86_64'
|
||||
break
|
||||
case 'arm64':
|
||||
break
|
||||
case 'x32':
|
||||
arch = 'i386'
|
||||
break
|
||||
default:
|
||||
throw new Error(`unsupported arch: ${arch}`);
|
||||
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);
|
||||
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}`);
|
||||
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<string> {
|
||||
core.info(`finding a release for ${tag}`);
|
||||
async function tagToVersion(
|
||||
tag: string,
|
||||
owner: string,
|
||||
repo: string
|
||||
): Promise<string> {
|
||||
core.info(`finding a release for ${tag}`)
|
||||
|
||||
interface Release {
|
||||
tag_name: string;
|
||||
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<Release>(url, headers);
|
||||
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<Release>(url, headers)
|
||||
|
||||
if (response.statusCode != http.HttpCodes.OK) {
|
||||
core.error(`${url} returns unexpected HTTP status code: ${response.statusCode}`);
|
||||
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;
|
||||
let realTag = response.result.tag_name
|
||||
|
||||
// if version starts with 'v', remove it
|
||||
realTag = realTag.replace(/^v/, "");
|
||||
realTag = realTag.replace(/^v/, '')
|
||||
|
||||
return realTag;
|
||||
return realTag
|
||||
}
|
158
src/main.ts
158
src/main.ts
|
@ -1,80 +1,160 @@
|
|||
import { promises as fs } from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from '@actions/core'
|
||||
import * as exec from '@actions/exec';
|
||||
|
||||
import * as installer from "./installer"
|
||||
import * as exec from '@actions/exec'
|
||||
import * as installer from './installer'
|
||||
import * as io from '@actions/io'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import {promises as fs} from 'fs'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const runnerTmpdir = process.env["RUNNER_TEMP"] || os.tmpdir();
|
||||
const tmpdir = await fs.mkdtemp(path.join(runnerTmpdir, "reviewdog-"));
|
||||
const runnerTmpdir = process.env['RUNNER_TEMP'] || os.tmpdir()
|
||||
const tmpdir = await fs.mkdtemp(path.join(runnerTmpdir, 'reviewdog-'))
|
||||
|
||||
try {
|
||||
const reviewdogVersion = core.getInput("reviewdog_version") || "latest";
|
||||
const toolName = core.getInput("tool_name") || "clippy";
|
||||
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);
|
||||
const reviewdogVersion = core.getInput('reviewdog_version') || 'latest'
|
||||
const toolName = core.getInput('tool_name') || 'clippy'
|
||||
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
|
||||
)
|
||||
|
||||
const reviewdog = await core.group(
|
||||
"🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog",
|
||||
'🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog',
|
||||
async () => {
|
||||
return await installer.installReviewdog(reviewdogVersion, tmpdir);
|
||||
return await installer.installReviewdog(reviewdogVersion, tmpdir)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
const code = await core.group("Running Clippy with reviewdog 🐶 ...", async (): Promise<number> => {
|
||||
const output = await exec.getExecOutput(
|
||||
"cargo",
|
||||
["clippy", "--color", "never", "-q", "--message-format", "short"],
|
||||
const code = await core.group(
|
||||
'Running Clippy with reviewdog 🐶 ...',
|
||||
async (): Promise<number> => {
|
||||
const output: string[] = []
|
||||
await exec.exec(
|
||||
'cargo',
|
||||
['clippy', '--color', 'never', '-q', '--message-format', 'json'],
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
listeners: {
|
||||
stdline: (line: string) => {
|
||||
let content: CompilerMessage
|
||||
try {
|
||||
content = JSON.parse(line)
|
||||
} catch (error) {
|
||||
core.debug('failed to parse JSON')
|
||||
return
|
||||
}
|
||||
);
|
||||
|
||||
process.env["REVIEWDOG_GITHUB_API_TOKEN"] = core.getInput("github_token");
|
||||
if (content.reason !== 'compiler-message') {
|
||||
core.debug('ignore all but `compiler-message`')
|
||||
return
|
||||
}
|
||||
|
||||
if (content.message.code === null) {
|
||||
core.debug('message code is missing, ignore it')
|
||||
return
|
||||
}
|
||||
|
||||
core.debug('this is a compiler-message!')
|
||||
const span = content.message.spans[0]
|
||||
const rendered =
|
||||
reporter === 'github-pr-review'
|
||||
? ` \n<pre><code>${content.message.rendered}</code></pre>\n__END__`
|
||||
: `${content.message.rendered}\n__END__`
|
||||
const ret = `${span.file_name}:${span.line_start}:${span.column_start}:${rendered}`
|
||||
output.push(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
core.info(`debug: ${output.join('\n')}`)
|
||||
|
||||
process.env['REVIEWDOG_GITHUB_API_TOKEN'] =
|
||||
core.getInput('github_token')
|
||||
return await exec.exec(
|
||||
reviewdog,
|
||||
[
|
||||
"-f=clippy",
|
||||
'-efm=%E%f:%l:%c:%m',
|
||||
'-efm=%Z__END__',
|
||||
'-efm=%C%m',
|
||||
'-efm=%C',
|
||||
`-name=${toolName}`,
|
||||
`-reporter=${reporter}`,
|
||||
`-filter-mode=${filterMode}`,
|
||||
`-fail-on-error=${failOnError}`,
|
||||
`-level=${level}`,
|
||||
...parse(reviewdogFlags),
|
||||
...parse(reviewdogFlags)
|
||||
],
|
||||
{
|
||||
cwd,
|
||||
input: Buffer.from(output.stderr, "utf-8"),
|
||||
ignoreReturnCode: true,
|
||||
input: Buffer.from(output.join('\n'), 'utf-8'),
|
||||
ignoreReturnCode: true
|
||||
}
|
||||
);
|
||||
});
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (code != 0) {
|
||||
core.setFailed(`reviewdog exited with status code: ${code}`);
|
||||
if (code !== 0) {
|
||||
core.setFailed(`reviewdog exited with status code: ${code}`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) core.setFailed(error.message)
|
||||
} finally {
|
||||
// clean up the temporary directory
|
||||
try {
|
||||
await io.rmRF(tmpdir)
|
||||
} catch (error) {
|
||||
// suppress errors
|
||||
// Garbage will remain, but it may be harmless.
|
||||
if (error instanceof Error) {
|
||||
core.info(`clean up failed: ${error.message}`)
|
||||
} else {
|
||||
core.info(`clean up failed: ${error}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parse(flags: string): string[] {
|
||||
flags = flags.trim();
|
||||
if (flags === "") {
|
||||
return [];
|
||||
flags = flags.trim()
|
||||
if (flags === '') {
|
||||
return []
|
||||
}
|
||||
|
||||
// TODO: need to simulate bash?
|
||||
return flags.split(/\s+/);
|
||||
return flags.split(/\s+/)
|
||||
}
|
||||
|
||||
interface CompilerMessage {
|
||||
reason: string
|
||||
message: {
|
||||
code: Code
|
||||
level: string
|
||||
message: string
|
||||
rendered: string
|
||||
spans: Span[]
|
||||
}
|
||||
}
|
||||
|
||||
interface Code {
|
||||
code: string
|
||||
explanation?: string
|
||||
}
|
||||
|
||||
interface Span {
|
||||
file_name: string
|
||||
is_primary: boolean
|
||||
line_start: number
|
||||
line_end: number
|
||||
column_start: number
|
||||
column_end: number
|
||||
}
|
||||
|
||||
run()
|
||||
|
|
Loading…
Reference in a new issue