Refactor configuration errors (#2105)
Refactor the existing classes of configuration errors into their own file; consolidate the place we check for configuration errors into `codeql.ts`, where the actual command invocations happen. Also, rename the `UserError` type to `ConfigurationError` to standardize on a single term.
This commit is contained in:
parent
fc9f9e5ef9
commit
1515e2bb20
54 changed files with 654 additions and 502 deletions
141
lib/codeql.js
generated
141
lib/codeql.js
generated
|
|
@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getGeneratedCodeScanningConfigPath = exports.getTrapCachingExtractorConfigArgsForLang = exports.getTrapCachingExtractorConfigArgs = exports.getExtraOptions = exports.getCodeQLForCmd = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.setupCodeQL = exports.CODEQL_VERSION_SUBLANGUAGE_FILE_COVERAGE = exports.CODEQL_VERSION_ANALYSIS_SUMMARY_V2 = exports.CODEQL_VERSION_LANGUAGE_ALIASING = exports.CODEQL_VERSION_LANGUAGE_BASELINE_CONFIG = exports.CODEQL_VERSION_RESOLVE_ENVIRONMENT = exports.CODEQL_VERSION_DIAGNOSTICS_EXPORT_FIXED = exports.CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE = exports.CODEQL_VERSION_INIT_WITH_QLCONFIG = exports.CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG = exports.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = exports.CommandInvocationError = void 0;
|
||||
exports.getGeneratedCodeScanningConfigPath = exports.getTrapCachingExtractorConfigArgsForLang = exports.getTrapCachingExtractorConfigArgs = exports.getExtraOptions = exports.getCodeQLForCmd = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.setupCodeQL = exports.CODEQL_VERSION_SUBLANGUAGE_FILE_COVERAGE = exports.CODEQL_VERSION_ANALYSIS_SUMMARY_V2 = exports.CODEQL_VERSION_LANGUAGE_ALIASING = exports.CODEQL_VERSION_LANGUAGE_BASELINE_CONFIG = exports.CODEQL_VERSION_RESOLVE_ENVIRONMENT = exports.CODEQL_VERSION_DIAGNOSTICS_EXPORT_FIXED = exports.CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE = exports.CODEQL_VERSION_INIT_WITH_QLCONFIG = exports.CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG = exports.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const core = __importStar(require("@actions/core"));
|
||||
|
|
@ -31,6 +31,7 @@ const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
|
|||
const yaml = __importStar(require("js-yaml"));
|
||||
const semver = __importStar(require("semver"));
|
||||
const actions_util_1 = require("./actions-util");
|
||||
const cli_errors_1 = require("./cli-errors");
|
||||
const environment_1 = require("./environment");
|
||||
const feature_flags_1 = require("./feature-flags");
|
||||
const languages_1 = require("./languages");
|
||||
|
|
@ -38,29 +39,6 @@ const setupCodeql = __importStar(require("./setup-codeql"));
|
|||
const tools_features_1 = require("./tools-features");
|
||||
const util = __importStar(require("./util"));
|
||||
const util_1 = require("./util");
|
||||
class CommandInvocationError extends Error {
|
||||
constructor(cmd, args, exitCode, stderr, stdout) {
|
||||
const prettyCommand = [cmd, ...args]
|
||||
.map((x) => (x.includes(" ") ? `'${x}'` : x))
|
||||
.join(" ");
|
||||
const fatalErrors = extractFatalErrors(stderr);
|
||||
const lastLine = stderr.trim().split("\n").pop()?.trim();
|
||||
let error = fatalErrors
|
||||
? ` and error was: ${fatalErrors.trim()}`
|
||||
: lastLine
|
||||
? ` and last log line was: ${lastLine}`
|
||||
: "";
|
||||
if (error[error.length - 1] !== ".") {
|
||||
error += ".";
|
||||
}
|
||||
super(`Encountered a fatal error while running "${prettyCommand}". ` +
|
||||
`Exit code was ${exitCode}${error} See the logs for more details.`);
|
||||
this.exitCode = exitCode;
|
||||
this.stderr = stderr;
|
||||
this.stdout = stdout;
|
||||
}
|
||||
}
|
||||
exports.CommandInvocationError = CommandInvocationError;
|
||||
/**
|
||||
* Stores the CodeQL object, and is populated by `setupCodeQL` or `getCodeQL`.
|
||||
* Can be overridden in tests using `setCodeQL`.
|
||||
|
|
@ -156,7 +134,7 @@ async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliV
|
|||
codeqlCmd += ".exe";
|
||||
}
|
||||
else if (process.platform !== "linux" && process.platform !== "darwin") {
|
||||
throw new util.UserError(`Unsupported platform: ${process.platform}`);
|
||||
throw new util.ConfigurationError(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
cachedCodeQL = await getCodeQLForCmd(codeqlCmd, checkVersion);
|
||||
return {
|
||||
|
|
@ -316,16 +294,24 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
|||
else if (await util.codeQlVersionAbove(this, exports.CODEQL_VERSION_SUBLANGUAGE_FILE_COVERAGE)) {
|
||||
extraArgs.push("--no-sublanguage-file-coverage");
|
||||
}
|
||||
await runTool(cmd, [
|
||||
"database",
|
||||
"init",
|
||||
"--db-cluster",
|
||||
config.dbLocation,
|
||||
`--source-root=${sourceRoot}`,
|
||||
...(await getLanguageAliasingArguments(this)),
|
||||
...extraArgs,
|
||||
...getExtraOptionsFromEnv(["database", "init"]),
|
||||
], { stdin: externalRepositoryToken });
|
||||
try {
|
||||
await runTool(cmd, [
|
||||
"database",
|
||||
"init",
|
||||
"--db-cluster",
|
||||
config.dbLocation,
|
||||
`--source-root=${sourceRoot}`,
|
||||
...(await getLanguageAliasingArguments(this)),
|
||||
...extraArgs,
|
||||
...getExtraOptionsFromEnv(["database", "init"]),
|
||||
], { stdin: externalRepositoryToken });
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw (0, cli_errors_1.wrapCliConfigurationError)(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
async runAutobuild(language) {
|
||||
const autobuildCmd = path.join(await this.resolveExtractor(language), "tools", process.platform === "win32" ? "autobuild.cmd" : "autobuild.sh");
|
||||
|
|
@ -389,11 +375,9 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
|||
await runTool(cmd, args);
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof CommandInvocationError &&
|
||||
!(await util.codeQlVersionAbove(this, exports.CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE)) &&
|
||||
isNoCodeFoundError(e)) {
|
||||
throw new util.UserError("No code found during the build. Please see: " +
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build");
|
||||
if (e instanceof Error &&
|
||||
!(await util.codeQlVersionAbove(this, exports.CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE))) {
|
||||
throw (0, cli_errors_1.wrapCliConfigurationError)(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -700,7 +684,7 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
|||
// CodeQL object is created.
|
||||
if (checkVersion &&
|
||||
!(await util.codeQlVersionAbove(codeql, CODEQL_MINIMUM_VERSION))) {
|
||||
throw new util.UserError(`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${(await codeql.getVersion()).version}`);
|
||||
throw new util.ConfigurationError(`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${(await codeql.getVersion()).version}`);
|
||||
}
|
||||
else if (checkVersion &&
|
||||
process.env[environment_1.EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING] !== "true" &&
|
||||
|
|
@ -805,71 +789,10 @@ async function runTool(cmd, args = [], opts = {}) {
|
|||
...(opts.stdin ? { input: Buffer.from(opts.stdin || "") } : {}),
|
||||
}).exec();
|
||||
if (exitCode !== 0) {
|
||||
throw new CommandInvocationError(cmd, args, exitCode, error, output);
|
||||
throw new cli_errors_1.CommandInvocationError(cmd, args, exitCode, error, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
/**
|
||||
* Provide a better error message from the stderr of a CLI invocation that failed with a fatal
|
||||
* error.
|
||||
*
|
||||
* - If the CLI invocation failed with a fatal error, this returns that fatal error, followed by
|
||||
* any fatal errors that occurred in plumbing commands.
|
||||
* - If the CLI invocation did not fail with a fatal error, this returns `undefined`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* Running TRAP import for CodeQL database at /home/runner/work/_temp/codeql_databases/javascript...
|
||||
* A fatal error occurred: Evaluator heap must be at least 384.00 MiB
|
||||
* A fatal error occurred: Dataset import for
|
||||
* /home/runner/work/_temp/codeql_databases/javascript/db-javascript failed with code 2
|
||||
* ```
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* ```
|
||||
* Encountered a fatal error while running "codeql-for-testing database finalize --finalize-dataset
|
||||
* --threads=2 --ram=2048 db". Exit code was 32 and error was: A fatal error occurred: Dataset
|
||||
* import for /home/runner/work/_temp/codeql_databases/javascript/db-javascript failed with code 2.
|
||||
* Context: A fatal error occurred: Evaluator heap must be at least 384.00 MiB.
|
||||
* ```
|
||||
*
|
||||
* Where possible, this tries to summarize the error into a single line, as this displays better in
|
||||
* the Actions UI.
|
||||
*/
|
||||
function extractFatalErrors(error) {
|
||||
const fatalErrorRegex = /.*fatal error occurred:/gi;
|
||||
let fatalErrors = [];
|
||||
let lastFatalErrorIndex;
|
||||
let match;
|
||||
while ((match = fatalErrorRegex.exec(error)) !== null) {
|
||||
if (lastFatalErrorIndex !== undefined) {
|
||||
fatalErrors.push(error.slice(lastFatalErrorIndex, match.index).trim());
|
||||
}
|
||||
lastFatalErrorIndex = match.index;
|
||||
}
|
||||
if (lastFatalErrorIndex !== undefined) {
|
||||
const lastError = error.slice(lastFatalErrorIndex).trim();
|
||||
if (fatalErrors.length === 0) {
|
||||
// No other errors
|
||||
return lastError;
|
||||
}
|
||||
const isOneLiner = !fatalErrors.some((e) => e.includes("\n"));
|
||||
if (isOneLiner) {
|
||||
fatalErrors = fatalErrors.map(ensureEndsInPeriod);
|
||||
}
|
||||
return [
|
||||
ensureEndsInPeriod(lastError),
|
||||
"Context:",
|
||||
...fatalErrors.reverse(),
|
||||
].join(isOneLiner ? " " : "\n");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function ensureEndsInPeriod(text) {
|
||||
return text[text.length - 1] === "." ? text : `${text}.`;
|
||||
}
|
||||
/**
|
||||
* Generates a code scanning configuration that is to be used for a scan.
|
||||
*
|
||||
|
|
@ -971,18 +894,6 @@ function getGeneratedCodeScanningConfigPath(config) {
|
|||
return path.resolve(config.tempDir, "user-config.yaml");
|
||||
}
|
||||
exports.getGeneratedCodeScanningConfigPath = getGeneratedCodeScanningConfigPath;
|
||||
function isNoCodeFoundError(e) {
|
||||
/**
|
||||
* Earlier versions of the JavaScript extractor (pre-CodeQL 2.12.0) extract externs even if no
|
||||
* source code was found. This means that we don't get the no code found error from
|
||||
* `codeql database finalize`. To ensure users get a good error message, we detect this manually
|
||||
* here, and upon detection override the error message.
|
||||
*
|
||||
* This can be removed once support for CodeQL 2.11.6 is removed.
|
||||
*/
|
||||
const javascriptNoCodeFoundWarning = "No JavaScript or TypeScript code found.";
|
||||
return e.exitCode === 32 || e.stderr.includes(javascriptNoCodeFoundWarning);
|
||||
}
|
||||
async function isDiagnosticsExportInvalidSarifFixed(codeql) {
|
||||
return await util.codeQlVersionAbove(codeql, exports.CODEQL_VERSION_DIAGNOSTICS_EXPORT_FIXED);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue