mirror of
https://github.com/golangci/golangci-lint-action.git
synced 2026-02-26 09:24:59 +00:00
CheckRun Resolution
Add a Setup (Pre-Main) action to prepare the Env and begin CheckRun Resolution. With any luck, this will solve that little issue, otherwise I give up.
This commit is contained in:
parent
2941a79185
commit
4c15f47e78
8 changed files with 68741 additions and 412 deletions
|
|
@ -38,6 +38,7 @@ inputs:
|
||||||
required: false
|
required: false
|
||||||
runs:
|
runs:
|
||||||
using: "node12"
|
using: "node12"
|
||||||
|
pre: "dist/pre/index.js"
|
||||||
main: "dist/run/index.js"
|
main: "dist/run/index.js"
|
||||||
post: "dist/post_run/index.js"
|
post: "dist/post_run/index.js"
|
||||||
branding:
|
branding:
|
||||||
|
|
|
||||||
389
dist/post_run/index.js
vendored
389
dist/post_run/index.js
vendored
|
|
@ -6678,7 +6678,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.postRun = exports.run = void 0;
|
exports.postRun = exports.run = exports.setup = void 0;
|
||||||
const core = __importStar(__webpack_require__(470));
|
const core = __importStar(__webpack_require__(470));
|
||||||
const github = __importStar(__webpack_require__(469));
|
const github = __importStar(__webpack_require__(469));
|
||||||
const ansi_styles_1 = __importDefault(__webpack_require__(663));
|
const ansi_styles_1 = __importDefault(__webpack_require__(663));
|
||||||
|
|
@ -6689,6 +6689,7 @@ const tmp_1 = __webpack_require__(150);
|
||||||
const util_1 = __webpack_require__(669);
|
const util_1 = __webpack_require__(669);
|
||||||
const uuid_1 = __webpack_require__(930);
|
const uuid_1 = __webpack_require__(930);
|
||||||
const cache_1 = __webpack_require__(913);
|
const cache_1 = __webpack_require__(913);
|
||||||
|
const constants_1 = __webpack_require__(196);
|
||||||
const install_1 = __webpack_require__(655);
|
const install_1 = __webpack_require__(655);
|
||||||
const version_1 = __webpack_require__(52);
|
const version_1 = __webpack_require__(52);
|
||||||
const execShellCommand = util_1.promisify(child_process_1.exec);
|
const execShellCommand = util_1.promisify(child_process_1.exec);
|
||||||
|
|
@ -6754,22 +6755,184 @@ function fetchPatch() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function fetchCheckSuiteId(runId) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let currentCheckSuiteId = -1;
|
||||||
|
if (runId > 0) {
|
||||||
|
try {
|
||||||
|
const { data: currentRun } = yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.actions.getWorkflowRun(Object.assign(Object.assign({}, github.context.repo), { run_id: runId }))
|
||||||
|
.catch((e) => {
|
||||||
|
throw `Unable to fetch Workflow Run: ${e}`;
|
||||||
|
});
|
||||||
|
if (currentRun.status === `in_progress`) {
|
||||||
|
// The GitHub API it's self does present the `check_suite_id` property, but it is not documented or present returned object's `type`
|
||||||
|
currentCheckSuiteId = (_a = parseInt(currentRun.check_suite_url.substr(1 + currentRun.check_suite_url.lastIndexOf(`/`)))) !== null && _a !== void 0 ? _a : -1;
|
||||||
|
// The following SHOULD work, but alas
|
||||||
|
// currentCheckSuiteId = currentRun.check_suite_id
|
||||||
|
if (currentCheckSuiteId <= 0) {
|
||||||
|
throw `Error extracting Check Suite ID from: ${currentRun.check_suite_url}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Current Run: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentCheckSuiteId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function fetchCheckSuiteRuns(checkSuiteId, name) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let checkSuiteRuns = [];
|
||||||
|
if (checkSuiteId > 0) {
|
||||||
|
try {
|
||||||
|
checkSuiteRuns = (yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listForSuite(Object.assign(Object.assign({}, github.context.repo), { check_suite_id: checkSuiteId }))
|
||||||
|
.catch((e) => {
|
||||||
|
throw `Unable to fetch Check Suite Runs List: ${e}`;
|
||||||
|
})).data.check_runs.filter((run) => run.status === `in_progress`);
|
||||||
|
if (checkSuiteRuns.length > 0 && name) {
|
||||||
|
const _checkSuiteRuns = checkSuiteRuns.filter((run) => run.name.indexOf(name) === 0 && (run.name.length === name.length || run.name[name.length] === ` `));
|
||||||
|
checkSuiteRuns = _checkSuiteRuns.length ? _checkSuiteRuns : checkSuiteRuns;
|
||||||
|
}
|
||||||
|
if (checkSuiteRuns.length === 0) {
|
||||||
|
throw `Check Suite returned 0 runs`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Check Suite Runs (${checkSuiteId}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkSuiteRuns;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function prepareCheckRunIdent(runId, runName) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const checkRunIdent = {
|
||||||
|
runId: runId !== null && runId !== void 0 ? runId : github.context.runId,
|
||||||
|
runName: runName !== null && runName !== void 0 ? runName : github.context.job,
|
||||||
|
checkRunId: -1,
|
||||||
|
};
|
||||||
|
if (process.env.GITHUB_ACTIONS === `true` && checkRunIdent.runId > 0) {
|
||||||
|
core.info(`Resolving current GitHub Check Run ${checkRunIdent.runId}`);
|
||||||
|
try {
|
||||||
|
const checkSuiteId = yield fetchCheckSuiteId(checkRunIdent.runId);
|
||||||
|
if (checkSuiteId < 0) {
|
||||||
|
throw `Unable to resolve Check Suite ID`;
|
||||||
|
}
|
||||||
|
const checkSuiteRuns = yield fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName);
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`;
|
||||||
|
}
|
||||||
|
checkRunIdent.checkSuiteId = checkSuiteId;
|
||||||
|
if (checkSuiteRuns.length === 1) {
|
||||||
|
checkRunIdent.checkRunId = checkSuiteRuns[0].id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
checkRunIdent.checkRunSearchToken = uuid_1.v4();
|
||||||
|
core.info(`::warning::[golangci-lint-action] Tagging Current GitHub CheckRun ${checkRunIdent.runId}<${checkRunIdent.checkRunSearchToken}>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error resolving Run (${checkRunIdent.runId}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||||
|
}
|
||||||
|
return checkRunIdent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function resolveCheckRunId(checkRunIdent) {
|
||||||
|
var _a, _b;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let checkRunId = checkRunIdent.checkRunId;
|
||||||
|
if (checkRunIdent.runId > 0) {
|
||||||
|
if (checkRunId <= 0) {
|
||||||
|
try {
|
||||||
|
const checkSuiteId = (_a = checkRunIdent === null || checkRunIdent === void 0 ? void 0 : checkRunIdent.checkSuiteId) !== null && _a !== void 0 ? _a : -1;
|
||||||
|
if (checkSuiteId <= 0) {
|
||||||
|
throw `No Check Suite ID`;
|
||||||
|
}
|
||||||
|
const checkRunSearchToken = (_b = checkRunIdent === null || checkRunIdent === void 0 ? void 0 : checkRunIdent.checkRunSearchToken) !== null && _b !== void 0 ? _b : ``;
|
||||||
|
if (!checkRunSearchToken) {
|
||||||
|
throw `No Check Run Search Token`;
|
||||||
|
}
|
||||||
|
const checkSuiteRuns = (yield fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName)).filter((run) => run.output.annotations_count);
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`;
|
||||||
|
}
|
||||||
|
core.info(`resolveCheckRunId() Found ${checkSuiteRuns.length} Jobs:\n` + util_1.inspect(checkSuiteRuns));
|
||||||
|
core.info(`Resolving current Check Run in Check Suite (${checkSuiteId})`);
|
||||||
|
for (const run of checkSuiteRuns) {
|
||||||
|
try {
|
||||||
|
const { data: annotations } = yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listAnnotations(Object.assign(Object.assign({}, github.context.repo), { check_run_id: run.id }));
|
||||||
|
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Check Run '${run.id}':\n` + util_1.inspect(annotations));
|
||||||
|
if (annotations.findIndex((annotation) => {
|
||||||
|
core.info(`resolveCheckRunId() Looking for Search Token (${checkRunSearchToken}) in message: ${annotation.message}`);
|
||||||
|
return annotation.message.indexOf(checkRunSearchToken) >= 0;
|
||||||
|
}) !== -1) {
|
||||||
|
core.info(`resolveCheckRunId() Found Search Token (${checkRunSearchToken}) in Check Run ${run.id}`);
|
||||||
|
checkRunId = run.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`resolveCheckRunId() Error Fetching Check Run (${run.id}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Unable to resolve Check Run ID: ${e}`);
|
||||||
|
core.info(`checkRunIdent = ` + util_1.inspect(checkRunIdent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||||
|
}
|
||||||
|
return checkRunId;
|
||||||
|
});
|
||||||
|
}
|
||||||
function prepareEnv() {
|
function prepareEnv() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
// Resolve Check Run ID
|
// Resolve Check Run ID
|
||||||
const resolveCheckRunIdPromise = resolveCheckRunId();
|
const prepareCheckRunIdentPromise = prepareCheckRunIdent();
|
||||||
// Prepare cache, lint and go in parallel.
|
// Prepare cache, lint and go in parallel.
|
||||||
const restoreCachePromise = cache_1.restoreCache();
|
const restoreCachePromise = cache_1.restoreCache();
|
||||||
const prepareLintPromise = prepareLint();
|
const prepareLintPromise = prepareLint();
|
||||||
const installGoPromise = install_1.installGo();
|
const installGoPromise = install_1.installGo();
|
||||||
const patchPromise = fetchPatch();
|
const patchPromise = fetchPatch();
|
||||||
const lintPath = yield prepareLintPromise;
|
core.saveState(constants_1.Env.LintPath, yield prepareLintPromise);
|
||||||
yield installGoPromise;
|
yield installGoPromise;
|
||||||
yield restoreCachePromise;
|
yield restoreCachePromise;
|
||||||
const patchPath = yield patchPromise;
|
core.saveState(constants_1.Env.PatchPath, yield patchPromise);
|
||||||
const checkRunId = yield resolveCheckRunIdPromise;
|
core.saveState(constants_1.Env.CheckRunIdent, JSON.stringify(yield prepareCheckRunIdentPromise));
|
||||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function restoreEnv() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const startedAt = Date.now();
|
||||||
|
const lintPath = core.getState(constants_1.Env.LintPath);
|
||||||
|
const patchPath = core.getState(constants_1.Env.PatchPath);
|
||||||
|
let checkRunId;
|
||||||
|
try {
|
||||||
|
const checkRunIdent = JSON.parse(core.getState(constants_1.Env.CheckRunIdent));
|
||||||
|
checkRunId = yield resolveCheckRunId(checkRunIdent);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Resolving Check Run ID: ${e}`);
|
||||||
|
checkRunId = -1;
|
||||||
|
}
|
||||||
|
core.info(`Restored env in ${Date.now() - startedAt}ms`);
|
||||||
return { lintPath, patchPath, checkRunId };
|
return { lintPath, patchPath, checkRunId };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -6834,78 +6997,41 @@ const logLintIssues = (issues) => {
|
||||||
` - ${issue.Text} (${issue.FromLinter})`);
|
` - ${issue.Text} (${issue.FromLinter})`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function resolveCheckRunId() {
|
const annotationFromIssue = (issue) => {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
const annotation = {
|
||||||
let jobId = -1;
|
path: issue.Pos.Filename,
|
||||||
const ctx = github.context;
|
start_line: issue.Pos.Line,
|
||||||
if (process.env.GITHUB_ACTIONS === `true` && ctx.runId) {
|
end_line: issue.Pos.Line,
|
||||||
try {
|
title: issue.FromLinter,
|
||||||
const searchToken = uuid_1.v4();
|
message: issue.Text,
|
||||||
core.info(`::warning::Attempting to resolve current GitHub Job ${ctx.runId}<${searchToken}>`);
|
annotation_level: issue.Severity,
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
};
|
||||||
let workflowJobs = (yield octokit.actions
|
if (issue.LineRange !== undefined) {
|
||||||
.listJobsForWorkflowRun(Object.assign(Object.assign({}, ctx.repo), { run_id: ctx.runId }))
|
annotation.end_line = issue.LineRange.To;
|
||||||
.catch((e) => {
|
}
|
||||||
throw `Unable to fetch Workflow Job List: ${e}`;
|
else if (issue.Pos.Column) {
|
||||||
})).data.jobs.filter((job) => job.status === `in_progress`);
|
annotation.start_column = issue.Pos.Column;
|
||||||
if (workflowJobs.length > 0) {
|
annotation.end_column = issue.Pos.Column;
|
||||||
core.info(`resolveCheckRunId() Found ${workflowJobs.length} Jobs:\n` + util_1.inspect(workflowJobs));
|
}
|
||||||
if (workflowJobs.length > 1) {
|
if (issue.Replacement !== null) {
|
||||||
const searchRegExp = new RegExp(`^` + ctx.job.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + `(\\s+\\(|$)`);
|
let replacement = ``;
|
||||||
const jobs = workflowJobs.filter((job) => searchRegExp.test(job.name));
|
if (issue.Replacement.Inline) {
|
||||||
core.info(`resolveCheckRunId() Found ${jobs.length} Jobs whose base name is '${ctx.job}'`);
|
replacement =
|
||||||
workflowJobs = jobs.length ? jobs : workflowJobs;
|
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
||||||
}
|
issue.Replacement.Inline.NewString +
|
||||||
if (workflowJobs.length > 1) {
|
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length);
|
||||||
const startedAt = Date.now();
|
|
||||||
// Sleep for MS, to allow Annotation to be captured and populated
|
|
||||||
yield ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))(10 * 1000);
|
|
||||||
core.info(`Paused for ${Date.now() - startedAt}ms`);
|
|
||||||
for (const job of workflowJobs) {
|
|
||||||
try {
|
|
||||||
const { data: annotations } = yield octokit.checks.listAnnotations(Object.assign(Object.assign({}, ctx.repo), { check_run_id: job.id }));
|
|
||||||
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Job '${job.id}':\n` + util_1.inspect(annotations));
|
|
||||||
if (annotations.findIndex((annotation) => {
|
|
||||||
core.info(`resolveCheckRunId() Looking for Search Token (${searchToken}) in message: ${annotation.message}`);
|
|
||||||
return annotation.message.includes(searchToken);
|
|
||||||
}) !== -1) {
|
|
||||||
core.info(`resolveCheckRunId() Found Search Token (${searchToken}) in Job ${job.id}`);
|
|
||||||
jobId = job.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
core.info(`resolveCheckRunId() Error Fetching Job ${job.id}: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(`resolveCheckRunId() Finished looking for Search Token`);
|
|
||||||
}
|
|
||||||
else if (workflowJobs[0]) {
|
|
||||||
jobId = workflowJobs[0].id;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw `Unable to resolve GitHub Job`;
|
|
||||||
}
|
|
||||||
core.info(`Current Job: ${jobId}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw `Fetching octokit.actions.getWorkflowRun(${process.env.GITHUB_RUN_ID}) returned no results`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
core.info(`::error::Unable to resolve GitHub Job: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else if (issue.Replacement.NewLines) {
|
||||||
core.info(`Not in GitHub Action Context, Skipping Job Resolution`);
|
replacement = issue.Replacement.NewLines.join("\n");
|
||||||
}
|
}
|
||||||
return jobId;
|
annotation.raw_details = "```suggestion\n" + replacement + "\n```";
|
||||||
});
|
}
|
||||||
}
|
return annotation;
|
||||||
|
};
|
||||||
function annotateLintIssues(issues, checkRunId) {
|
function annotateLintIssues(issues, checkRunId) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (checkRunId === -1 || !issues.length) {
|
if (checkRunId >= 0 || !issues.length) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const chunkSize = 50;
|
const chunkSize = 50;
|
||||||
const issueCounts = {
|
const issueCounts = {
|
||||||
|
|
@ -6913,52 +7039,29 @@ function annotateLintIssues(issues, checkRunId) {
|
||||||
warning: 0,
|
warning: 0,
|
||||||
failure: 0,
|
failure: 0,
|
||||||
};
|
};
|
||||||
const ctx = github.context;
|
try {
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||||
const title = `GolangCI-Lint`;
|
const title = `GolangCI-Lint`;
|
||||||
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
||||||
octokit.checks
|
octokit.checks
|
||||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRunId, output: {
|
.update(Object.assign(Object.assign({}, github.context.repo), { check_run_id: checkRunId, output: {
|
||||||
title: title,
|
title: title,
|
||||||
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map((issue) => {
|
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map((issue) => {
|
||||||
// If/when we transition to comments, we would build the request structure here
|
++issueCounts[issue.Severity];
|
||||||
const annotation = {
|
return annotationFromIssue(issue);
|
||||||
path: issue.Pos.Filename,
|
}),
|
||||||
start_line: issue.Pos.Line,
|
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
||||||
end_line: issue.Pos.Line,
|
} }))
|
||||||
title: issue.FromLinter,
|
.catch((e) => {
|
||||||
message: issue.Text,
|
throw `Error patching Check Run Data (annotations): ${e}`;
|
||||||
annotation_level: issue.Severity,
|
});
|
||||||
};
|
}
|
||||||
issueCounts[issue.Severity]++;
|
|
||||||
if (issue.LineRange !== undefined) {
|
|
||||||
annotation.end_line = issue.LineRange.To;
|
|
||||||
}
|
|
||||||
else if (issue.Pos.Column) {
|
|
||||||
annotation.start_column = issue.Pos.Column;
|
|
||||||
annotation.end_column = issue.Pos.Column;
|
|
||||||
}
|
|
||||||
if (issue.Replacement !== null) {
|
|
||||||
let replacement = ``;
|
|
||||||
if (issue.Replacement.Inline) {
|
|
||||||
replacement =
|
|
||||||
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
|
||||||
issue.Replacement.Inline.NewString +
|
|
||||||
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length);
|
|
||||||
}
|
|
||||||
else if (issue.Replacement.NewLines) {
|
|
||||||
replacement = issue.Replacement.NewLines.join("\n");
|
|
||||||
}
|
|
||||||
annotation.raw_details = "```suggestion\n" + replacement + "\n```";
|
|
||||||
}
|
|
||||||
return annotation;
|
|
||||||
}),
|
|
||||||
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
|
||||||
} }))
|
|
||||||
.catch((e) => {
|
|
||||||
throw `Error patching Check Run Data (annotations): ${e}`;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Annotating Lint Issues: ${e}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const printOutput = (res) => {
|
const printOutput = (res) => {
|
||||||
|
|
@ -6979,19 +7082,23 @@ function processLintOutput(res, checkRunId) {
|
||||||
;
|
;
|
||||||
({ Issues: lintIssues } = parseOutput(res.stdout));
|
({ Issues: lintIssues } = parseOutput(res.stdout));
|
||||||
if (lintIssues.length) {
|
if (lintIssues.length) {
|
||||||
logLintIssues(lintIssues);
|
if (!(yield ((eventName) => __awaiter(this, void 0, void 0, function* () {
|
||||||
// We can only Annotate (or Comment) on Push or Pull Request
|
// We can only Annotate (or Comment) on Push or Pull Request
|
||||||
switch (github.context.eventName) {
|
switch (eventName) {
|
||||||
case `pull_request`:
|
case `pull_request`:
|
||||||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||||
/* falls through */
|
/* falls through */
|
||||||
case `push`:
|
case `push`:
|
||||||
yield annotateLintIssues(lintIssues, checkRunId);
|
return yield annotateLintIssues(lintIssues, checkRunId);
|
||||||
break;
|
default:
|
||||||
default:
|
// At this time, other events are not supported
|
||||||
// At this time, other events are not supported
|
return false;
|
||||||
break;
|
}
|
||||||
|
}))(github.context.eventName))) {
|
||||||
|
core.info(`::add-matcher::matchers-golangci-lint-action.json`);
|
||||||
}
|
}
|
||||||
|
// Log Issues at the end to allow failed GitHub Actions above to set the Problem Matcher
|
||||||
|
logLintIssues(lintIssues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
@ -7090,10 +7197,22 @@ function runLint(lintPath, patchPath, checkRunId) {
|
||||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function setup() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
yield core.group(`prepare environment`, prepareEnv);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.error(`Failed to prepare: ${error}, ${error.stack}`);
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.setup = setup;
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
const { lintPath, patchPath, checkRunId } = yield core.group(`prepare environment`, prepareEnv);
|
const { lintPath, patchPath, checkRunId } = yield core.group(`restore environment`, restoreEnv);
|
||||||
core.addPath(path.dirname(lintPath));
|
core.addPath(path.dirname(lintPath));
|
||||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
||||||
}
|
}
|
||||||
|
|
@ -7163,7 +7282,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.RefKey = exports.Events = exports.State = exports.Inputs = void 0;
|
exports.RefKey = exports.Env = exports.Events = exports.State = exports.Inputs = void 0;
|
||||||
var Inputs;
|
var Inputs;
|
||||||
(function (Inputs) {
|
(function (Inputs) {
|
||||||
Inputs["Key"] = "key";
|
Inputs["Key"] = "key";
|
||||||
|
|
@ -7181,6 +7300,12 @@ var Events;
|
||||||
Events["Push"] = "push";
|
Events["Push"] = "push";
|
||||||
Events["PullRequest"] = "pull_request";
|
Events["PullRequest"] = "pull_request";
|
||||||
})(Events = exports.Events || (exports.Events = {}));
|
})(Events = exports.Events || (exports.Events = {}));
|
||||||
|
var Env;
|
||||||
|
(function (Env) {
|
||||||
|
Env["LintPath"] = "GOLANGCI_LINT_ACTION_LINT_PATH";
|
||||||
|
Env["PatchPath"] = "GOLANGCI_LINT_ACTION_PATCH_PATH";
|
||||||
|
Env["CheckRunIdent"] = "GOLANGCI_LINT_ACTION_CHECK_RUN_IDENT";
|
||||||
|
})(Env = exports.Env || (exports.Env = {}));
|
||||||
exports.RefKey = "GITHUB_REF";
|
exports.RefKey = "GITHUB_REF";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
67912
dist/pre/index.js
vendored
Normal file
67912
dist/pre/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
389
dist/run/index.js
vendored
389
dist/run/index.js
vendored
|
|
@ -6688,7 +6688,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.postRun = exports.run = void 0;
|
exports.postRun = exports.run = exports.setup = void 0;
|
||||||
const core = __importStar(__webpack_require__(470));
|
const core = __importStar(__webpack_require__(470));
|
||||||
const github = __importStar(__webpack_require__(469));
|
const github = __importStar(__webpack_require__(469));
|
||||||
const ansi_styles_1 = __importDefault(__webpack_require__(663));
|
const ansi_styles_1 = __importDefault(__webpack_require__(663));
|
||||||
|
|
@ -6699,6 +6699,7 @@ const tmp_1 = __webpack_require__(150);
|
||||||
const util_1 = __webpack_require__(669);
|
const util_1 = __webpack_require__(669);
|
||||||
const uuid_1 = __webpack_require__(930);
|
const uuid_1 = __webpack_require__(930);
|
||||||
const cache_1 = __webpack_require__(913);
|
const cache_1 = __webpack_require__(913);
|
||||||
|
const constants_1 = __webpack_require__(196);
|
||||||
const install_1 = __webpack_require__(655);
|
const install_1 = __webpack_require__(655);
|
||||||
const version_1 = __webpack_require__(52);
|
const version_1 = __webpack_require__(52);
|
||||||
const execShellCommand = util_1.promisify(child_process_1.exec);
|
const execShellCommand = util_1.promisify(child_process_1.exec);
|
||||||
|
|
@ -6764,22 +6765,184 @@ function fetchPatch() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function fetchCheckSuiteId(runId) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let currentCheckSuiteId = -1;
|
||||||
|
if (runId > 0) {
|
||||||
|
try {
|
||||||
|
const { data: currentRun } = yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.actions.getWorkflowRun(Object.assign(Object.assign({}, github.context.repo), { run_id: runId }))
|
||||||
|
.catch((e) => {
|
||||||
|
throw `Unable to fetch Workflow Run: ${e}`;
|
||||||
|
});
|
||||||
|
if (currentRun.status === `in_progress`) {
|
||||||
|
// The GitHub API it's self does present the `check_suite_id` property, but it is not documented or present returned object's `type`
|
||||||
|
currentCheckSuiteId = (_a = parseInt(currentRun.check_suite_url.substr(1 + currentRun.check_suite_url.lastIndexOf(`/`)))) !== null && _a !== void 0 ? _a : -1;
|
||||||
|
// The following SHOULD work, but alas
|
||||||
|
// currentCheckSuiteId = currentRun.check_suite_id
|
||||||
|
if (currentCheckSuiteId <= 0) {
|
||||||
|
throw `Error extracting Check Suite ID from: ${currentRun.check_suite_url}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Current Run: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentCheckSuiteId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function fetchCheckSuiteRuns(checkSuiteId, name) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let checkSuiteRuns = [];
|
||||||
|
if (checkSuiteId > 0) {
|
||||||
|
try {
|
||||||
|
checkSuiteRuns = (yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listForSuite(Object.assign(Object.assign({}, github.context.repo), { check_suite_id: checkSuiteId }))
|
||||||
|
.catch((e) => {
|
||||||
|
throw `Unable to fetch Check Suite Runs List: ${e}`;
|
||||||
|
})).data.check_runs.filter((run) => run.status === `in_progress`);
|
||||||
|
if (checkSuiteRuns.length > 0 && name) {
|
||||||
|
const _checkSuiteRuns = checkSuiteRuns.filter((run) => run.name.indexOf(name) === 0 && (run.name.length === name.length || run.name[name.length] === ` `));
|
||||||
|
checkSuiteRuns = _checkSuiteRuns.length ? _checkSuiteRuns : checkSuiteRuns;
|
||||||
|
}
|
||||||
|
if (checkSuiteRuns.length === 0) {
|
||||||
|
throw `Check Suite returned 0 runs`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Check Suite Runs (${checkSuiteId}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkSuiteRuns;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function prepareCheckRunIdent(runId, runName) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const checkRunIdent = {
|
||||||
|
runId: runId !== null && runId !== void 0 ? runId : github.context.runId,
|
||||||
|
runName: runName !== null && runName !== void 0 ? runName : github.context.job,
|
||||||
|
checkRunId: -1,
|
||||||
|
};
|
||||||
|
if (process.env.GITHUB_ACTIONS === `true` && checkRunIdent.runId > 0) {
|
||||||
|
core.info(`Resolving current GitHub Check Run ${checkRunIdent.runId}`);
|
||||||
|
try {
|
||||||
|
const checkSuiteId = yield fetchCheckSuiteId(checkRunIdent.runId);
|
||||||
|
if (checkSuiteId < 0) {
|
||||||
|
throw `Unable to resolve Check Suite ID`;
|
||||||
|
}
|
||||||
|
const checkSuiteRuns = yield fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName);
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`;
|
||||||
|
}
|
||||||
|
checkRunIdent.checkSuiteId = checkSuiteId;
|
||||||
|
if (checkSuiteRuns.length === 1) {
|
||||||
|
checkRunIdent.checkRunId = checkSuiteRuns[0].id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
checkRunIdent.checkRunSearchToken = uuid_1.v4();
|
||||||
|
core.info(`::warning::[golangci-lint-action] Tagging Current GitHub CheckRun ${checkRunIdent.runId}<${checkRunIdent.checkRunSearchToken}>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error resolving Run (${checkRunIdent.runId}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||||
|
}
|
||||||
|
return checkRunIdent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function resolveCheckRunId(checkRunIdent) {
|
||||||
|
var _a, _b;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let checkRunId = checkRunIdent.checkRunId;
|
||||||
|
if (checkRunIdent.runId > 0) {
|
||||||
|
if (checkRunId <= 0) {
|
||||||
|
try {
|
||||||
|
const checkSuiteId = (_a = checkRunIdent === null || checkRunIdent === void 0 ? void 0 : checkRunIdent.checkSuiteId) !== null && _a !== void 0 ? _a : -1;
|
||||||
|
if (checkSuiteId <= 0) {
|
||||||
|
throw `No Check Suite ID`;
|
||||||
|
}
|
||||||
|
const checkRunSearchToken = (_b = checkRunIdent === null || checkRunIdent === void 0 ? void 0 : checkRunIdent.checkRunSearchToken) !== null && _b !== void 0 ? _b : ``;
|
||||||
|
if (!checkRunSearchToken) {
|
||||||
|
throw `No Check Run Search Token`;
|
||||||
|
}
|
||||||
|
const checkSuiteRuns = (yield fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName)).filter((run) => run.output.annotations_count);
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`;
|
||||||
|
}
|
||||||
|
core.info(`resolveCheckRunId() Found ${checkSuiteRuns.length} Jobs:\n` + util_1.inspect(checkSuiteRuns));
|
||||||
|
core.info(`Resolving current Check Run in Check Suite (${checkSuiteId})`);
|
||||||
|
for (const run of checkSuiteRuns) {
|
||||||
|
try {
|
||||||
|
const { data: annotations } = yield github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listAnnotations(Object.assign(Object.assign({}, github.context.repo), { check_run_id: run.id }));
|
||||||
|
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Check Run '${run.id}':\n` + util_1.inspect(annotations));
|
||||||
|
if (annotations.findIndex((annotation) => {
|
||||||
|
core.info(`resolveCheckRunId() Looking for Search Token (${checkRunSearchToken}) in message: ${annotation.message}`);
|
||||||
|
return annotation.message.indexOf(checkRunSearchToken) >= 0;
|
||||||
|
}) !== -1) {
|
||||||
|
core.info(`resolveCheckRunId() Found Search Token (${checkRunSearchToken}) in Check Run ${run.id}`);
|
||||||
|
checkRunId = run.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`resolveCheckRunId() Error Fetching Check Run (${run.id}): ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Unable to resolve Check Run ID: ${e}`);
|
||||||
|
core.info(`checkRunIdent = ` + util_1.inspect(checkRunIdent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`);
|
||||||
|
}
|
||||||
|
return checkRunId;
|
||||||
|
});
|
||||||
|
}
|
||||||
function prepareEnv() {
|
function prepareEnv() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
// Resolve Check Run ID
|
// Resolve Check Run ID
|
||||||
const resolveCheckRunIdPromise = resolveCheckRunId();
|
const prepareCheckRunIdentPromise = prepareCheckRunIdent();
|
||||||
// Prepare cache, lint and go in parallel.
|
// Prepare cache, lint and go in parallel.
|
||||||
const restoreCachePromise = cache_1.restoreCache();
|
const restoreCachePromise = cache_1.restoreCache();
|
||||||
const prepareLintPromise = prepareLint();
|
const prepareLintPromise = prepareLint();
|
||||||
const installGoPromise = install_1.installGo();
|
const installGoPromise = install_1.installGo();
|
||||||
const patchPromise = fetchPatch();
|
const patchPromise = fetchPatch();
|
||||||
const lintPath = yield prepareLintPromise;
|
core.saveState(constants_1.Env.LintPath, yield prepareLintPromise);
|
||||||
yield installGoPromise;
|
yield installGoPromise;
|
||||||
yield restoreCachePromise;
|
yield restoreCachePromise;
|
||||||
const patchPath = yield patchPromise;
|
core.saveState(constants_1.Env.PatchPath, yield patchPromise);
|
||||||
const checkRunId = yield resolveCheckRunIdPromise;
|
core.saveState(constants_1.Env.CheckRunIdent, JSON.stringify(yield prepareCheckRunIdentPromise));
|
||||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
core.info(`Prepared env in ${Date.now() - startedAt}ms`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function restoreEnv() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const startedAt = Date.now();
|
||||||
|
const lintPath = core.getState(constants_1.Env.LintPath);
|
||||||
|
const patchPath = core.getState(constants_1.Env.PatchPath);
|
||||||
|
let checkRunId;
|
||||||
|
try {
|
||||||
|
const checkRunIdent = JSON.parse(core.getState(constants_1.Env.CheckRunIdent));
|
||||||
|
checkRunId = yield resolveCheckRunId(checkRunIdent);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Resolving Check Run ID: ${e}`);
|
||||||
|
checkRunId = -1;
|
||||||
|
}
|
||||||
|
core.info(`Restored env in ${Date.now() - startedAt}ms`);
|
||||||
return { lintPath, patchPath, checkRunId };
|
return { lintPath, patchPath, checkRunId };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -6844,78 +7007,41 @@ const logLintIssues = (issues) => {
|
||||||
` - ${issue.Text} (${issue.FromLinter})`);
|
` - ${issue.Text} (${issue.FromLinter})`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function resolveCheckRunId() {
|
const annotationFromIssue = (issue) => {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
const annotation = {
|
||||||
let jobId = -1;
|
path: issue.Pos.Filename,
|
||||||
const ctx = github.context;
|
start_line: issue.Pos.Line,
|
||||||
if (process.env.GITHUB_ACTIONS === `true` && ctx.runId) {
|
end_line: issue.Pos.Line,
|
||||||
try {
|
title: issue.FromLinter,
|
||||||
const searchToken = uuid_1.v4();
|
message: issue.Text,
|
||||||
core.info(`::warning::Attempting to resolve current GitHub Job ${ctx.runId}<${searchToken}>`);
|
annotation_level: issue.Severity,
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
};
|
||||||
let workflowJobs = (yield octokit.actions
|
if (issue.LineRange !== undefined) {
|
||||||
.listJobsForWorkflowRun(Object.assign(Object.assign({}, ctx.repo), { run_id: ctx.runId }))
|
annotation.end_line = issue.LineRange.To;
|
||||||
.catch((e) => {
|
}
|
||||||
throw `Unable to fetch Workflow Job List: ${e}`;
|
else if (issue.Pos.Column) {
|
||||||
})).data.jobs.filter((job) => job.status === `in_progress`);
|
annotation.start_column = issue.Pos.Column;
|
||||||
if (workflowJobs.length > 0) {
|
annotation.end_column = issue.Pos.Column;
|
||||||
core.info(`resolveCheckRunId() Found ${workflowJobs.length} Jobs:\n` + util_1.inspect(workflowJobs));
|
}
|
||||||
if (workflowJobs.length > 1) {
|
if (issue.Replacement !== null) {
|
||||||
const searchRegExp = new RegExp(`^` + ctx.job.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + `(\\s+\\(|$)`);
|
let replacement = ``;
|
||||||
const jobs = workflowJobs.filter((job) => searchRegExp.test(job.name));
|
if (issue.Replacement.Inline) {
|
||||||
core.info(`resolveCheckRunId() Found ${jobs.length} Jobs whose base name is '${ctx.job}'`);
|
replacement =
|
||||||
workflowJobs = jobs.length ? jobs : workflowJobs;
|
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
||||||
}
|
issue.Replacement.Inline.NewString +
|
||||||
if (workflowJobs.length > 1) {
|
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length);
|
||||||
const startedAt = Date.now();
|
|
||||||
// Sleep for MS, to allow Annotation to be captured and populated
|
|
||||||
yield ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))(10 * 1000);
|
|
||||||
core.info(`Paused for ${Date.now() - startedAt}ms`);
|
|
||||||
for (const job of workflowJobs) {
|
|
||||||
try {
|
|
||||||
const { data: annotations } = yield octokit.checks.listAnnotations(Object.assign(Object.assign({}, ctx.repo), { check_run_id: job.id }));
|
|
||||||
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Job '${job.id}':\n` + util_1.inspect(annotations));
|
|
||||||
if (annotations.findIndex((annotation) => {
|
|
||||||
core.info(`resolveCheckRunId() Looking for Search Token (${searchToken}) in message: ${annotation.message}`);
|
|
||||||
return annotation.message.includes(searchToken);
|
|
||||||
}) !== -1) {
|
|
||||||
core.info(`resolveCheckRunId() Found Search Token (${searchToken}) in Job ${job.id}`);
|
|
||||||
jobId = job.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
core.info(`resolveCheckRunId() Error Fetching Job ${job.id}: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(`resolveCheckRunId() Finished looking for Search Token`);
|
|
||||||
}
|
|
||||||
else if (workflowJobs[0]) {
|
|
||||||
jobId = workflowJobs[0].id;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw `Unable to resolve GitHub Job`;
|
|
||||||
}
|
|
||||||
core.info(`Current Job: ${jobId}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw `Fetching octokit.actions.getWorkflowRun(${process.env.GITHUB_RUN_ID}) returned no results`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
core.info(`::error::Unable to resolve GitHub Job: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else if (issue.Replacement.NewLines) {
|
||||||
core.info(`Not in GitHub Action Context, Skipping Job Resolution`);
|
replacement = issue.Replacement.NewLines.join("\n");
|
||||||
}
|
}
|
||||||
return jobId;
|
annotation.raw_details = "```suggestion\n" + replacement + "\n```";
|
||||||
});
|
}
|
||||||
}
|
return annotation;
|
||||||
|
};
|
||||||
function annotateLintIssues(issues, checkRunId) {
|
function annotateLintIssues(issues, checkRunId) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (checkRunId === -1 || !issues.length) {
|
if (checkRunId >= 0 || !issues.length) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const chunkSize = 50;
|
const chunkSize = 50;
|
||||||
const issueCounts = {
|
const issueCounts = {
|
||||||
|
|
@ -6923,52 +7049,29 @@ function annotateLintIssues(issues, checkRunId) {
|
||||||
warning: 0,
|
warning: 0,
|
||||||
failure: 0,
|
failure: 0,
|
||||||
};
|
};
|
||||||
const ctx = github.context;
|
try {
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }));
|
||||||
const title = `GolangCI-Lint`;
|
const title = `GolangCI-Lint`;
|
||||||
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
||||||
octokit.checks
|
octokit.checks
|
||||||
.update(Object.assign(Object.assign({}, ctx.repo), { check_run_id: checkRunId, output: {
|
.update(Object.assign(Object.assign({}, github.context.repo), { check_run_id: checkRunId, output: {
|
||||||
title: title,
|
title: title,
|
||||||
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map((issue) => {
|
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map((issue) => {
|
||||||
// If/when we transition to comments, we would build the request structure here
|
++issueCounts[issue.Severity];
|
||||||
const annotation = {
|
return annotationFromIssue(issue);
|
||||||
path: issue.Pos.Filename,
|
}),
|
||||||
start_line: issue.Pos.Line,
|
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
||||||
end_line: issue.Pos.Line,
|
} }))
|
||||||
title: issue.FromLinter,
|
.catch((e) => {
|
||||||
message: issue.Text,
|
throw `Error patching Check Run Data (annotations): ${e}`;
|
||||||
annotation_level: issue.Severity,
|
});
|
||||||
};
|
}
|
||||||
issueCounts[issue.Severity]++;
|
|
||||||
if (issue.LineRange !== undefined) {
|
|
||||||
annotation.end_line = issue.LineRange.To;
|
|
||||||
}
|
|
||||||
else if (issue.Pos.Column) {
|
|
||||||
annotation.start_column = issue.Pos.Column;
|
|
||||||
annotation.end_column = issue.Pos.Column;
|
|
||||||
}
|
|
||||||
if (issue.Replacement !== null) {
|
|
||||||
let replacement = ``;
|
|
||||||
if (issue.Replacement.Inline) {
|
|
||||||
replacement =
|
|
||||||
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
|
||||||
issue.Replacement.Inline.NewString +
|
|
||||||
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length);
|
|
||||||
}
|
|
||||||
else if (issue.Replacement.NewLines) {
|
|
||||||
replacement = issue.Replacement.NewLines.join("\n");
|
|
||||||
}
|
|
||||||
annotation.raw_details = "```suggestion\n" + replacement + "\n```";
|
|
||||||
}
|
|
||||||
return annotation;
|
|
||||||
}),
|
|
||||||
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
|
||||||
} }))
|
|
||||||
.catch((e) => {
|
|
||||||
throw `Error patching Check Run Data (annotations): ${e}`;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
core.info(`::error::Error Annotating Lint Issues: ${e}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const printOutput = (res) => {
|
const printOutput = (res) => {
|
||||||
|
|
@ -6989,19 +7092,23 @@ function processLintOutput(res, checkRunId) {
|
||||||
;
|
;
|
||||||
({ Issues: lintIssues } = parseOutput(res.stdout));
|
({ Issues: lintIssues } = parseOutput(res.stdout));
|
||||||
if (lintIssues.length) {
|
if (lintIssues.length) {
|
||||||
logLintIssues(lintIssues);
|
if (!(yield ((eventName) => __awaiter(this, void 0, void 0, function* () {
|
||||||
// We can only Annotate (or Comment) on Push or Pull Request
|
// We can only Annotate (or Comment) on Push or Pull Request
|
||||||
switch (github.context.eventName) {
|
switch (eventName) {
|
||||||
case `pull_request`:
|
case `pull_request`:
|
||||||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||||
/* falls through */
|
/* falls through */
|
||||||
case `push`:
|
case `push`:
|
||||||
yield annotateLintIssues(lintIssues, checkRunId);
|
return yield annotateLintIssues(lintIssues, checkRunId);
|
||||||
break;
|
default:
|
||||||
default:
|
// At this time, other events are not supported
|
||||||
// At this time, other events are not supported
|
return false;
|
||||||
break;
|
}
|
||||||
|
}))(github.context.eventName))) {
|
||||||
|
core.info(`::add-matcher::matchers-golangci-lint-action.json`);
|
||||||
}
|
}
|
||||||
|
// Log Issues at the end to allow failed GitHub Actions above to set the Problem Matcher
|
||||||
|
logLintIssues(lintIssues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
@ -7100,10 +7207,22 @@ function runLint(lintPath, patchPath, checkRunId) {
|
||||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function setup() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
yield core.group(`prepare environment`, prepareEnv);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.error(`Failed to prepare: ${error}, ${error.stack}`);
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.setup = setup;
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
const { lintPath, patchPath, checkRunId } = yield core.group(`prepare environment`, prepareEnv);
|
const { lintPath, patchPath, checkRunId } = yield core.group(`restore environment`, restoreEnv);
|
||||||
core.addPath(path.dirname(lintPath));
|
core.addPath(path.dirname(lintPath));
|
||||||
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
yield core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId));
|
||||||
}
|
}
|
||||||
|
|
@ -7173,7 +7292,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.RefKey = exports.Events = exports.State = exports.Inputs = void 0;
|
exports.RefKey = exports.Env = exports.Events = exports.State = exports.Inputs = void 0;
|
||||||
var Inputs;
|
var Inputs;
|
||||||
(function (Inputs) {
|
(function (Inputs) {
|
||||||
Inputs["Key"] = "key";
|
Inputs["Key"] = "key";
|
||||||
|
|
@ -7191,6 +7310,12 @@ var Events;
|
||||||
Events["Push"] = "push";
|
Events["Push"] = "push";
|
||||||
Events["PullRequest"] = "pull_request";
|
Events["PullRequest"] = "pull_request";
|
||||||
})(Events = exports.Events || (exports.Events = {}));
|
})(Events = exports.Events || (exports.Events = {}));
|
||||||
|
var Env;
|
||||||
|
(function (Env) {
|
||||||
|
Env["LintPath"] = "GOLANGCI_LINT_ACTION_LINT_PATH";
|
||||||
|
Env["PatchPath"] = "GOLANGCI_LINT_ACTION_PATCH_PATH";
|
||||||
|
Env["CheckRunIdent"] = "GOLANGCI_LINT_ACTION_CHECK_RUN_IDENT";
|
||||||
|
})(Env = exports.Env || (exports.Env = {}));
|
||||||
exports.RefKey = "GITHUB_REF";
|
exports.RefKey = "GITHUB_REF";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare-setup-go": "cd node_modules/setup-go && npm run build",
|
"prepare-setup-go": "cd node_modules/setup-go && npm run build",
|
||||||
"prepare-deps": "npm run prepare-setup-go",
|
"prepare-deps": "npm run prepare-setup-go",
|
||||||
"build": "tsc && ncc build -o dist/run/ src/main.ts && ncc build -o dist/post_run/ src/post_main.ts",
|
"build": "tsc && npm run build_pre && npm run build_main && npm run build_post_main",
|
||||||
|
"build_pre": "ncc build -o dist/pre/ src/setup.ts",
|
||||||
|
"build_main": "ncc build -o dist/run/ src/main.ts",
|
||||||
|
"build_post_main": "ncc build -o dist/post_run/ src/post_main.ts",
|
||||||
|
"watched_build_pre": "tsc && ncc build -w -o dist/pre/ src/setup.ts",
|
||||||
"watched_build_main": "tsc && ncc build -w -o dist/run/ src/main.ts",
|
"watched_build_main": "tsc && ncc build -w -o dist/run/ src/main.ts",
|
||||||
"watched_build_post_main": "tsc && ncc build -w -o dist/post_run/ src/post_main.ts",
|
"watched_build_post_main": "tsc && ncc build -w -o dist/post_run/ src/post_main.ts",
|
||||||
"type-check": "tsc",
|
"type-check": "tsc",
|
||||||
|
|
@ -15,7 +19,8 @@
|
||||||
"lint-fix": "eslint **/*.ts --cache --fix",
|
"lint-fix": "eslint **/*.ts --cache --fix",
|
||||||
"format": "prettier --write **/*.ts",
|
"format": "prettier --write **/*.ts",
|
||||||
"format-check": "prettier --check **/*.ts",
|
"format-check": "prettier --check **/*.ts",
|
||||||
"all": "npm run prepare-deps && npm run build && npm run format-check && npm run lint",
|
"all": "npm run prepare-deps && npm run format-check && npm run build && npm run lint",
|
||||||
|
"all-format": "npm run format && npm run all",
|
||||||
"local": "npm run build && act -j test -b",
|
"local": "npm run build && act -j test -b",
|
||||||
"local-full-version": "npm run build && act -j test-full-version -b"
|
"local-full-version": "npm run build && act -j test-full-version -b"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,10 @@ export enum Events {
|
||||||
PullRequest = "pull_request",
|
PullRequest = "pull_request",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Env {
|
||||||
|
LintPath = "GOLANGCI_LINT_ACTION_LINT_PATH",
|
||||||
|
PatchPath = "GOLANGCI_LINT_ACTION_PATCH_PATH",
|
||||||
|
CheckRunIdent = "GOLANGCI_LINT_ACTION_CHECK_RUN_IDENT",
|
||||||
|
}
|
||||||
|
|
||||||
export const RefKey = "GITHUB_REF"
|
export const RefKey = "GITHUB_REF"
|
||||||
|
|
|
||||||
444
src/run.ts
444
src/run.ts
|
|
@ -9,6 +9,7 @@ import { inspect, promisify } from "util"
|
||||||
import { v4 as uuidv4 } from "uuid"
|
import { v4 as uuidv4 } from "uuid"
|
||||||
|
|
||||||
import { restoreCache, saveCache } from "./cache"
|
import { restoreCache, saveCache } from "./cache"
|
||||||
|
import { Env as EnvKey } from "./constants"
|
||||||
import { installGo, installLint } from "./install"
|
import { installGo, installLint } from "./install"
|
||||||
import { findLintVersion } from "./version"
|
import { findLintVersion } from "./version"
|
||||||
|
|
||||||
|
|
@ -82,11 +83,198 @@ type Env = {
|
||||||
checkRunId: number
|
checkRunId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prepareEnv(): Promise<Env> {
|
type CheckRunIdent = {
|
||||||
|
runId: number
|
||||||
|
runName: string
|
||||||
|
checkRunId: number
|
||||||
|
checkSuiteId?: number
|
||||||
|
checkRunSearchToken?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckRun = {
|
||||||
|
id: number
|
||||||
|
status: string
|
||||||
|
name: string
|
||||||
|
output: {
|
||||||
|
title: string | null
|
||||||
|
summary: string | null
|
||||||
|
text: string | null
|
||||||
|
annotations_count: number | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchCheckSuiteId(runId: number): Promise<number> {
|
||||||
|
let currentCheckSuiteId = -1
|
||||||
|
if (runId > 0) {
|
||||||
|
try {
|
||||||
|
const { data: currentRun } = await github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.actions.getWorkflowRun({
|
||||||
|
...github.context.repo,
|
||||||
|
run_id: runId,
|
||||||
|
})
|
||||||
|
.catch((e: string) => {
|
||||||
|
throw `Unable to fetch Workflow Run: ${e}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (currentRun.status === `in_progress`) {
|
||||||
|
// The GitHub API it's self does present the `check_suite_id` property, but it is not documented or present returned object's `type`
|
||||||
|
currentCheckSuiteId = parseInt(currentRun.check_suite_url.substr(1 + currentRun.check_suite_url.lastIndexOf(`/`))) ?? -1
|
||||||
|
// The following SHOULD work, but alas
|
||||||
|
// currentCheckSuiteId = currentRun.check_suite_id
|
||||||
|
if (currentCheckSuiteId <= 0) {
|
||||||
|
throw `Error extracting Check Suite ID from: ${currentRun.check_suite_url}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Current Run: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentCheckSuiteId
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchCheckSuiteRuns(checkSuiteId: number, name?: string): Promise<CheckRun[]> {
|
||||||
|
let checkSuiteRuns: CheckRun[] = []
|
||||||
|
if (checkSuiteId > 0) {
|
||||||
|
try {
|
||||||
|
checkSuiteRuns = (
|
||||||
|
await github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listForSuite({
|
||||||
|
...github.context.repo,
|
||||||
|
check_suite_id: checkSuiteId,
|
||||||
|
})
|
||||||
|
.catch((e: string) => {
|
||||||
|
throw `Unable to fetch Check Suite Runs List: ${e}`
|
||||||
|
})
|
||||||
|
).data.check_runs.filter((run) => run.status === `in_progress`)
|
||||||
|
|
||||||
|
if (checkSuiteRuns.length > 0 && name) {
|
||||||
|
const _checkSuiteRuns = checkSuiteRuns.filter(
|
||||||
|
(run) => run.name.indexOf(name) === 0 && (run.name.length === name.length || run.name[name.length] === ` `)
|
||||||
|
)
|
||||||
|
checkSuiteRuns = _checkSuiteRuns.length ? _checkSuiteRuns : checkSuiteRuns
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSuiteRuns.length === 0) {
|
||||||
|
throw `Check Suite returned 0 runs`
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`::error::Error Fetching Check Suite Runs (${checkSuiteId}): ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkSuiteRuns
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepareCheckRunIdent(runId?: number, runName?: string): Promise<CheckRunIdent> {
|
||||||
|
const checkRunIdent: CheckRunIdent = {
|
||||||
|
runId: runId ?? github.context.runId,
|
||||||
|
runName: runName ?? github.context.job,
|
||||||
|
checkRunId: -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.GITHUB_ACTIONS === `true` && checkRunIdent.runId > 0) {
|
||||||
|
core.info(`Resolving current GitHub Check Run ${checkRunIdent.runId}`)
|
||||||
|
try {
|
||||||
|
const checkSuiteId = await fetchCheckSuiteId(checkRunIdent.runId)
|
||||||
|
if (checkSuiteId < 0) {
|
||||||
|
throw `Unable to resolve Check Suite ID`
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkSuiteRuns = await fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName)
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRunIdent.checkSuiteId = checkSuiteId
|
||||||
|
|
||||||
|
if (checkSuiteRuns.length === 1) {
|
||||||
|
checkRunIdent.checkRunId = checkSuiteRuns[0].id
|
||||||
|
} else {
|
||||||
|
checkRunIdent.checkRunSearchToken = uuidv4()
|
||||||
|
core.info(
|
||||||
|
`::warning::[golangci-lint-action] Tagging Current GitHub CheckRun ${checkRunIdent.runId}<${checkRunIdent.checkRunSearchToken}>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`::error::Error resolving Run (${checkRunIdent.runId}): ${e}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkRunIdent
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveCheckRunId(checkRunIdent: CheckRunIdent): Promise<number> {
|
||||||
|
let checkRunId = checkRunIdent.checkRunId
|
||||||
|
|
||||||
|
if (checkRunIdent.runId > 0) {
|
||||||
|
if (checkRunId <= 0) {
|
||||||
|
try {
|
||||||
|
const checkSuiteId = checkRunIdent?.checkSuiteId ?? -1
|
||||||
|
if (checkSuiteId <= 0) {
|
||||||
|
throw `No Check Suite ID`
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkRunSearchToken = checkRunIdent?.checkRunSearchToken ?? ``
|
||||||
|
if (!checkRunSearchToken) {
|
||||||
|
throw `No Check Run Search Token`
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkSuiteRuns = (await fetchCheckSuiteRuns(checkSuiteId, checkRunIdent.runName)).filter(
|
||||||
|
(run) => run.output.annotations_count
|
||||||
|
)
|
||||||
|
if (checkSuiteRuns.length < 1) {
|
||||||
|
throw `Unable to resolve Check Suite children`
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`resolveCheckRunId() Found ${checkSuiteRuns.length} Jobs:\n` + inspect(checkSuiteRuns))
|
||||||
|
|
||||||
|
core.info(`Resolving current Check Run in Check Suite (${checkSuiteId})`)
|
||||||
|
|
||||||
|
for (const run of checkSuiteRuns) {
|
||||||
|
try {
|
||||||
|
const { data: annotations } = await github
|
||||||
|
.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
|
.checks.listAnnotations({
|
||||||
|
...github.context.repo,
|
||||||
|
check_run_id: run.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Check Run '${run.id}':\n` + inspect(annotations))
|
||||||
|
|
||||||
|
if (
|
||||||
|
annotations.findIndex((annotation) => {
|
||||||
|
core.info(`resolveCheckRunId() Looking for Search Token (${checkRunSearchToken}) in message: ${annotation.message}`)
|
||||||
|
return annotation.message.indexOf(checkRunSearchToken) >= 0
|
||||||
|
}) !== -1
|
||||||
|
) {
|
||||||
|
core.info(`resolveCheckRunId() Found Search Token (${checkRunSearchToken}) in Check Run ${run.id}`)
|
||||||
|
checkRunId = run.id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`resolveCheckRunId() Error Fetching Check Run (${run.id}): ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`::error::Unable to resolve Check Run ID: ${e}`)
|
||||||
|
core.info(`checkRunIdent = ` + inspect(checkRunIdent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`Not in GitHub Action Context, Skipping Check Run Resolution`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkRunId
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepareEnv(): Promise<void> {
|
||||||
const startedAt = Date.now()
|
const startedAt = Date.now()
|
||||||
|
|
||||||
// Resolve Check Run ID
|
// Resolve Check Run ID
|
||||||
const resolveCheckRunIdPromise = resolveCheckRunId()
|
const prepareCheckRunIdentPromise = prepareCheckRunIdent()
|
||||||
|
|
||||||
// Prepare cache, lint and go in parallel.
|
// Prepare cache, lint and go in parallel.
|
||||||
const restoreCachePromise = restoreCache()
|
const restoreCachePromise = restoreCache()
|
||||||
|
|
@ -94,13 +282,31 @@ async function prepareEnv(): Promise<Env> {
|
||||||
const installGoPromise = installGo()
|
const installGoPromise = installGo()
|
||||||
const patchPromise = fetchPatch()
|
const patchPromise = fetchPatch()
|
||||||
|
|
||||||
const lintPath = await prepareLintPromise
|
core.saveState(EnvKey.LintPath, await prepareLintPromise)
|
||||||
await installGoPromise
|
await installGoPromise
|
||||||
await restoreCachePromise
|
await restoreCachePromise
|
||||||
const patchPath = await patchPromise
|
core.saveState(EnvKey.PatchPath, await patchPromise)
|
||||||
const checkRunId = await resolveCheckRunIdPromise
|
core.saveState(EnvKey.CheckRunIdent, JSON.stringify(await prepareCheckRunIdentPromise))
|
||||||
|
|
||||||
core.info(`Prepared env in ${Date.now() - startedAt}ms`)
|
core.info(`Prepared env in ${Date.now() - startedAt}ms`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreEnv(): Promise<Env> {
|
||||||
|
const startedAt = Date.now()
|
||||||
|
|
||||||
|
const lintPath = core.getState(EnvKey.LintPath)
|
||||||
|
const patchPath = core.getState(EnvKey.PatchPath)
|
||||||
|
let checkRunId: number
|
||||||
|
try {
|
||||||
|
const checkRunIdent: CheckRunIdent = JSON.parse(core.getState(EnvKey.CheckRunIdent))
|
||||||
|
checkRunId = await resolveCheckRunId(checkRunIdent)
|
||||||
|
} catch (e) {
|
||||||
|
core.info(`::error::Error Resolving Check Run ID: ${e}`)
|
||||||
|
checkRunId = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Restored env in ${Date.now() - startedAt}ms`)
|
||||||
|
|
||||||
return { lintPath, patchPath, checkRunId }
|
return { lintPath, patchPath, checkRunId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,85 +445,42 @@ const logLintIssues = (issues: LintIssue[]): void => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveCheckRunId(): Promise<number> {
|
const annotationFromIssue = (issue: LintIssue): GithubAnnotation => {
|
||||||
let jobId = -1
|
const annotation: GithubAnnotation = {
|
||||||
const ctx = github.context
|
path: issue.Pos.Filename,
|
||||||
|
start_line: issue.Pos.Line,
|
||||||
if (process.env.GITHUB_ACTIONS === `true` && ctx.runId) {
|
end_line: issue.Pos.Line,
|
||||||
try {
|
title: issue.FromLinter,
|
||||||
const searchToken = uuidv4()
|
message: issue.Text,
|
||||||
core.info(`::warning::Attempting to resolve current GitHub Job ${ctx.runId}<${searchToken}>`)
|
annotation_level: issue.Severity,
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }))
|
|
||||||
let workflowJobs = (
|
|
||||||
await octokit.actions
|
|
||||||
.listJobsForWorkflowRun({
|
|
||||||
...ctx.repo,
|
|
||||||
run_id: ctx.runId,
|
|
||||||
})
|
|
||||||
.catch((e: string) => {
|
|
||||||
throw `Unable to fetch Workflow Job List: ${e}`
|
|
||||||
})
|
|
||||||
).data.jobs.filter((job) => job.status === `in_progress`)
|
|
||||||
|
|
||||||
if (workflowJobs.length > 0) {
|
|
||||||
core.info(`resolveCheckRunId() Found ${workflowJobs.length} Jobs:\n` + inspect(workflowJobs))
|
|
||||||
if (workflowJobs.length > 1) {
|
|
||||||
const searchRegExp = new RegExp(`^` + ctx.job.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + `(\\s+\\(|$)`)
|
|
||||||
const jobs = workflowJobs.filter((job) => searchRegExp.test(job.name))
|
|
||||||
core.info(`resolveCheckRunId() Found ${jobs.length} Jobs whose base name is '${ctx.job}'`)
|
|
||||||
workflowJobs = jobs.length ? jobs : workflowJobs
|
|
||||||
}
|
|
||||||
if (workflowJobs.length > 1) {
|
|
||||||
const startedAt = Date.now()
|
|
||||||
// Sleep for MS, to allow Annotation to be captured and populated
|
|
||||||
await ((ms): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms)))(10 * 1000)
|
|
||||||
core.info(`Paused for ${Date.now() - startedAt}ms`)
|
|
||||||
for (const job of workflowJobs) {
|
|
||||||
try {
|
|
||||||
const { data: annotations } = await octokit.checks.listAnnotations({
|
|
||||||
...ctx.repo,
|
|
||||||
check_run_id: job.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
core.info(`resolveCheckRunId() Found ${annotations.length} Annotations for Job '${job.id}':\n` + inspect(annotations))
|
|
||||||
|
|
||||||
if (
|
|
||||||
annotations.findIndex((annotation) => {
|
|
||||||
core.info(`resolveCheckRunId() Looking for Search Token (${searchToken}) in message: ${annotation.message}`)
|
|
||||||
return annotation.message.includes(searchToken)
|
|
||||||
}) !== -1
|
|
||||||
) {
|
|
||||||
core.info(`resolveCheckRunId() Found Search Token (${searchToken}) in Job ${job.id}`)
|
|
||||||
jobId = job.id
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
core.info(`resolveCheckRunId() Error Fetching Job ${job.id}: ${e}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(`resolveCheckRunId() Finished looking for Search Token`)
|
|
||||||
} else if (workflowJobs[0]) {
|
|
||||||
jobId = workflowJobs[0].id
|
|
||||||
} else {
|
|
||||||
throw `Unable to resolve GitHub Job`
|
|
||||||
}
|
|
||||||
core.info(`Current Job: ${jobId}`)
|
|
||||||
} else {
|
|
||||||
throw `Fetching octokit.actions.getWorkflowRun(${process.env.GITHUB_RUN_ID}) returned no results`
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
core.info(`::error::Unable to resolve GitHub Job: ${e}`)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
core.info(`Not in GitHub Action Context, Skipping Job Resolution`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return jobId
|
if (issue.LineRange !== undefined) {
|
||||||
|
annotation.end_line = issue.LineRange.To
|
||||||
|
} else if (issue.Pos.Column) {
|
||||||
|
annotation.start_column = issue.Pos.Column
|
||||||
|
annotation.end_column = issue.Pos.Column
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issue.Replacement !== null) {
|
||||||
|
let replacement = ``
|
||||||
|
if (issue.Replacement.Inline) {
|
||||||
|
replacement =
|
||||||
|
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
||||||
|
issue.Replacement.Inline.NewString +
|
||||||
|
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length)
|
||||||
|
} else if (issue.Replacement.NewLines) {
|
||||||
|
replacement = issue.Replacement.NewLines.join("\n")
|
||||||
|
}
|
||||||
|
annotation.raw_details = "```suggestion\n" + replacement + "\n```"
|
||||||
|
}
|
||||||
|
|
||||||
|
return annotation
|
||||||
}
|
}
|
||||||
|
|
||||||
async function annotateLintIssues(issues: LintIssue[], checkRunId: number): Promise<void> {
|
async function annotateLintIssues(issues: LintIssue[], checkRunId: number): Promise<boolean> {
|
||||||
if (checkRunId === -1 || !issues.length) {
|
if (checkRunId >= 0 || !issues.length) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
const chunkSize = 50
|
const chunkSize = 50
|
||||||
const issueCounts = {
|
const issueCounts = {
|
||||||
|
|
@ -325,60 +488,34 @@ async function annotateLintIssues(issues: LintIssue[], checkRunId: number): Prom
|
||||||
warning: 0,
|
warning: 0,
|
||||||
failure: 0,
|
failure: 0,
|
||||||
}
|
}
|
||||||
const ctx = github.context
|
|
||||||
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }))
|
|
||||||
const title = `GolangCI-Lint`
|
|
||||||
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
|
||||||
octokit.checks
|
|
||||||
.update({
|
|
||||||
...ctx.repo,
|
|
||||||
check_run_id: checkRunId,
|
|
||||||
output: {
|
|
||||||
title: title,
|
|
||||||
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map(
|
|
||||||
(issue: LintIssue): GithubAnnotation => {
|
|
||||||
// If/when we transition to comments, we would build the request structure here
|
|
||||||
const annotation: GithubAnnotation = {
|
|
||||||
path: issue.Pos.Filename,
|
|
||||||
start_line: issue.Pos.Line,
|
|
||||||
end_line: issue.Pos.Line,
|
|
||||||
title: issue.FromLinter,
|
|
||||||
message: issue.Text,
|
|
||||||
annotation_level: issue.Severity,
|
|
||||||
}
|
|
||||||
|
|
||||||
issueCounts[issue.Severity]++
|
try {
|
||||||
|
const octokit = github.getOctokit(core.getInput(`github-token`, { required: true }))
|
||||||
if (issue.LineRange !== undefined) {
|
const title = `GolangCI-Lint`
|
||||||
annotation.end_line = issue.LineRange.To
|
for (let i = 0; i < Math.ceil(issues.length / chunkSize); i++) {
|
||||||
} else if (issue.Pos.Column) {
|
octokit.checks
|
||||||
annotation.start_column = issue.Pos.Column
|
.update({
|
||||||
annotation.end_column = issue.Pos.Column
|
...github.context.repo,
|
||||||
}
|
check_run_id: checkRunId,
|
||||||
|
output: {
|
||||||
if (issue.Replacement !== null) {
|
title: title,
|
||||||
let replacement = ``
|
annotations: issues.slice(i * chunkSize, i * chunkSize + chunkSize).map((issue: LintIssue) => {
|
||||||
if (issue.Replacement.Inline) {
|
++issueCounts[issue.Severity]
|
||||||
replacement =
|
return annotationFromIssue(issue)
|
||||||
issue.SourceLines[0].slice(0, issue.Replacement.Inline.StartCol) +
|
}),
|
||||||
issue.Replacement.Inline.NewString +
|
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
||||||
issue.SourceLines[0].slice(issue.Replacement.Inline.StartCol + issue.Replacement.Inline.Length)
|
},
|
||||||
} else if (issue.Replacement.NewLines) {
|
})
|
||||||
replacement = issue.Replacement.NewLines.join("\n")
|
.catch((e) => {
|
||||||
}
|
throw `Error patching Check Run Data (annotations): ${e}`
|
||||||
annotation.raw_details = "```suggestion\n" + replacement + "\n```"
|
})
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
return annotation as GithubAnnotation
|
core.info(`::error::Error Annotating Lint Issues: ${e}`)
|
||||||
}
|
return false
|
||||||
),
|
|
||||||
summary: `There are {issueCounts.failure} failures, {issueCounts.wairning} warnings, and {issueCounts.notice} notices.`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
throw `Error patching Check Run Data (annotations): ${e}`
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const printOutput = (res: ExecRes): void => {
|
const printOutput = (res: ExecRes): void => {
|
||||||
|
|
@ -399,20 +536,26 @@ async function processLintOutput(res: ExecRes, checkRunId: number): Promise<Lint
|
||||||
;({ Issues: lintIssues } = parseOutput(res.stdout))
|
;({ Issues: lintIssues } = parseOutput(res.stdout))
|
||||||
|
|
||||||
if (lintIssues.length) {
|
if (lintIssues.length) {
|
||||||
logLintIssues(lintIssues)
|
if (
|
||||||
|
!(await (async (eventName: string): Promise<boolean> => {
|
||||||
// We can only Annotate (or Comment) on Push or Pull Request
|
// We can only Annotate (or Comment) on Push or Pull Request
|
||||||
switch (github.context.eventName) {
|
switch (eventName) {
|
||||||
case `pull_request`:
|
case `pull_request`:
|
||||||
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
// TODO: When we are ready to handle these as Comments, instead of Annotations, we would place that logic here
|
||||||
/* falls through */
|
/* falls through */
|
||||||
case `push`:
|
case `push`:
|
||||||
await annotateLintIssues(lintIssues, checkRunId)
|
return await annotateLintIssues(lintIssues, checkRunId)
|
||||||
break
|
default:
|
||||||
default:
|
// At this time, other events are not supported
|
||||||
// At this time, other events are not supported
|
return false
|
||||||
break
|
}
|
||||||
|
})(github.context.eventName))
|
||||||
|
) {
|
||||||
|
core.info(`::add-matcher::matchers-golangci-lint-action.json`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log Issues at the end to allow failed GitHub Actions above to set the Problem Matcher
|
||||||
|
logLintIssues(lintIssues)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
core.setFailed(`Error processing golangci-lint output: ${e}`)
|
core.setFailed(`Error processing golangci-lint output: ${e}`)
|
||||||
|
|
@ -519,9 +662,18 @@ async function runLint(lintPath: string, patchPath: string, checkRunId: number):
|
||||||
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`)
|
core.info(`Ran golangci-lint in ${Date.now() - startedAt}ms`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setup(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await core.group(`prepare environment`, prepareEnv)
|
||||||
|
} catch (error) {
|
||||||
|
core.error(`Failed to prepare: ${error}, ${error.stack}`)
|
||||||
|
core.setFailed(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function run(): Promise<void> {
|
export async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const { lintPath, patchPath, checkRunId } = await core.group(`prepare environment`, prepareEnv)
|
const { lintPath, patchPath, checkRunId } = await core.group(`restore environment`, restoreEnv)
|
||||||
core.addPath(path.dirname(lintPath))
|
core.addPath(path.dirname(lintPath))
|
||||||
await core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId))
|
await core.group(`run golangci-lint`, () => runLint(lintPath, patchPath, checkRunId))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
3
src/setup.ts
Normal file
3
src/setup.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { setup } from "./run"
|
||||||
|
|
||||||
|
setup()
|
||||||
Loading…
Reference in a new issue