From 4cc8d8f4c05bc014d13d996f60d2056a5c85f2e9 Mon Sep 17 00:00:00 2001 From: Alberto Gimeno Date: Wed, 9 Jan 2019 19:14:33 +0100 Subject: [PATCH] RuboCop action --- eslint/lib/entrypoint.sh | 5 +- eslint/lib/{create-check.js => run.js} | 0 rubocop/Dockerfile | 11 +++ rubocop/lib/entrypoint.sh | 7 ++ rubocop/lib/request.js | 29 ++++++ rubocop/lib/run.js | 122 +++++++++++++++++++++++++ 6 files changed, 173 insertions(+), 1 deletion(-) rename eslint/lib/{create-check.js => run.js} (100%) create mode 100644 rubocop/Dockerfile create mode 100755 rubocop/lib/entrypoint.sh create mode 100644 rubocop/lib/request.js create mode 100644 rubocop/lib/run.js diff --git a/eslint/lib/entrypoint.sh b/eslint/lib/entrypoint.sh index 30f9957..c67a33f 100755 --- a/eslint/lib/entrypoint.sh +++ b/eslint/lib/entrypoint.sh @@ -1,4 +1,7 @@ #!/bin/sh +set -e + npm install -NODE_PATH=node_modules node /action/lib/create-check.js + +NODE_PATH=node_modules node /action/lib/run.js diff --git a/eslint/lib/create-check.js b/eslint/lib/run.js similarity index 100% rename from eslint/lib/create-check.js rename to eslint/lib/run.js diff --git a/rubocop/Dockerfile b/rubocop/Dockerfile new file mode 100644 index 0000000..ad96775 --- /dev/null +++ b/rubocop/Dockerfile @@ -0,0 +1,11 @@ +FROM node:10-slim + +LABEL com.github.actions.name="RuboCop" +LABEL com.github.actions.description="" +LABEL com.github.actions.icon="code" +LABEL com.github.actions.color="red" +LABEL repository="" +LABEL maintainer="Alberto Gimeno " + +COPY lib /action/lib +ENTRYPOINT ["/action/lib/entrypoint.sh"] diff --git a/rubocop/lib/entrypoint.sh b/rubocop/lib/entrypoint.sh new file mode 100755 index 0000000..c3700a8 --- /dev/null +++ b/rubocop/lib/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +gem install rubocop + +node /action/lib/run.js diff --git a/rubocop/lib/request.js b/rubocop/lib/request.js new file mode 100644 index 0000000..777ad6a --- /dev/null +++ b/rubocop/lib/request.js @@ -0,0 +1,29 @@ +const https = require('https') + +module.exports = (url, options) => { + return new Promise((resolve, reject) => { + const req = https + .request(url, options, res => { + let data = '' + res.on('data', chunk => { + data += chunk + }) + res.on('end', () => { + if (res.statusCode >= 400) { + const err = new Error(`Received status code ${res.statusCode}`) + err.response = res + err.data = data + reject(err) + } else { + resolve({ res, data: JSON.parse(data) }) + } + }) + }) + .on('error', reject) + if (options.body) { + req.end(JSON.stringify(options.body)) + } else { + req.end() + } + }) +} diff --git a/rubocop/lib/run.js b/rubocop/lib/run.js new file mode 100644 index 0000000..2af2a19 --- /dev/null +++ b/rubocop/lib/run.js @@ -0,0 +1,122 @@ +const { execSync } = require('child_process') +const request = require('./request') + +const { GITHUB_SHA, GITHUB_EVENT_PATH, GITHUB_TOKEN } = process.env +const event = require(GITHUB_EVENT_PATH) +const { repository } = event +const { + owner: { login: owner } +} = repository +const { name: repo } = repository + +const checkName = 'ESLint check' + +const headers = { + 'Content-Type': 'application/json', + Accept: 'application/vnd.github.antiope-preview+json', + Authorization: `Bearer ${GITHUB_TOKEN}`, + 'User-Agent': 'eslint-action' +} + +async function createCheck() { + const body = { + name: checkName, + head_sha: GITHUB_SHA, + status: 'in_progress', + started_at: new Date() + } + + const { data } = await request(`https://api.github.com/repos/${owner}/${repo}/check-runs`, { + method: 'POST', + headers, + body + }) + const { id } = data + return id +} + +function rubocop() { + const result = spawnSync('rubocop', ['--format', 'json']) + + const { status, output } = result + const [, stderr] = output + const report = JSON.parse(stderr) + + const annotationLevels = { + refactor: 'failure', + convention: 'failure', + warning: 'warning', + error: 'failure', + fatal: 'failure' + } + + const { files, summary } = report + + const annotations = [] + for (const file of files) { + const { path, offenses } = file + for (const offense of offenses) { + const { severity, message, location } = offense + const { start_line, last_line: end_line } = location + + annotations.push({ + path, + start_line, + end_line, + annotation_level: annotationLevels[severity], + message + }) + } + } + + return { + conclusion: status > 0 ? 'failure' : 'success', + output: { + title: checkName, + summary: `${summary.offense_count} offense(s) found`, + annotations + } + } +} + +async function updateCheck(id, conclusion, output) { + const body = { + name: checkName, + head_sha: GITHUB_SHA, + status: 'completed', + completed_at: new Date(), + conclusion, + output + } + + await request(`https://api.github.com/repos/${owner}/${repo}/check-runs/${id}`, { + method: 'PATCH', + headers, + body + }) +} + +function exitWithError(err) { + console.error('Error', err.stack) + if (err.data) { + console.error(err.data) + } + process.exit(1) +} + +async function run() { + const id = await createCheck() + try { + const { conclusion, output } = rubocop() + console.log(output.summary) + await updateCheck(id, conclusion, output) + if (conclusion === 'failure') { + process.exit(78) + } + } catch (err) { + await updateCheck(id, 'failure') + exitWithError(err) + } +} + +run().catch(exitWithError)