Add common CLI configuration error categories (#2130)
Co-authored-by: Henry Mercer <henry@henrymercer.name>
This commit is contained in:
parent
bc64d12bb9
commit
7b30fefa68
10 changed files with 238 additions and 124 deletions
|
|
@ -1,5 +1,8 @@
|
|||
import { ConfigurationError } from "./util";
|
||||
|
||||
const NO_SOURCE_CODE_SEEN_DOCS_LINK =
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build";
|
||||
|
||||
/**
|
||||
* A class of Error that we can classify as an error stemming from a CLI
|
||||
* invocation, with associated exit code, stderr,etc.
|
||||
|
|
@ -102,15 +105,18 @@ export enum CliConfigErrorCategory {
|
|||
IncompatibleWithActionVersion = "IncompatibleWithActionVersion",
|
||||
InitCalledTwice = "InitCalledTwice",
|
||||
InvalidSourceRoot = "InvalidSourceRoot",
|
||||
NoJavaScriptTypeScriptCodeFound = "NoJavaScriptTypeScriptCodeFound",
|
||||
NoBuildCommandAutodetected = "NoBuildCommandAutodetected",
|
||||
NoBuildMethodAutodetected = "NoBuildMethodAutodetected",
|
||||
NoSourceCodeSeen = "NoSourceCodeSeen",
|
||||
NoSupportedBuildCommandSucceeded = "NoSupportedBuildCommandSucceeded",
|
||||
NoSupportedBuildSystemDetected = "NoSupportedBuildSystemDetected",
|
||||
}
|
||||
|
||||
type CliErrorConfiguration = {
|
||||
cliErrorMessageSnippets: string[];
|
||||
/** One of these candidates, or the exit code, must be present in the error message. */
|
||||
cliErrorMessageCandidates: RegExp[];
|
||||
exitCode?: number;
|
||||
// Error message to prepend for this type of CLI error.
|
||||
// If undefined, use original CLI error message.
|
||||
additionalErrorMessageToPrepend?: string;
|
||||
additionalErrorMessageToAppend?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -123,40 +129,75 @@ export const cliErrorsConfig: Record<
|
|||
> = {
|
||||
// Version of CodeQL CLI is incompatible with this version of the CodeQL Action
|
||||
[CliConfigErrorCategory.IncompatibleWithActionVersion]: {
|
||||
cliErrorMessageSnippets: ["is not compatible with this CodeQL CLI"],
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp("is not compatible with this CodeQL CLI"),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.InitCalledTwice]: {
|
||||
cliErrorMessageSnippets: [
|
||||
"Refusing to create databases",
|
||||
"exists and is not an empty directory",
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp(
|
||||
"Refusing to create databases .* but could not process any of it",
|
||||
),
|
||||
],
|
||||
additionalErrorMessageToPrepend: `Is the "init" action called twice in the same job?`,
|
||||
additionalErrorMessageToAppend: `Is the "init" action called twice in the same job?`,
|
||||
},
|
||||
// Expected source location for database creation does not exist
|
||||
[CliConfigErrorCategory.InvalidSourceRoot]: {
|
||||
cliErrorMessageSnippets: ["Invalid source root"],
|
||||
cliErrorMessageCandidates: [new RegExp("Invalid source root")],
|
||||
},
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[CliConfigErrorCategory.NoJavaScriptTypeScriptCodeFound]: {
|
||||
[CliConfigErrorCategory.NoBuildCommandAutodetected]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp("Could not auto-detect a suitable build method"),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.NoBuildMethodAutodetected]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp(
|
||||
"Could not detect a suitable build command for the source checkout",
|
||||
),
|
||||
],
|
||||
},
|
||||
// Usually when a manual build script has failed, or if an autodetected language
|
||||
// was unintended to have CodeQL analysis run on it.
|
||||
[CliConfigErrorCategory.NoSourceCodeSeen]: {
|
||||
exitCode: 32,
|
||||
cliErrorMessageSnippets: ["No JavaScript or TypeScript code found."],
|
||||
additionalErrorMessageToPrepend:
|
||||
"No code found during the build. Please see: " +
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build.",
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp(
|
||||
"CodeQL detected code written in .* but could not process any of it",
|
||||
),
|
||||
new RegExp(
|
||||
"CodeQL did not detect any code written in languages supported by CodeQL",
|
||||
),
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
new RegExp("No JavaScript or TypeScript code found"),
|
||||
],
|
||||
},
|
||||
|
||||
[CliConfigErrorCategory.NoSupportedBuildCommandSucceeded]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp("No supported build command succeeded"),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.NoSupportedBuildSystemDetected]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp("No supported build system detected"),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Check if the given CLI error or exit code, if applicable, apply to any known
|
||||
// CLI errors in the configuration record. If either the CLI error message matches all of
|
||||
// the error messages in the config record, or the exit codes match, return the error category;
|
||||
// if not, return undefined.
|
||||
/**
|
||||
* Check if the given CLI error or exit code, if applicable, apply to any known
|
||||
* CLI errors in the configuration record. If either the CLI error message matches one of
|
||||
* the error messages in the config record, or the exit codes match, return the error category;
|
||||
* if not, return undefined.
|
||||
*/
|
||||
export function getCliConfigCategoryIfExists(
|
||||
cliError: CommandInvocationError,
|
||||
): CliConfigErrorCategory | undefined {
|
||||
|
|
@ -169,23 +210,31 @@ export function getCliConfigCategoryIfExists(
|
|||
return category as CliConfigErrorCategory;
|
||||
}
|
||||
|
||||
let allMessageSnippetsFound: boolean = true;
|
||||
for (const e of configuration.cliErrorMessageSnippets) {
|
||||
if (!cliError.message.includes(e) && !cliError.stderr.includes(e)) {
|
||||
allMessageSnippetsFound = false;
|
||||
for (const e of configuration.cliErrorMessageCandidates) {
|
||||
if (cliError.message.match(e) || cliError.stderr.match(e)) {
|
||||
return category as CliConfigErrorCategory;
|
||||
}
|
||||
}
|
||||
if (allMessageSnippetsFound === true) {
|
||||
return category as CliConfigErrorCategory;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a clearer error message with the docs link if the error message does not already
|
||||
* include it. Can be removed once support for CodeQL 2.11.6 is removed; at that point, all runs
|
||||
* should already include the doc link.
|
||||
*/
|
||||
function prependDocsLinkIfApplicable(cliErrorMessage: string): string {
|
||||
if (!cliErrorMessage.includes(NO_SOURCE_CODE_SEEN_DOCS_LINK)) {
|
||||
return `No code found during the build. Please see: ${NO_SOURCE_CODE_SEEN_DOCS_LINK}. Detailed error: ${cliErrorMessage}`;
|
||||
}
|
||||
return cliErrorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes an error received from the CLI to a ConfigurationError with optionally an extra
|
||||
* error message prepended, if it exists in a known set of configuration errors. Otherwise,
|
||||
* error message appended, if it exists in a known set of configuration errors. Otherwise,
|
||||
* simply returns the original error.
|
||||
*/
|
||||
export function wrapCliConfigurationError(cliError: Error): Error {
|
||||
|
|
@ -198,12 +247,19 @@ export function wrapCliConfigurationError(cliError: Error): Error {
|
|||
return cliError;
|
||||
}
|
||||
|
||||
const errorMessageWrapperIfExists =
|
||||
cliErrorsConfig[cliConfigErrorCategory].additionalErrorMessageToPrepend;
|
||||
let errorMessageBuilder = cliError.message;
|
||||
|
||||
return errorMessageWrapperIfExists
|
||||
? new ConfigurationError(
|
||||
`${errorMessageWrapperIfExists} ${cliError.message}`,
|
||||
)
|
||||
: new ConfigurationError(cliError.message);
|
||||
// Can be removed once support for CodeQL 2.11.6 is removed; at that point, all runs should
|
||||
// already include the doc link.
|
||||
if (cliConfigErrorCategory === CliConfigErrorCategory.NoSourceCodeSeen) {
|
||||
errorMessageBuilder = prependDocsLinkIfApplicable(errorMessageBuilder);
|
||||
}
|
||||
|
||||
const additionalErrorMessageToAppend =
|
||||
cliErrorsConfig[cliConfigErrorCategory].additionalErrorMessageToAppend;
|
||||
if (additionalErrorMessageToAppend !== undefined) {
|
||||
errorMessageBuilder = `${errorMessageBuilder} ${additionalErrorMessageToAppend}`;
|
||||
}
|
||||
|
||||
return new ConfigurationError(errorMessageBuilder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -877,8 +877,8 @@ test("database finalize recognises JavaScript no code found error on CodeQL 2.11
|
|||
{
|
||||
instanceOf: util.ConfigurationError,
|
||||
message: new RegExp(
|
||||
"No code found during the build. Please see: " +
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build.+",
|
||||
"No code found during the build\\. Please see: " +
|
||||
"https://gh\\.io/troubleshooting-code-scanning/no-source-code-seen-during-build\\.",
|
||||
),
|
||||
},
|
||||
);
|
||||
|
|
@ -896,8 +896,8 @@ test("database finalize overrides no code found error on CodeQL 2.11.6", async (
|
|||
{
|
||||
instanceOf: util.ConfigurationError,
|
||||
message: new RegExp(
|
||||
"No code found during the build. Please see: " +
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build.+",
|
||||
"No code found during the build\\. Please see: " +
|
||||
"https://gh\\.io/troubleshooting-code-scanning/no-source-code-seen-during-build\\.",
|
||||
),
|
||||
},
|
||||
);
|
||||
|
|
@ -942,9 +942,17 @@ test("runTool summarizes several fatal errors", async (t) => {
|
|||
async () =>
|
||||
await codeqlObject.finalizeDatabase("db", "--threads=2", "--ram=2048"),
|
||||
{
|
||||
message:
|
||||
'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: ${datasetImportError}. Context: ${heapError}. See the logs for more details.`,
|
||||
instanceOf: util.ConfigurationError,
|
||||
message: new RegExp(
|
||||
'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: ${datasetImportError.replaceAll(
|
||||
".",
|
||||
"\\.",
|
||||
)}\\. Context: ${heapError.replaceAll(
|
||||
".",
|
||||
"\\.",
|
||||
)}\\. See the logs for more details\\.`,
|
||||
),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -961,9 +969,11 @@ test("runTool outputs last line of stderr if fatal error could not be found", as
|
|||
async () =>
|
||||
await codeqlObject.finalizeDatabase("db", "--threads=2", "--ram=2048"),
|
||||
{
|
||||
message:
|
||||
'Encountered a fatal error while running "codeql-for-testing database finalize --finalize-dataset --threads=2 --ram=2048 db". ' +
|
||||
"Exit code was 32 and last log line was: line5. See the logs for more details.",
|
||||
instanceOf: util.ConfigurationError,
|
||||
message: new RegExp(
|
||||
'Encountered a fatal error while running \\"codeql-for-testing database finalize --finalize-dataset --threads=2 --ram=2048 db\\"\\. ' +
|
||||
"Exit code was 32 and last log line was: line5\\. See the logs for more details\\.",
|
||||
),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -284,6 +284,8 @@ const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.7";
|
|||
const GHES_MOST_RECENT_DEPRECATION_DATE = "2023-11-08";
|
||||
|
||||
/*
|
||||
* Deprecated in favor of ToolsFeature.
|
||||
*
|
||||
* Versions of CodeQL that version-flag certain functionality in the Action.
|
||||
* For convenience, please keep these in descending order. Once a version
|
||||
* flag is older than the oldest supported version above, it may be removed.
|
||||
|
|
@ -306,12 +308,6 @@ export const CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG = "2.12.3";
|
|||
*/
|
||||
export const CODEQL_VERSION_INIT_WITH_QLCONFIG = "2.12.4";
|
||||
|
||||
/**
|
||||
* Versions 2.12.4+ of the CodeQL CLI provide a better error message when `database finalize`
|
||||
* determines that no code has been found.
|
||||
*/
|
||||
export const CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE = "2.12.4";
|
||||
|
||||
/**
|
||||
* Versions 2.13.1+ of the CodeQL CLI fix a bug where diagnostics export could produce invalid SARIF.
|
||||
*/
|
||||
|
|
@ -670,7 +666,14 @@ export async function getCodeQLForCmd(
|
|||
// When `DYLD_INSERT_LIBRARIES` is set in the environment for a step,
|
||||
// the Actions runtime introduces its own workaround for SIP
|
||||
// (https://github.com/actions/runner/pull/416).
|
||||
await runTool(autobuildCmd);
|
||||
try {
|
||||
await runTool(autobuildCmd);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw wrapCliConfigurationError(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
async extractScannedLanguage(config: Config, language: Language) {
|
||||
await runTool(cmd, [
|
||||
|
|
@ -709,13 +712,7 @@ export async function getCodeQLForCmd(
|
|||
try {
|
||||
await runTool(cmd, args);
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof Error &&
|
||||
!(await util.codeQlVersionAbove(
|
||||
this,
|
||||
CODEQL_VERSION_BETTER_NO_CODE_ERROR_MESSAGE,
|
||||
))
|
||||
) {
|
||||
if (e instanceof Error) {
|
||||
throw wrapCliConfigurationError(e);
|
||||
}
|
||||
throw e;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue