Add expect-error input to force PR check green on expected failure (#1177)

This commit is contained in:
Angela P Wen 2022-08-16 16:27:14 -07:00 committed by GitHub
parent b0d61cff1a
commit 9b7fa3dd99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 96 additions and 16 deletions

View file

@ -21,7 +21,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
name: Failure Expected - Upload debug artifacts name: Upload debug artifacts after failure in analyze
continue-on-error: true continue-on-error: true
timeout-minutes: 45 timeout-minutes: 45
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -45,6 +45,7 @@ jobs:
- uses: ./../action/analyze - uses: ./../action/analyze
id: analysis id: analysis
with: with:
expect-error: true
ram: 1 ram: 1
download-and-check-artifacts: download-and-check-artifacts:
name: Download and check debug artifacts after failure in analyze name: Download and check debug artifacts after failure in analyze

View file

@ -66,6 +66,10 @@ inputs:
default: ${{ github.token }} default: ${{ github.token }}
matrix: matrix:
default: ${{ toJson(matrix) }} default: ${{ toJson(matrix) }}
expect-error:
description: "[Internal] It is an error to use this input outside of integration testing of the codeql-action."
required: false
default: "false"
outputs: outputs:
db-locations: db-locations:
description: A map from language to absolute path for each database created by CodeQL. description: A map from language to absolute path for each database created by CodeQL.

18
lib/actions-util.js generated
View file

@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.printDebugLogs = exports.isAnalyzingDefaultBranch = exports.getRelativeScriptPath = exports.isRunningLocalAction = exports.sendStatusReport = exports.createStatusReportBase = exports.getActionsStatus = exports.getRef = exports.computeAutomationID = exports.getAutomationID = exports.getAnalysisKey = exports.getWorkflowRunID = exports.getWorkflow = exports.formatWorkflowCause = exports.formatWorkflowErrors = exports.validateWorkflow = exports.getWorkflowErrors = exports.WorkflowErrors = exports.patternIsSuperset = exports.determineMergeBaseCommitOid = exports.getCommitOid = exports.getTemporaryDirectory = exports.getOptionalInput = exports.getRequiredInput = void 0; exports.isAnalyzingCodeQLActionRepoOrFork = exports.printDebugLogs = exports.isAnalyzingDefaultBranch = exports.getRelativeScriptPath = exports.isRunningLocalAction = exports.sendStatusReport = exports.createStatusReportBase = exports.getActionsStatus = exports.getRef = exports.computeAutomationID = exports.getAutomationID = exports.getAnalysisKey = exports.getWorkflowRunID = exports.getWorkflow = exports.formatWorkflowCause = exports.formatWorkflowErrors = exports.validateWorkflow = exports.getWorkflowErrors = exports.WorkflowErrors = exports.patternIsSuperset = exports.determineMergeBaseCommitOid = exports.getCommitOid = exports.getTemporaryDirectory = exports.getOptionalInput = exports.getRequiredInput = void 0;
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const os = __importStar(require("os")); const os = __importStar(require("os"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
@ -28,6 +28,7 @@ const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
const safeWhich = __importStar(require("@chrisgavin/safe-which")); const safeWhich = __importStar(require("@chrisgavin/safe-which"));
const yaml = __importStar(require("js-yaml")); const yaml = __importStar(require("js-yaml"));
const api = __importStar(require("./api-client")); const api = __importStar(require("./api-client"));
const codeql_1 = require("./codeql");
const sharedEnv = __importStar(require("./shared-environment")); const sharedEnv = __importStar(require("./shared-environment"));
const util_1 = require("./util"); const util_1 = require("./util");
// eslint-disable-next-line import/no-commonjs // eslint-disable-next-line import/no-commonjs
@ -702,4 +703,19 @@ async function printDebugLogs(config) {
} }
} }
exports.printDebugLogs = printDebugLogs; exports.printDebugLogs = printDebugLogs;
// Returns whether workflow kicked off by codeql-action repo itself,
// or a fork of it.
function isAnalyzingCodeQLActionRepoOrFork() {
var _a, _b;
const codeQLActionRepoUrl = `https://api.github.com/repos/${codeql_1.CODEQL_DEFAULT_ACTION_REPOSITORY}`;
const repo = (_a = getWorkflowEvent()) === null || _a === void 0 ? void 0 : _a.repository;
if ((repo === null || repo === void 0 ? void 0 : repo.url) === codeQLActionRepoUrl) {
return true;
}
if ((repo === null || repo === void 0 ? void 0 : repo.fork) && ((_b = repo === null || repo === void 0 ? void 0 : repo.parent) === null || _b === void 0 ? void 0 : _b.url) === codeQLActionRepoUrl) {
return true;
}
return false;
}
exports.isAnalyzingCodeQLActionRepoOrFork = isAnalyzingCodeQLActionRepoOrFork;
//# sourceMappingURL=actions-util.js.map //# sourceMappingURL=actions-util.js.map

