mirror of
https://github.com/golangci/golangci-lint-action.git
synced 2025-12-16 07:28:27 +00:00
Comprehensive Chech Run ID Resolution
This was surprisingly difficult. This is pretty reliable, using low-cost methods (filtering on Name comparison, Annotation count [we add one, so it should have atleast one]) and then searching for a unique string if the count is still higher than 1. I'm pretty happy about this, I think this will stand the test of time until GitHub get's their act together...
This commit is contained in:
parent
6910ba7f87
commit
aa82c092a1
3 changed files with 710 additions and 551 deletions
541
dist/post_run/index.js
vendored
541
dist/post_run/index.js
vendored
|
|
@ -2505,7 +2505,7 @@ exports.toCommandValue = toCommandValue;
|
|||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var rng = __webpack_require__(139);
|
||||
var bytesToUuid = __webpack_require__(105);
|
||||
var bytesToUuid = __webpack_require__(722);
|
||||
|
||||
// **`v1()` - Generate time-based UUID**
|
||||
//
|
||||
|
|
@ -3769,38 +3769,7 @@ var _default = version;
|
|||
exports.default = _default;
|
||||
|
||||
/***/ }),
|
||||
/* 105 */
|
||||
/***/ (function(module) {
|
||||
|
||||
/**
|
||||
* Convert array of 16 byte values to UUID string format of the form:
|
||||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
*/
|
||||
var byteToHex = [];
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
||||
}
|
||||
|
||||
function bytesToUuid(buf, offset) {
|
||||
var i = offset || 0;
|
||||
var bth = byteToHex;
|
||||
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
||||
return ([
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]]
|
||||
]).join('');
|
||||
}
|
||||
|
||||
module.exports = bytesToUuid;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 105 */,
|
||||
/* 106 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
|
||||
|
|
@ -6718,7 +6687,8 @@ const fs = __importStar(__webpack_require__(747));
|
|||
const path = __importStar(__webpack_require__(622));
|
||||
const tmp_1 = __webpack_require__(150);
|
||||
const util_1 = __webpack_require__(669);
|
||||
const cache_1 = __webpack_require__(722);
|
||||
const uuid_1 = __webpack_require__(930);
|
||||
const cache_1 = __webpack_require__(913);
|
||||
const install_1 = __webpack_require__(655);
|
||||
const version_1 = __webpack_require__(52);
|
||||
const execShellCommand = util_1.promisify(child_process_1.exec);
|
||||
|
|
@ -6787,6 +6757,8 @@ function fetchPatch() {
|
|||
function prepareEnv() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const startedAt = Date.now();
|
||||
// Resolve Check Run ID
|
||||
const resolveCheckRunIdPromise = resolveCheckRunId();
|
||||
// Prepare cache, lint and go in parallel.
|
||||
const restoreCachePromise = cache_1.restoreCache();
|
||||
const prepareLintPromise = prepareLint();
|
||||
|
|
@ -6796,8 +6768,9 @@ function prepareEnv() {
|
|||
yield installGoPromise;
|
||||
yield restoreCachePromise;
|
||||
const patchPath = yield patchPromise;
|
||||
const checkRunId = yield resolveCheckRunIdPromise;
|
||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
||||
return { lintPath, patchPath };
|
||||
return { lintPath, patchPath, checkRunId };
|
||||
});
|
||||
}
|
||||
var LintSeverity;
|
||||
|
|
@ -6851,26 +6824,78 @@ const logLintIssues = (issues) => {
|
|||
core.info(`${header} ${pos} - ${issue.Text} (${issue.FromLinter})`);
|
||||
});
|
||||
};
|
||||
function annotateLintIssues(issues) {
|
||||
var _a;
|
||||
function resolveCheckRunId() {
|
||||
var _a, _b, _c;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!issues.length) {
|
||||
let checkRunId = -1;
|
||||
if (process.env.GITHUB_ACTIONS === `true`) {
|
||||
try {
|
||||
const searchToken = uuid_1.v4();
|
||||
core.info(`::warning::Resolving GitHub CheckRunID (${searchToken})`);
|
||||
const ctx = github.context;
|
||||
const ref = (_a = ctx.payload.after) !== null && _a !== void 0 ? _a : ctx.sha;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const { data: checkRunsResponse } = yield octokit.checks
|
||||
.listForRef(Object.assign(Object.assign({}, ctx.repo), { ref, status: `in_progress`, filter: `latest` }))
|
||||
.catch((e) => {
|
||||
throw `Unable to fetch Check Run List: ${e}`;
|
||||
});
|
||||
if (checkRunsResponse.check_runs.length > 0) {
|
||||
let checkRuns = checkRunsResponse.check_runs;
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = (_b = checkRuns.filter((run) => run.name.includes(ctx.job))) !== null && _b !== void 0 ? _b : checkRuns;
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = (_c = checkRuns.filter((run) => run.output.annotations_count > 0)) !== null && _c !== void 0 ? _c : checkRuns;
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
for (const run of checkRuns) {
|
||||
try {
|
||||
if ((yield octokit.checks.listAnnotations(Object.assign(Object.assign({}, ctx.repo), { check_run_id: run.id }))).data.findIndex((annotation) => annotation.message.includes(searchToken)) !== -1) {
|
||||
checkRunId = run.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
core.info(`::debug::Error Fetching CheckRun ${run.id}: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (checkRuns[0]) {
|
||||
checkRunId = checkRuns[0].id;
|
||||
core.info(`Current Check Run is: ${checkRunId}`);
|
||||
}
|
||||
else {
|
||||
throw `Unable to resolve GitHub Check Run`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw `Fetching octokit.checks.listForRef(${ref}) returned no results`;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
core.info(`::error::Error resolving GitHub Check Run: ${e}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
core.info(`::debug::Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||
}
|
||||
return checkRunId;
|
||||
});
|
||||
}
|
||||
function annotateLintIssues(issues, checkRunId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (checkRunId === -1 || !issues.length) {
|
||||
return;
|
||||
}
|
||||
const ctx = github.context;
|
||||
const ref = ctx.payload.after;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const checkRunsPromise = octokit.checks
|
||||
.listForRef(Object.assign(Object.assign({}, ctx.repo), { ref, status: "in_progress" }))
|
||||
.catch((e) => {
|
||||
throw `Error getting Check Run Data: ${e}`;
|
||||
});
|
||||
const chunkSize = 50;
|
||||
const issueCounts = {
|
||||
notice: 0,
|
||||
warning: 0,
|
||||
failure: 0,
|
||||
};
|
||||
const ctx = github.context;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const githubAnnotations = issues.map((issue) => {
|
||||
// If/when we transition to comments, we would build the request structure here
|
||||
const annotation = {
|
||||
|
|
@ -6904,22 +6929,11 @@ function annotateLintIssues(issues) {
|
|||
}
|
||||
return annotation;
|
||||
});
|
||||
let checkRun;
|
||||
const { data: checkRunsResponse } = yield checkRunsPromise;
|
||||
if (checkRunsResponse.check_runs.length === 0) {
|
||||
throw `octokit.checks.listForRef(${ref}) returned no results`;
|
||||
}
|
||||
else {
|
||||
checkRun = checkRunsResponse.check_runs.find((run) => run.name.includes(`Lint`));
|
||||
}
|
||||
if (!(checkRun === null || checkRun === void 0 ? void 0 : checkRun.id)) {
|
||||
throw `Could not find current check run`;
|
||||
}
|
||||
const title = (_a = checkRun.output.title) !== null && _a !== void 0 ? _a : `GolangCI-Lint`;
|
||||
const title = `GolangCI-Lint`;
|
||||
const summary = `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`;
|
||||
Array.from({ length: Math.ceil(githubAnnotations.length / chunkSize) }, (v, i) => githubAnnotations.slice(i * chunkSize, i * chunkSize + chunkSize)).forEach((annotations) => {
|
||||
octokit.checks
|
||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRun === null || checkRun === void 0 ? void 0 : checkRun.id, output: {
|
||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRunId, output: {
|
||||
title,
|
||||
summary,
|
||||
annotations,
|
||||
|
|
@ -6961,14 +6975,14 @@ const printOutput = (res) => {
|
|||
core.info(res.stderr);
|
||||
}
|
||||
};
|
||||
function printLintOutput(res) {
|
||||
function printLintOutput(res, checkRunId) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let lintOutput;
|
||||
const exit_code = (_a = res.code) !== null && _a !== void 0 ? _a : 0;
|
||||
try {
|
||||
try {
|
||||
if (res.stdout) {
|
||||
try {
|
||||
// This object contains other information, such as errors and the active linters
|
||||
// TODO: Should we do something with that data?
|
||||
lintOutput = parseOutput(res.stdout);
|
||||
|
|
@ -6980,7 +6994,7 @@ function printLintOutput(res) {
|
|||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||
/* falls through */
|
||||
case `push`:
|
||||
yield annotateLintIssues(lintOutput.Issues);
|
||||
yield annotateLintIssues(lintOutput.Issues, checkRunId);
|
||||
break;
|
||||
default:
|
||||
// At this time, other events are not supported
|
||||
|
|
@ -6988,23 +7002,21 @@ function printLintOutput(res) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw `there was an error processing golangci-lint output: ${e}`;
|
||||
}
|
||||
}
|
||||
if (res.stderr) {
|
||||
core.info(res.stderr);
|
||||
}
|
||||
if (exit_code === 1) {
|
||||
if (lintOutput) {
|
||||
if (!lintOutput) {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`;
|
||||
}
|
||||
if (hasFailingIssues(lintOutput.Issues)) {
|
||||
throw `issues found`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`;
|
||||
}
|
||||
}
|
||||
else if (exit_code > 1) {
|
||||
throw `golangci-lint exit with code ${exit_code}`;
|
||||
}
|
||||
|
|
@ -7015,7 +7027,7 @@ function printLintOutput(res) {
|
|||
return core.info(`golangci-lint found no blocking issues`);
|
||||
});
|
||||
}
|
||||
function runLint(lintPath, patchPath) {
|
||||
function runLint(lintPath, patchPath, checkRunId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const debug = core.getInput(`debug`);
|
||||
if (debug.split(`,`).includes(`cache`)) {
|
||||
|
|
@ -7065,12 +7077,12 @@ function runLint(lintPath, patchPath) {
|
|||
const startedAt = Date.now();
|
||||
try {
|
||||
const res = yield execShellCommand(cmd, cmdArgs);
|
||||
yield printLintOutput(res);
|
||||
yield printLintOutput(res, checkRunId);
|
||||
}
|
||||
catch (exc) {
|
||||
// This logging passes issues to GitHub annotations but comments can be more convenient for some users.
|
||||
// TODO: support reviewdog or leaving comments by GitHub API.
|
||||
yield printLintOutput(exc);
|
||||
yield printLintOutput(exc, checkRunId);
|
||||
}
|
||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
||||
});
|
||||
|
|
@ -7078,9 +7090,9 @@ function runLint(lintPath, patchPath) {
|
|||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
const { lintPath, patchPath } = yield core.group(`prepare environment`, prepareEnv);
|
||||
const { lintPath, patchPath, checkRunId } = yield core.group(`prepare environment`, prepareEnv);
|
||||
core.addPath(path.dirname(lintPath));
|
||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath));
|
||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
||||
}
|
||||
catch (error) {
|
||||
core.error(`Failed to run: ${error}, ${error.stack}`);
|
||||
|
|
@ -52341,183 +52353,34 @@ __webpack_require__(71);
|
|||
/* 720 */,
|
||||
/* 721 */,
|
||||
/* 722 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
/***/ (function(module) {
|
||||
|
||||
"use strict";
|
||||
/**
|
||||
* Convert array of 16 byte values to UUID string format of the form:
|
||||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
*/
|
||||
var byteToHex = [];
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
||||
}
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.saveCache = exports.restoreCache = void 0;
|
||||
const cache = __importStar(__webpack_require__(638));
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const crypto = __importStar(__webpack_require__(417));
|
||||
const fs = __importStar(__webpack_require__(747));
|
||||
const path_1 = __importDefault(__webpack_require__(622));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
const utils = __importStar(__webpack_require__(443));
|
||||
function checksumFile(hashName, path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash(hashName);
|
||||
const stream = fs.createReadStream(path);
|
||||
stream.on("error", (err) => reject(err));
|
||||
stream.on("data", (chunk) => hash.update(chunk));
|
||||
stream.on("end", () => resolve(hash.digest("hex")));
|
||||
});
|
||||
function bytesToUuid(buf, offset) {
|
||||
var i = offset || 0;
|
||||
var bth = byteToHex;
|
||||
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
||||
return ([
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]]
|
||||
]).join('');
|
||||
}
|
||||
const pathExists = (path) => __awaiter(void 0, void 0, void 0, function* () { return !!(yield fs.promises.stat(path).catch(() => false)); });
|
||||
const getLintCacheDir = () => {
|
||||
return path_1.default.resolve(`${process.env.HOME}/.cache/golangci-lint`);
|
||||
};
|
||||
const getCacheDirs = () => {
|
||||
// Not existing dirs are ok here: it works.
|
||||
const skipPkgCache = core.getInput(`skip-pkg-cache`, { required: true }).trim();
|
||||
const skipBuildCache = core.getInput(`skip-build-cache`, { required: true }).trim();
|
||||
const dirs = [getLintCacheDir()];
|
||||
if (skipBuildCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/.cache/go-build from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/.cache/go-build`));
|
||||
}
|
||||
if (skipPkgCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/go/pkg from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/go/pkg`));
|
||||
}
|
||||
return dirs;
|
||||
};
|
||||
const getIntervalKey = (invalidationIntervalDays) => {
|
||||
const now = new Date();
|
||||
const secondsSinceEpoch = now.getTime() / 1000;
|
||||
const intervalNumber = Math.floor(secondsSinceEpoch / (invalidationIntervalDays * 86400));
|
||||
return intervalNumber.toString();
|
||||
};
|
||||
function buildCacheKeys() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const keys = [];
|
||||
let cacheKey = `golangci-lint.cache-`;
|
||||
keys.push(cacheKey);
|
||||
// Periodically invalidate a cache because a new code being added.
|
||||
// TODO: configure it via inputs.
|
||||
cacheKey += `${getIntervalKey(7)}-`;
|
||||
keys.push(cacheKey);
|
||||
if (yield pathExists(`go.mod`)) {
|
||||
// Add checksum to key to invalidate a cache when dependencies change.
|
||||
cacheKey += yield checksumFile(`sha1`, `go.mod`);
|
||||
}
|
||||
else {
|
||||
cacheKey += `nogomod`;
|
||||
}
|
||||
keys.push(cacheKey);
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
function restoreCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const keys = yield buildCacheKeys();
|
||||
const primaryKey = keys.pop();
|
||||
const restoreKeys = keys.reverse();
|
||||
// Tell golangci-lint to use our cache directory.
|
||||
process.env.GOLANGCI_LINT_CACHE = getLintCacheDir();
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Invalid primary key`);
|
||||
return;
|
||||
}
|
||||
core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
|
||||
try {
|
||||
const cacheKey = yield cache.restoreCache(getCacheDirs(), primaryKey, restoreKeys);
|
||||
if (!cacheKey) {
|
||||
core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(", ")}`);
|
||||
return;
|
||||
}
|
||||
// Store the matched cache key
|
||||
utils.setCacheState(cacheKey);
|
||||
core.info(`Restored cache for golangci-lint from key '${primaryKey}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
core.warning(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreCache = restoreCache;
|
||||
function saveCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// Validate inputs, this can cause task failure
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const cacheDirs = getCacheDirs();
|
||||
const primaryKey = core.getState(constants_1.State.CachePrimaryKey);
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Error retrieving key from state.`);
|
||||
return;
|
||||
}
|
||||
const state = utils.getCacheState();
|
||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
yield cache.saveCache(cacheDirs, primaryKey);
|
||||
core.info(`Saved cache for golangci-lint from paths '${cacheDirs.join(`, `)}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else if (error.name === cache.ReserveCacheError.name) {
|
||||
core.info(error.message);
|
||||
}
|
||||
else {
|
||||
core.info(`[warning] ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.saveCache = saveCache;
|
||||
|
||||
module.exports = bytesToUuid;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
|
@ -55772,7 +55635,7 @@ var __createBinding;
|
|||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var rng = __webpack_require__(139);
|
||||
var bytesToUuid = __webpack_require__(105);
|
||||
var bytesToUuid = __webpack_require__(722);
|
||||
|
||||
function v4(options, buf, offset) {
|
||||
var i = buf && offset || 0;
|
||||
|
|
@ -60629,7 +60492,187 @@ __exportStar(__webpack_require__(764), exports);
|
|||
/***/ }),
|
||||
/* 911 */,
|
||||
/* 912 */,
|
||||
/* 913 */,
|
||||
/* 913 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.saveCache = exports.restoreCache = void 0;
|
||||
const cache = __importStar(__webpack_require__(638));
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const crypto = __importStar(__webpack_require__(417));
|
||||
const fs = __importStar(__webpack_require__(747));
|
||||
const path_1 = __importDefault(__webpack_require__(622));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
const utils = __importStar(__webpack_require__(443));
|
||||
function checksumFile(hashName, path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash(hashName);
|
||||
const stream = fs.createReadStream(path);
|
||||
stream.on("error", (err) => reject(err));
|
||||
stream.on("data", (chunk) => hash.update(chunk));
|
||||
stream.on("end", () => resolve(hash.digest("hex")));
|
||||
});
|
||||
}
|
||||
const pathExists = (path) => __awaiter(void 0, void 0, void 0, function* () { return !!(yield fs.promises.stat(path).catch(() => false)); });
|
||||
const getLintCacheDir = () => {
|
||||
return path_1.default.resolve(`${process.env.HOME}/.cache/golangci-lint`);
|
||||
};
|
||||
const getCacheDirs = () => {
|
||||
// Not existing dirs are ok here: it works.
|
||||
const skipPkgCache = core.getInput(`skip-pkg-cache`, { required: true }).trim();
|
||||
const skipBuildCache = core.getInput(`skip-build-cache`, { required: true }).trim();
|
||||
const dirs = [getLintCacheDir()];
|
||||
if (skipBuildCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/.cache/go-build from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/.cache/go-build`));
|
||||
}
|
||||
if (skipPkgCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/go/pkg from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/go/pkg`));
|
||||
}
|
||||
return dirs;
|
||||
};
|
||||
const getIntervalKey = (invalidationIntervalDays) => {
|
||||
const now = new Date();
|
||||
const secondsSinceEpoch = now.getTime() / 1000;
|
||||
const intervalNumber = Math.floor(secondsSinceEpoch / (invalidationIntervalDays * 86400));
|
||||
return intervalNumber.toString();
|
||||
};
|
||||
function buildCacheKeys() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const keys = [];
|
||||
let cacheKey = `golangci-lint.cache-`;
|
||||
keys.push(cacheKey);
|
||||
// Periodically invalidate a cache because a new code being added.
|
||||
// TODO: configure it via inputs.
|
||||
cacheKey += `${getIntervalKey(7)}-`;
|
||||
keys.push(cacheKey);
|
||||
if (yield pathExists(`go.mod`)) {
|
||||
// Add checksum to key to invalidate a cache when dependencies change.
|
||||
cacheKey += yield checksumFile(`sha1`, `go.mod`);
|
||||
}
|
||||
else {
|
||||
cacheKey += `nogomod`;
|
||||
}
|
||||
keys.push(cacheKey);
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
function restoreCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const keys = yield buildCacheKeys();
|
||||
const primaryKey = keys.pop();
|
||||
const restoreKeys = keys.reverse();
|
||||
// Tell golangci-lint to use our cache directory.
|
||||
process.env.GOLANGCI_LINT_CACHE = getLintCacheDir();
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Invalid primary key`);
|
||||
return;
|
||||
}
|
||||
core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
|
||||
try {
|
||||
const cacheKey = yield cache.restoreCache(getCacheDirs(), primaryKey, restoreKeys);
|
||||
if (!cacheKey) {
|
||||
core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(", ")}`);
|
||||
return;
|
||||
}
|
||||
// Store the matched cache key
|
||||
utils.setCacheState(cacheKey);
|
||||
core.info(`Restored cache for golangci-lint from key '${primaryKey}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
core.warning(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreCache = restoreCache;
|
||||
function saveCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// Validate inputs, this can cause task failure
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const cacheDirs = getCacheDirs();
|
||||
const primaryKey = core.getState(constants_1.State.CachePrimaryKey);
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Error retrieving key from state.`);
|
||||
return;
|
||||
}
|
||||
const state = utils.getCacheState();
|
||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
yield cache.saveCache(cacheDirs, primaryKey);
|
||||
core.info(`Saved cache for golangci-lint from paths '${cacheDirs.join(`, `)}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else if (error.name === cache.ReserveCacheError.name) {
|
||||
core.info(error.message);
|
||||
}
|
||||
else {
|
||||
core.info(`[warning] ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.saveCache = saveCache;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 914 */,
|
||||
/* 915 */,
|
||||
/* 916 */,
|
||||
|
|
|
|||
541
dist/run/index.js
vendored
541
dist/run/index.js
vendored
|
|
@ -2505,7 +2505,7 @@ exports.toCommandValue = toCommandValue;
|
|||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var rng = __webpack_require__(139);
|
||||
var bytesToUuid = __webpack_require__(105);
|
||||
var bytesToUuid = __webpack_require__(722);
|
||||
|
||||
// **`v1()` - Generate time-based UUID**
|
||||
//
|
||||
|
|
@ -3769,38 +3769,7 @@ var _default = version;
|
|||
exports.default = _default;
|
||||
|
||||
/***/ }),
|
||||
/* 105 */
|
||||
/***/ (function(module) {
|
||||
|
||||
/**
|
||||
* Convert array of 16 byte values to UUID string format of the form:
|
||||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
*/
|
||||
var byteToHex = [];
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
||||
}
|
||||
|
||||
function bytesToUuid(buf, offset) {
|
||||
var i = offset || 0;
|
||||
var bth = byteToHex;
|
||||
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
||||
return ([
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]]
|
||||
]).join('');
|
||||
}
|
||||
|
||||
module.exports = bytesToUuid;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 105 */,
|
||||
/* 106 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
|
||||
|
|
@ -6728,7 +6697,8 @@ const fs = __importStar(__webpack_require__(747));
|
|||
const path = __importStar(__webpack_require__(622));
|
||||
const tmp_1 = __webpack_require__(150);
|
||||
const util_1 = __webpack_require__(669);
|
||||
const cache_1 = __webpack_require__(722);
|
||||
const uuid_1 = __webpack_require__(930);
|
||||
const cache_1 = __webpack_require__(913);
|
||||
const install_1 = __webpack_require__(655);
|
||||
const version_1 = __webpack_require__(52);
|
||||
const execShellCommand = util_1.promisify(child_process_1.exec);
|
||||
|
|
@ -6797,6 +6767,8 @@ function fetchPatch() {
|
|||
function prepareEnv() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const startedAt = Date.now();
|
||||
// Resolve Check Run ID
|
||||
const resolveCheckRunIdPromise = resolveCheckRunId();
|
||||
// Prepare cache, lint and go in parallel.
|
||||
const restoreCachePromise = cache_1.restoreCache();
|
||||
const prepareLintPromise = prepareLint();
|
||||
|
|
@ -6806,8 +6778,9 @@ function prepareEnv() {
|
|||
yield installGoPromise;
|
||||
yield restoreCachePromise;
|
||||
const patchPath = yield patchPromise;
|
||||
const checkRunId = yield resolveCheckRunIdPromise;
|
||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
||||
return { lintPath, patchPath };
|
||||
return { lintPath, patchPath, checkRunId };
|
||||
});
|
||||
}
|
||||
var LintSeverity;
|
||||
|
|
@ -6861,26 +6834,78 @@ const logLintIssues = (issues) => {
|
|||
core.info(`${header} ${pos} - ${issue.Text} (${issue.FromLinter})`);
|
||||
});
|
||||
};
|
||||
function annotateLintIssues(issues) {
|
||||
var _a;
|
||||
function resolveCheckRunId() {
|
||||
var _a, _b, _c;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!issues.length) {
|
||||
let checkRunId = -1;
|
||||
if (process.env.GITHUB_ACTIONS === `true`) {
|
||||
try {
|
||||
const searchToken = uuid_1.v4();
|
||||
core.info(`::warning::Resolving GitHub CheckRunID (${searchToken})`);
|
||||
const ctx = github.context;
|
||||
const ref = (_a = ctx.payload.after) !== null && _a !== void 0 ? _a : ctx.sha;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const { data: checkRunsResponse } = yield octokit.checks
|
||||
.listForRef(Object.assign(Object.assign({}, ctx.repo), { ref, status: `in_progress`, filter: `latest` }))
|
||||
.catch((e) => {
|
||||
throw `Unable to fetch Check Run List: ${e}`;
|
||||
});
|
||||
if (checkRunsResponse.check_runs.length > 0) {
|
||||
let checkRuns = checkRunsResponse.check_runs;
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = (_b = checkRuns.filter((run) => run.name.includes(ctx.job))) !== null && _b !== void 0 ? _b : checkRuns;
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = (_c = checkRuns.filter((run) => run.output.annotations_count > 0)) !== null && _c !== void 0 ? _c : checkRuns;
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
for (const run of checkRuns) {
|
||||
try {
|
||||
if ((yield octokit.checks.listAnnotations(Object.assign(Object.assign({}, ctx.repo), { check_run_id: run.id }))).data.findIndex((annotation) => annotation.message.includes(searchToken)) !== -1) {
|
||||
checkRunId = run.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
core.info(`::debug::Error Fetching CheckRun ${run.id}: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (checkRuns[0]) {
|
||||
checkRunId = checkRuns[0].id;
|
||||
core.info(`Current Check Run is: ${checkRunId}`);
|
||||
}
|
||||
else {
|
||||
throw `Unable to resolve GitHub Check Run`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw `Fetching octokit.checks.listForRef(${ref}) returned no results`;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
core.info(`::error::Error resolving GitHub Check Run: ${e}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
core.info(`::debug::Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||
}
|
||||
return checkRunId;
|
||||
});
|
||||
}
|
||||
function annotateLintIssues(issues, checkRunId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (checkRunId === -1 || !issues.length) {
|
||||
return;
|
||||
}
|
||||
const ctx = github.context;
|
||||
const ref = ctx.payload.after;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const checkRunsPromise = octokit.checks
|
||||
.listForRef(Object.assign(Object.assign({}, ctx.repo), { ref, status: "in_progress" }))
|
||||
.catch((e) => {
|
||||
throw `Error getting Check Run Data: ${e}`;
|
||||
});
|
||||
const chunkSize = 50;
|
||||
const issueCounts = {
|
||||
notice: 0,
|
||||
warning: 0,
|
||||
failure: 0,
|
||||
};
|
||||
const ctx = github.context;
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||
const githubAnnotations = issues.map((issue) => {
|
||||
// If/when we transition to comments, we would build the request structure here
|
||||
const annotation = {
|
||||
|
|
@ -6914,22 +6939,11 @@ function annotateLintIssues(issues) {
|
|||
}
|
||||
return annotation;
|
||||
});
|
||||
let checkRun;
|
||||
const { data: checkRunsResponse } = yield checkRunsPromise;
|
||||
if (checkRunsResponse.check_runs.length === 0) {
|
||||
throw `octokit.checks.listForRef(${ref}) returned no results`;
|
||||
}
|
||||
else {
|
||||
checkRun = checkRunsResponse.check_runs.find((run) => run.name.includes(`Lint`));
|
||||
}
|
||||
if (!(checkRun === null || checkRun === void 0 ? void 0 : checkRun.id)) {
|
||||
throw `Could not find current check run`;
|
||||
}
|
||||
const title = (_a = checkRun.output.title) !== null && _a !== void 0 ? _a : `GolangCI-Lint`;
|
||||
const title = `GolangCI-Lint`;
|
||||
const summary = `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`;
|
||||
Array.from({ length: Math.ceil(githubAnnotations.length / chunkSize) }, (v, i) => githubAnnotations.slice(i * chunkSize, i * chunkSize + chunkSize)).forEach((annotations) => {
|
||||
octokit.checks
|
||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRun === null || checkRun === void 0 ? void 0 : checkRun.id, output: {
|
||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRunId, output: {
|
||||
title,
|
||||
summary,
|
||||
annotations,
|
||||
|
|
@ -6971,14 +6985,14 @@ const printOutput = (res) => {
|
|||
core.info(res.stderr);
|
||||
}
|
||||
};
|
||||
function printLintOutput(res) {
|
||||
function printLintOutput(res, checkRunId) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let lintOutput;
|
||||
const exit_code = (_a = res.code) !== null && _a !== void 0 ? _a : 0;
|
||||
try {
|
||||
try {
|
||||
if (res.stdout) {
|
||||
try {
|
||||
// This object contains other information, such as errors and the active linters
|
||||
// TODO: Should we do something with that data?
|
||||
lintOutput = parseOutput(res.stdout);
|
||||
|
|
@ -6990,7 +7004,7 @@ function printLintOutput(res) {
|
|||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||
/* falls through */
|
||||
case `push`:
|
||||
yield annotateLintIssues(lintOutput.Issues);
|
||||
yield annotateLintIssues(lintOutput.Issues, checkRunId);
|
||||
break;
|
||||
default:
|
||||
// At this time, other events are not supported
|
||||
|
|
@ -6998,23 +7012,21 @@ function printLintOutput(res) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw `there was an error processing golangci-lint output: ${e}`;
|
||||
}
|
||||
}
|
||||
if (res.stderr) {
|
||||
core.info(res.stderr);
|
||||
}
|
||||
if (exit_code === 1) {
|
||||
if (lintOutput) {
|
||||
if (!lintOutput) {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`;
|
||||
}
|
||||
if (hasFailingIssues(lintOutput.Issues)) {
|
||||
throw `issues found`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`;
|
||||
}
|
||||
}
|
||||
else if (exit_code > 1) {
|
||||
throw `golangci-lint exit with code ${exit_code}`;
|
||||
}
|
||||
|
|
@ -7025,7 +7037,7 @@ function printLintOutput(res) {
|
|||
return core.info(`golangci-lint found no blocking issues`);
|
||||
});
|
||||
}
|
||||
function runLint(lintPath, patchPath) {
|
||||
function runLint(lintPath, patchPath, checkRunId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const debug = core.getInput(`debug`);
|
||||
if (debug.split(`,`).includes(`cache`)) {
|
||||
|
|
@ -7075,12 +7087,12 @@ function runLint(lintPath, patchPath) {
|
|||
const startedAt = Date.now();
|
||||
try {
|
||||
const res = yield execShellCommand(cmd, cmdArgs);
|
||||
yield printLintOutput(res);
|
||||
yield printLintOutput(res, checkRunId);
|
||||
}
|
||||
catch (exc) {
|
||||
// This logging passes issues to GitHub annotations but comments can be more convenient for some users.
|
||||
// TODO: support reviewdog or leaving comments by GitHub API.
|
||||
yield printLintOutput(exc);
|
||||
yield printLintOutput(exc, checkRunId);
|
||||
}
|
||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
||||
});
|
||||
|
|
@ -7088,9 +7100,9 @@ function runLint(lintPath, patchPath) {
|
|||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
const { lintPath, patchPath } = yield core.group(`prepare environment`, prepareEnv);
|
||||
const { lintPath, patchPath, checkRunId } = yield core.group(`prepare environment`, prepareEnv);
|
||||
core.addPath(path.dirname(lintPath));
|
||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath));
|
||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
||||
}
|
||||
catch (error) {
|
||||
core.error(`Failed to run: ${error}, ${error.stack}`);
|
||||
|
|
@ -52351,183 +52363,34 @@ __webpack_require__(71);
|
|||
/* 720 */,
|
||||
/* 721 */,
|
||||
/* 722 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
/***/ (function(module) {
|
||||
|
||||
"use strict";
|
||||
/**
|
||||
* Convert array of 16 byte values to UUID string format of the form:
|
||||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
*/
|
||||
var byteToHex = [];
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
||||
}
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.saveCache = exports.restoreCache = void 0;
|
||||
const cache = __importStar(__webpack_require__(638));
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const crypto = __importStar(__webpack_require__(417));
|
||||
const fs = __importStar(__webpack_require__(747));
|
||||
const path_1 = __importDefault(__webpack_require__(622));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
const utils = __importStar(__webpack_require__(443));
|
||||
function checksumFile(hashName, path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash(hashName);
|
||||
const stream = fs.createReadStream(path);
|
||||
stream.on("error", (err) => reject(err));
|
||||
stream.on("data", (chunk) => hash.update(chunk));
|
||||
stream.on("end", () => resolve(hash.digest("hex")));
|
||||
});
|
||||
function bytesToUuid(buf, offset) {
|
||||
var i = offset || 0;
|
||||
var bth = byteToHex;
|
||||
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
||||
return ([
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]], '-',
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]],
|
||||
bth[buf[i++]], bth[buf[i++]]
|
||||
]).join('');
|
||||
}
|
||||
const pathExists = (path) => __awaiter(void 0, void 0, void 0, function* () { return !!(yield fs.promises.stat(path).catch(() => false)); });
|
||||
const getLintCacheDir = () => {
|
||||
return path_1.default.resolve(`${process.env.HOME}/.cache/golangci-lint`);
|
||||
};
|
||||
const getCacheDirs = () => {
|
||||
// Not existing dirs are ok here: it works.
|
||||
const skipPkgCache = core.getInput(`skip-pkg-cache`, { required: true }).trim();
|
||||
const skipBuildCache = core.getInput(`skip-build-cache`, { required: true }).trim();
|
||||
const dirs = [getLintCacheDir()];
|
||||
if (skipBuildCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/.cache/go-build from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/.cache/go-build`));
|
||||
}
|
||||
if (skipPkgCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/go/pkg from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/go/pkg`));
|
||||
}
|
||||
return dirs;
|
||||
};
|
||||
const getIntervalKey = (invalidationIntervalDays) => {
|
||||
const now = new Date();
|
||||
const secondsSinceEpoch = now.getTime() / 1000;
|
||||
const intervalNumber = Math.floor(secondsSinceEpoch / (invalidationIntervalDays * 86400));
|
||||
return intervalNumber.toString();
|
||||
};
|
||||
function buildCacheKeys() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const keys = [];
|
||||
let cacheKey = `golangci-lint.cache-`;
|
||||
keys.push(cacheKey);
|
||||
// Periodically invalidate a cache because a new code being added.
|
||||
// TODO: configure it via inputs.
|
||||
cacheKey += `${getIntervalKey(7)}-`;
|
||||
keys.push(cacheKey);
|
||||
if (yield pathExists(`go.mod`)) {
|
||||
// Add checksum to key to invalidate a cache when dependencies change.
|
||||
cacheKey += yield checksumFile(`sha1`, `go.mod`);
|
||||
}
|
||||
else {
|
||||
cacheKey += `nogomod`;
|
||||
}
|
||||
keys.push(cacheKey);
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
function restoreCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const keys = yield buildCacheKeys();
|
||||
const primaryKey = keys.pop();
|
||||
const restoreKeys = keys.reverse();
|
||||
// Tell golangci-lint to use our cache directory.
|
||||
process.env.GOLANGCI_LINT_CACHE = getLintCacheDir();
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Invalid primary key`);
|
||||
return;
|
||||
}
|
||||
core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
|
||||
try {
|
||||
const cacheKey = yield cache.restoreCache(getCacheDirs(), primaryKey, restoreKeys);
|
||||
if (!cacheKey) {
|
||||
core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(", ")}`);
|
||||
return;
|
||||
}
|
||||
// Store the matched cache key
|
||||
utils.setCacheState(cacheKey);
|
||||
core.info(`Restored cache for golangci-lint from key '${primaryKey}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
core.warning(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreCache = restoreCache;
|
||||
function saveCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// Validate inputs, this can cause task failure
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const cacheDirs = getCacheDirs();
|
||||
const primaryKey = core.getState(constants_1.State.CachePrimaryKey);
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Error retrieving key from state.`);
|
||||
return;
|
||||
}
|
||||
const state = utils.getCacheState();
|
||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
yield cache.saveCache(cacheDirs, primaryKey);
|
||||
core.info(`Saved cache for golangci-lint from paths '${cacheDirs.join(`, `)}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else if (error.name === cache.ReserveCacheError.name) {
|
||||
core.info(error.message);
|
||||
}
|
||||
else {
|
||||
core.info(`[warning] ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.saveCache = saveCache;
|
||||
|
||||
module.exports = bytesToUuid;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
|
@ -55782,7 +55645,7 @@ var __createBinding;
|
|||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var rng = __webpack_require__(139);
|
||||
var bytesToUuid = __webpack_require__(105);
|
||||
var bytesToUuid = __webpack_require__(722);
|
||||
|
||||
function v4(options, buf, offset) {
|
||||
var i = buf && offset || 0;
|
||||
|
|
@ -60629,7 +60492,187 @@ __exportStar(__webpack_require__(764), exports);
|
|||
/***/ }),
|
||||
/* 911 */,
|
||||
/* 912 */,
|
||||
/* 913 */,
|
||||
/* 913 */
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.saveCache = exports.restoreCache = void 0;
|
||||
const cache = __importStar(__webpack_require__(638));
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const crypto = __importStar(__webpack_require__(417));
|
||||
const fs = __importStar(__webpack_require__(747));
|
||||
const path_1 = __importDefault(__webpack_require__(622));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
const utils = __importStar(__webpack_require__(443));
|
||||
function checksumFile(hashName, path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash(hashName);
|
||||
const stream = fs.createReadStream(path);
|
||||
stream.on("error", (err) => reject(err));
|
||||
stream.on("data", (chunk) => hash.update(chunk));
|
||||
stream.on("end", () => resolve(hash.digest("hex")));
|
||||
});
|
||||
}
|
||||
const pathExists = (path) => __awaiter(void 0, void 0, void 0, function* () { return !!(yield fs.promises.stat(path).catch(() => false)); });
|
||||
const getLintCacheDir = () => {
|
||||
return path_1.default.resolve(`${process.env.HOME}/.cache/golangci-lint`);
|
||||
};
|
||||
const getCacheDirs = () => {
|
||||
// Not existing dirs are ok here: it works.
|
||||
const skipPkgCache = core.getInput(`skip-pkg-cache`, { required: true }).trim();
|
||||
const skipBuildCache = core.getInput(`skip-build-cache`, { required: true }).trim();
|
||||
const dirs = [getLintCacheDir()];
|
||||
if (skipBuildCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/.cache/go-build from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/.cache/go-build`));
|
||||
}
|
||||
if (skipPkgCache.toLowerCase() == "true") {
|
||||
core.info(`Omitting ~/go/pkg from cache directories`);
|
||||
}
|
||||
else {
|
||||
dirs.push(path_1.default.resolve(`${process.env.HOME}/go/pkg`));
|
||||
}
|
||||
return dirs;
|
||||
};
|
||||
const getIntervalKey = (invalidationIntervalDays) => {
|
||||
const now = new Date();
|
||||
const secondsSinceEpoch = now.getTime() / 1000;
|
||||
const intervalNumber = Math.floor(secondsSinceEpoch / (invalidationIntervalDays * 86400));
|
||||
return intervalNumber.toString();
|
||||
};
|
||||
function buildCacheKeys() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const keys = [];
|
||||
let cacheKey = `golangci-lint.cache-`;
|
||||
keys.push(cacheKey);
|
||||
// Periodically invalidate a cache because a new code being added.
|
||||
// TODO: configure it via inputs.
|
||||
cacheKey += `${getIntervalKey(7)}-`;
|
||||
keys.push(cacheKey);
|
||||
if (yield pathExists(`go.mod`)) {
|
||||
// Add checksum to key to invalidate a cache when dependencies change.
|
||||
cacheKey += yield checksumFile(`sha1`, `go.mod`);
|
||||
}
|
||||
else {
|
||||
cacheKey += `nogomod`;
|
||||
}
|
||||
keys.push(cacheKey);
|
||||
return keys;
|
||||
});
|
||||
}
|
||||
function restoreCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const keys = yield buildCacheKeys();
|
||||
const primaryKey = keys.pop();
|
||||
const restoreKeys = keys.reverse();
|
||||
// Tell golangci-lint to use our cache directory.
|
||||
process.env.GOLANGCI_LINT_CACHE = getLintCacheDir();
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Invalid primary key`);
|
||||
return;
|
||||
}
|
||||
core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
|
||||
try {
|
||||
const cacheKey = yield cache.restoreCache(getCacheDirs(), primaryKey, restoreKeys);
|
||||
if (!cacheKey) {
|
||||
core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(", ")}`);
|
||||
return;
|
||||
}
|
||||
// Store the matched cache key
|
||||
utils.setCacheState(cacheKey);
|
||||
core.info(`Restored cache for golangci-lint from key '${primaryKey}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
core.warning(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreCache = restoreCache;
|
||||
function saveCache() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// Validate inputs, this can cause task failure
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const cacheDirs = getCacheDirs();
|
||||
const primaryKey = core.getState(constants_1.State.CachePrimaryKey);
|
||||
if (!primaryKey) {
|
||||
utils.logWarning(`Error retrieving key from state.`);
|
||||
return;
|
||||
}
|
||||
const state = utils.getCacheState();
|
||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
yield cache.saveCache(cacheDirs, primaryKey);
|
||||
core.info(`Saved cache for golangci-lint from paths '${cacheDirs.join(`, `)}' in ${Date.now() - startedAt}ms`);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.name === cache.ValidationError.name) {
|
||||
throw error;
|
||||
}
|
||||
else if (error.name === cache.ReserveCacheError.name) {
|
||||
core.info(error.message);
|
||||
}
|
||||
else {
|
||||
core.info(`[warning] ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.saveCache = saveCache;
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 914 */,
|
||||
/* 915 */,
|
||||
/* 916 */,
|
||||
|
|
|
|||
147
src/run.ts
147
src/run.ts
|
|
@ -6,6 +6,7 @@ import * as fs from "fs"
|
|||
import * as path from "path"
|
||||
import { dir } from "tmp"
|
||||
import { promisify } from "util"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
|
||||
import { restoreCache, saveCache } from "./cache"
|
||||
import { installGo, installLint } from "./install"
|
||||
|
|
@ -78,11 +79,15 @@ async function fetchPatch(): Promise<string> {
|
|||
type Env = {
|
||||
lintPath: string
|
||||
patchPath: string
|
||||
checkRunId: number
|
||||
}
|
||||
|
||||
async function prepareEnv(): Promise<Env> {
|
||||
const startedAt = Date.now()
|
||||
|
||||
// Resolve Check Run ID
|
||||
const resolveCheckRunIdPromise = resolveCheckRunId()
|
||||
|
||||
// Prepare cache, lint and go in parallel.
|
||||
const restoreCachePromise = restoreCache()
|
||||
const prepareLintPromise = prepareLint()
|
||||
|
|
@ -93,9 +98,10 @@ async function prepareEnv(): Promise<Env> {
|
|||
await installGoPromise
|
||||
await restoreCachePromise
|
||||
const patchPath = await patchPromise
|
||||
const checkRunId = await resolveCheckRunIdPromise
|
||||
|
||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`)
|
||||
return { lintPath, patchPath }
|
||||
return { lintPath, patchPath, checkRunId }
|
||||
}
|
||||
|
||||
type ExecRes = {
|
||||
|
|
@ -164,16 +170,34 @@ type GithubAnnotation = {
|
|||
end_line: number
|
||||
start_column?: number
|
||||
end_column?: number
|
||||
annotation_level: LintSeverityStrings
|
||||
title: string
|
||||
message: string
|
||||
annotation_level: LintSeverityStrings
|
||||
raw_details?: string
|
||||
}
|
||||
|
||||
type CheckRun = {
|
||||
id: number
|
||||
node_id: string
|
||||
head_sha: string
|
||||
external_id: string
|
||||
url: string
|
||||
html_url: string
|
||||
details_url: string
|
||||
status: string
|
||||
conclusion: string | null
|
||||
started_at: string
|
||||
completed_at: string | null
|
||||
output: {
|
||||
title: string
|
||||
title: string | null
|
||||
summary: string | null
|
||||
text: string | null
|
||||
annotations_count: number
|
||||
annotations_url: string
|
||||
}
|
||||
name: string
|
||||
check_suite: {
|
||||
id: number
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,29 +255,89 @@ const logLintIssues = (issues: LintIssue[]): void => {
|
|||
})
|
||||
}
|
||||
|
||||
async function annotateLintIssues(issues: LintIssue[]): Promise<void> {
|
||||
if (!issues.length) {
|
||||
return
|
||||
}
|
||||
async function resolveCheckRunId(): Promise<number> {
|
||||
let checkRunId = -1
|
||||
|
||||
if (process.env.GITHUB_ACTIONS === `true`) {
|
||||
try {
|
||||
const searchToken = uuidv4()
|
||||
core.info(`::warning::Resolving GitHub CheckRunID (${searchToken})`)
|
||||
|
||||
const ctx = github.context
|
||||
const ref = ctx.payload.after
|
||||
const ref = ctx.payload.after ?? ctx.sha
|
||||
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||
const checkRunsPromise = octokit.checks
|
||||
const { data: checkRunsResponse } = await octokit.checks
|
||||
.listForRef({
|
||||
...ctx.repo,
|
||||
ref,
|
||||
status: "in_progress",
|
||||
status: `in_progress`,
|
||||
filter: `latest`,
|
||||
})
|
||||
.catch((e) => {
|
||||
throw `Error getting Check Run Data: ${e}`
|
||||
.catch((e: string) => {
|
||||
throw `Unable to fetch Check Run List: ${e}`
|
||||
})
|
||||
|
||||
if (checkRunsResponse.check_runs.length > 0) {
|
||||
let checkRuns: CheckRun[] = checkRunsResponse.check_runs
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = checkRuns.filter((run) => run.name.includes(ctx.job)) ?? checkRuns
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
checkRuns = checkRuns.filter((run) => run.output.annotations_count > 0) ?? checkRuns
|
||||
}
|
||||
if (checkRuns.length > 1) {
|
||||
for (const run of checkRuns) {
|
||||
try {
|
||||
if (
|
||||
(
|
||||
await octokit.checks.listAnnotations({
|
||||
...ctx.repo,
|
||||
check_run_id: run.id,
|
||||
})
|
||||
).data.findIndex((annotation: { message: string }) => annotation.message.includes(searchToken)) !== -1
|
||||
) {
|
||||
checkRunId = run.id
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
core.info(`::debug::Error Fetching CheckRun ${run.id}: ${e}`)
|
||||
}
|
||||
}
|
||||
} else if (checkRuns[0]) {
|
||||
checkRunId = checkRuns[0].id
|
||||
core.info(`Current Check Run is: ${checkRunId}`)
|
||||
} else {
|
||||
throw `Unable to resolve GitHub Check Run`
|
||||
}
|
||||
} else {
|
||||
throw `Fetching octokit.checks.listForRef(${ref}) returned no results`
|
||||
}
|
||||
} catch (e) {
|
||||
core.info(`::error::Error resolving GitHub Check Run: ${e}`)
|
||||
}
|
||||
} else {
|
||||
core.info(`::debug::Not in GitHub Action Context, Skipping Check Run Resolution`)
|
||||
}
|
||||
|
||||
return checkRunId
|
||||
}
|
||||
|
||||
async function annotateLintIssues(issues: LintIssue[], checkRunId: number): Promise<void> {
|
||||
if (checkRunId === -1 || !issues.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const chunkSize = 50
|
||||
const issueCounts = {
|
||||
notice: 0,
|
||||
warning: 0,
|
||||
failure: 0,
|
||||
}
|
||||
|
||||
const ctx = github.context
|
||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||
|
||||
const githubAnnotations: GithubAnnotation[] = issues.map(
|
||||
(issue: LintIssue): GithubAnnotation => {
|
||||
// If/when we transition to comments, we would build the request structure here
|
||||
|
|
@ -291,17 +375,7 @@ async function annotateLintIssues(issues: LintIssue[]): Promise<void> {
|
|||
return annotation as GithubAnnotation
|
||||
}
|
||||
)
|
||||
let checkRun: CheckRun | undefined
|
||||
const { data: checkRunsResponse } = await checkRunsPromise
|
||||
if (checkRunsResponse.check_runs.length === 0) {
|
||||
throw `octokit.checks.listForRef(${ref}) returned no results`
|
||||
} else {
|
||||
checkRun = checkRunsResponse.check_runs.find((run) => run.name.includes(`Lint`))
|
||||
}
|
||||
if (!checkRun?.id) {
|
||||
throw `Could not find current check run`
|
||||
}
|
||||
const title = checkRun.output.title ?? `GolangCI-Lint`
|
||||
const title = `GolangCI-Lint`
|
||||
const summary = `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`
|
||||
Array.from({ length: Math.ceil(githubAnnotations.length / chunkSize) }, (v, i) =>
|
||||
githubAnnotations.slice(i * chunkSize, i * chunkSize + chunkSize)
|
||||
|
|
@ -309,7 +383,7 @@ async function annotateLintIssues(issues: LintIssue[]): Promise<void> {
|
|||
octokit.checks
|
||||
.update({
|
||||
...ctx.repo,
|
||||
check_run_id: checkRun?.id as number,
|
||||
check_run_id: checkRunId,
|
||||
output: {
|
||||
title,
|
||||
summary,
|
||||
|
|
@ -359,12 +433,12 @@ const printOutput = (res: ExecRes): void => {
|
|||
}
|
||||
}
|
||||
|
||||
async function printLintOutput(res: ExecRes): Promise<void> {
|
||||
async function printLintOutput(res: ExecRes, checkRunId: number): Promise<void> {
|
||||
let lintOutput: LintOutput | undefined
|
||||
const exit_code = res.code ?? 0
|
||||
try {
|
||||
try {
|
||||
if (res.stdout) {
|
||||
try {
|
||||
// This object contains other information, such as errors and the active linters
|
||||
// TODO: Should we do something with that data?
|
||||
lintOutput = parseOutput(res.stdout)
|
||||
|
|
@ -378,30 +452,29 @@ async function printLintOutput(res: ExecRes): Promise<void> {
|
|||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||
/* falls through */
|
||||
case `push`:
|
||||
await annotateLintIssues(lintOutput.Issues)
|
||||
await annotateLintIssues(lintOutput.Issues, checkRunId)
|
||||
break
|
||||
default:
|
||||
// At this time, other events are not supported
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw `there was an error processing golangci-lint output: ${e}`
|
||||
}
|
||||
}
|
||||
|
||||
if (res.stderr) {
|
||||
core.info(res.stderr)
|
||||
}
|
||||
|
||||
if (exit_code === 1) {
|
||||
if (lintOutput) {
|
||||
if (!lintOutput) {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`
|
||||
}
|
||||
if (hasFailingIssues(lintOutput.Issues)) {
|
||||
throw `issues found`
|
||||
}
|
||||
} else {
|
||||
throw `unexpected state, golangci-lint exited with 1, but provided no lint output`
|
||||
}
|
||||
} else if (exit_code > 1) {
|
||||
throw `golangci-lint exit with code ${exit_code}`
|
||||
}
|
||||
|
|
@ -411,7 +484,7 @@ async function printLintOutput(res: ExecRes): Promise<void> {
|
|||
return <void>core.info(`golangci-lint found no blocking issues`)
|
||||
}
|
||||
|
||||
async function runLint(lintPath: string, patchPath: string): Promise<void> {
|
||||
async function runLint(lintPath: string, patchPath: string, checkRunId: number): Promise<void> {
|
||||
const debug = core.getInput(`debug`)
|
||||
if (debug.split(`,`).includes(`cache`)) {
|
||||
const res = await execShellCommand(`${lintPath} cache status`)
|
||||
|
|
@ -467,11 +540,11 @@ async function runLint(lintPath: string, patchPath: string): Promise<void> {
|
|||
const startedAt = Date.now()
|
||||
try {
|
||||
const res = await execShellCommand(cmd, cmdArgs)
|
||||
await printLintOutput(res)
|
||||
await printLintOutput(res, checkRunId)
|
||||
} catch (exc) {
|
||||
// This logging passes issues to GitHub annotations but comments can be more convenient for some users.
|
||||
// TODO: support reviewdog or leaving comments by GitHub API.
|
||||
await printLintOutput(exc)
|
||||
await printLintOutput(exc, checkRunId)
|
||||
}
|
||||
|
||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`)
|
||||
|
|
@ -479,9 +552,9 @@ async function runLint(lintPath: string, patchPath: string): Promise<void> {
|
|||
|
||||
export async function run(): Promise<void> {
|
||||
try {
|
||||
const { lintPath, patchPath } = await core.group(`prepare environment`, prepareEnv)
|
||||
const { lintPath, patchPath, checkRunId } = await core.group(`prepare environment`, prepareEnv)
|
||||
core.addPath(path.dirname(lintPath))
|
||||
await core.group(`run golangci-lint`, () => runLint(lintPath, patchPath))
|
||||
await core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId))
|
||||
} catch (error) {
|
||||
core.error(`Failed to run: ${error}, ${error.stack}`)
|
||||
core.setFailed(error.message)
|
||||
|
|
|
|||
Loading…
Reference in a new issue