File diff suppressed because one or more lines are too long

18
lib/analyze-action.js generated
View file

@ -60,6 +60,12 @@ async function sendStatusReport(startedAt, config, stats, error, trapCacheUpload
} }
} }
exports.sendStatusReport = sendStatusReport; exports.sendStatusReport = sendStatusReport;
// `expect-error` should only be set to any value by the
// codeql-action repo or a fork of it.
function hasBadExpectErrorInput() {
return (actionsUtil.getOptionalInput("expect-error") !== "false" &&
!actionsUtil.isAnalyzingCodeQLActionRepoOrFork());
}
async function run() { async function run() {
const startedAt = new Date(); const startedAt = new Date();
let uploadResult = undefined; let uploadResult = undefined;
@ -78,6 +84,9 @@ async function run() {
if (config === undefined) { if (config === undefined) {
throw new Error("Config file could not be found at expected location. Has the 'init' action been called?"); throw new Error("Config file could not be found at expected location. Has the 'init' action been called?");
} }
if (hasBadExpectErrorInput()) {
throw new Error("`expect-error` input parameter is for internal use only. It should only be set by codeql-action or a fork.");
}
await util.enrichEnvironment(util.Mode.actions, await (0, codeql_1.getCodeQL)(config.codeQLCmd)); await util.enrichEnvironment(util.Mode.actions, await (0, codeql_1.getCodeQL)(config.codeQLCmd));
const apiDetails = { const apiDetails = {
auth: actionsUtil.getRequiredInput("token"), auth: actionsUtil.getRequiredInput("token"),
@ -124,10 +133,17 @@ async function run() {
actionsUtil.getRequiredInput("wait-for-processing") === "true") { actionsUtil.getRequiredInput("wait-for-processing") === "true") {
await upload_lib.waitForProcessing((0, repository_1.parseRepositoryNwo)(util.getRequiredEnvParam("GITHUB_REPOSITORY")), uploadResult.sarifID, apiDetails, (0, logging_1.getActionsLogger)()); await upload_lib.waitForProcessing((0, repository_1.parseRepositoryNwo)(util.getRequiredEnvParam("GITHUB_REPOSITORY")), uploadResult.sarifID, apiDetails, (0, logging_1.getActionsLogger)());
} }
// If we did not throw an error yet here, but we expect one, throw it.
if (actionsUtil.getOptionalInput("expect-error") === "true") {
core.setFailed(`expect-error input was set to true but no error was thrown.`);
}
} }
catch (origError) { catch (origError) {
const error = origError instanceof Error ? origError : new Error(String(origError)); const error = origError instanceof Error ? origError : new Error(String(origError));
core.setFailed(error.message); if (actionsUtil.getOptionalInput("expect-error") !== "true" ||
hasBadExpectErrorInput()) {
core.setFailed(error.message);
}
console.log(error); console.log(error);
if (error instanceof analyze_1.CodeQLAnalysisError) { if (error instanceof analyze_1.CodeQLAnalysisError) {
const stats = { ...error.queriesStatusReport }; const stats = { ...error.queriesStatusReport };

File diff suppressed because one or more lines are too long

16
lib/codeql.js generated
View file

@ -22,7 +22,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.getExtraOptions = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.convertToSemVer = exports.getCodeQLURLVersion = exports.setupCodeQL = exports.getCodeQLActionRepository = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_CONFIG_FILES = exports.CODEQL_VERSION_ML_POWERED_QUERIES = exports.CODEQL_VERSION_COUNTS_LINES = exports.CommandInvocationError = void 0; exports.getExtraOptions = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.convertToSemVer = exports.getCodeQLURLVersion = exports.setupCodeQL = exports.getCodeQLActionRepository = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_CONFIG_FILES = exports.CODEQL_VERSION_ML_POWERED_QUERIES = exports.CODEQL_VERSION_COUNTS_LINES = exports.CODEQL_DEFAULT_ACTION_REPOSITORY = exports.CommandInvocationError = void 0;
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
const toolrunner = __importStar(require("@actions/exec/lib/toolrunner")); const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
@ -56,7 +56,7 @@ exports.CommandInvocationError = CommandInvocationError;
*/ */
let cachedCodeQL = undefined; let cachedCodeQL = undefined;
const CODEQL_BUNDLE_VERSION = defaults.bundleVersion; const CODEQL_BUNDLE_VERSION = defaults.bundleVersion;
const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action"; exports.CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
/** /**
* The oldest version of CodeQL that the Action will run with. This should be * The oldest version of CodeQL that the Action will run with. This should be
* at least three minor versions behind the current version. The version flags * at least three minor versions behind the current version. The version flags
@ -121,7 +121,7 @@ function getCodeQLBundleName() {
} }
function getCodeQLActionRepository(logger) { function getCodeQLActionRepository(logger) {
if (!util.isActions()) { if (!util.isActions()) {
return CODEQL_DEFAULT_ACTION_REPOSITORY; return exports.CODEQL_DEFAULT_ACTION_REPOSITORY;
} }
else { else {
return getActionsCodeQLActionRepository(logger); return getActionsCodeQLActionRepository(logger);
@ -138,7 +138,7 @@ function getActionsCodeQLActionRepository(logger) {
// This handles the case where the Action does not come from an Action repository, // This handles the case where the Action does not come from an Action repository,
// e.g. our integration tests which use the Action code from the current checkout. // e.g. our integration tests which use the Action code from the current checkout.
logger.info("The CodeQL Action is checked out locally. Using the default CodeQL Action repository."); logger.info("The CodeQL Action is checked out locally. Using the default CodeQL Action repository.");
return CODEQL_DEFAULT_ACTION_REPOSITORY; return exports.CODEQL_DEFAULT_ACTION_REPOSITORY;
} }
logger.info("GITHUB_ACTION_REPOSITORY environment variable was not set. Falling back to legacy method of finding the GitHub Action."); logger.info("GITHUB_ACTION_REPOSITORY environment variable was not set. Falling back to legacy method of finding the GitHub Action.");
const relativeScriptPathParts = (0, actions_util_1.getRelativeScriptPath)().split(path.sep); const relativeScriptPathParts = (0, actions_util_1.getRelativeScriptPath)().split(path.sep);
@ -150,9 +150,9 @@ async function getCodeQLBundleDownloadURL(apiDetails, variant, logger) {
// This GitHub instance, and this Action. // This GitHub instance, and this Action.
[apiDetails.url, codeQLActionRepository], [apiDetails.url, codeQLActionRepository],
// This GitHub instance, and the canonical Action. // This GitHub instance, and the canonical Action.
[apiDetails.url, CODEQL_DEFAULT_ACTION_REPOSITORY], [apiDetails.url, exports.CODEQL_DEFAULT_ACTION_REPOSITORY],
// GitHub.com, and the canonical Action. // GitHub.com, and the canonical Action.
[util.GITHUB_DOTCOM_URL, CODEQL_DEFAULT_ACTION_REPOSITORY], [util.GITHUB_DOTCOM_URL, exports.CODEQL_DEFAULT_ACTION_REPOSITORY],
]; ];
// We now filter out any duplicates. // We now filter out any duplicates.
// Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork. // Duplicates will happen either because the GitHub instance is GitHub.com, or because the Action is not a fork.
@ -188,7 +188,7 @@ async function getCodeQLBundleDownloadURL(apiDetails, variant, logger) {
const [apiURL, repository] = downloadSource; const [apiURL, repository] = downloadSource;
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public. // If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
if (apiURL === util.GITHUB_DOTCOM_URL && if (apiURL === util.GITHUB_DOTCOM_URL &&
repository === CODEQL_DEFAULT_ACTION_REPOSITORY) { repository === exports.CODEQL_DEFAULT_ACTION_REPOSITORY) {
break; break;
} }
const [repositoryOwner, repositoryName] = repository.split("/"); const [repositoryOwner, repositoryName] = repository.split("/");
@ -209,7 +209,7 @@ async function getCodeQLBundleDownloadURL(apiDetails, variant, logger) {
logger.info(`Looked for CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} but got error ${e}.`); logger.info(`Looked for CodeQL bundle in ${downloadSource[1]} on ${downloadSource[0]} but got error ${e}.`);
} }
} }
return `https://github.com/${CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${CODEQL_BUNDLE_VERSION}/${codeQLBundleName}`; return `https://github.com/${exports.CODEQL_DEFAULT_ACTION_REPOSITORY}/releases/download/${CODEQL_BUNDLE_VERSION}/${codeQLBundleName}`;
} }
/** /**
* Set up CodeQL CLI access. * Set up CodeQL CLI access.

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,7 @@ import * as safeWhich from "@chrisgavin/safe-which";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import * as api from "./api-client"; import * as api from "./api-client";
import { CODEQL_DEFAULT_ACTION_REPOSITORY } from "./codeql";
import { Config } from "./config-utils"; import { Config } from "./config-utils";
import * as sharedEnv from "./shared-environment"; import * as sharedEnv from "./shared-environment";
import { import {
@ -900,3 +901,17 @@ export async function printDebugLogs(config: Config) {
walkLogFiles(logsDirectory); walkLogFiles(logsDirectory);
} }
} }
// Returns whether workflow kicked off by codeql-action repo itself,
// or a fork of it.
export function isAnalyzingCodeQLActionRepoOrFork(): boolean {
const codeQLActionRepoUrl = `https://api.github.com/repos/${CODEQL_DEFAULT_ACTION_REPOSITORY}`;
const repo = getWorkflowEvent()?.repository;
if (repo?.url === codeQLActionRepoUrl) {
return true;
}
if (repo?.fork && repo?.parent?.url === codeQLActionRepoUrl) {
return true;
}
return false;
}

View file

@ -83,6 +83,15 @@ export async function sendStatusReport(
} }
} }
// `expect-error` should only be set to any value by the
// codeql-action repo or a fork of it.
function hasBadExpectErrorInput(): boolean {
return (
actionsUtil.getOptionalInput("expect-error") !== "false" &&
!actionsUtil.isAnalyzingCodeQLActionRepoOrFork()
);
}
async function run() { async function run() {
const startedAt = new Date(); const startedAt = new Date();
let uploadResult: UploadResult | undefined = undefined; let uploadResult: UploadResult | undefined = undefined;
@ -112,6 +121,13 @@ async function run() {
"Config file could not be found at expected location. Has the 'init' action been called?" "Config file could not be found at expected location. Has the 'init' action been called?"
); );
} }
if (hasBadExpectErrorInput()) {
throw new Error(
"`expect-error` input parameter is for internal use only. It should only be set by codeql-action or a fork."
);
}
await util.enrichEnvironment( await util.enrichEnvironment(
util.Mode.actions, util.Mode.actions,
await getCodeQL(config.codeQLCmd) await getCodeQL(config.codeQLCmd)
@ -206,10 +222,22 @@ async function run() {
getActionsLogger() getActionsLogger()
); );
} }
// If we did not throw an error yet here, but we expect one, throw it.
if (actionsUtil.getOptionalInput("expect-error") === "true") {
core.setFailed(
`expect-error input was set to true but no error was thrown.`
);
}
} catch (origError) { } catch (origError) {
const error = const error =
origError instanceof Error ? origError : new Error(String(origError)); origError instanceof Error ? origError : new Error(String(origError));
core.setFailed(error.message); if (
actionsUtil.getOptionalInput("expect-error") !== "true" ||
hasBadExpectErrorInput()
) {
core.setFailed(error.message);
}
console.log(error); console.log(error);
if (error instanceof CodeQLAnalysisError) { if (error instanceof CodeQLAnalysisError) {

View file

@ -222,7 +222,7 @@ interface PackDownloadItem {
let cachedCodeQL: CodeQL | undefined = undefined; let cachedCodeQL: CodeQL | undefined = undefined;
const CODEQL_BUNDLE_VERSION = defaults.bundleVersion; const CODEQL_BUNDLE_VERSION = defaults.bundleVersion;
const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action"; export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
/** /**
* The oldest version of CodeQL that the Action will run with. This should be * The oldest version of CodeQL that the Action will run with. This should be