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
|
|
@ -11,7 +11,7 @@ import {
|
|||
doesDirectoryExist,
|
||||
getCodeQLDatabasePath,
|
||||
getRequiredEnvParam,
|
||||
UserError,
|
||||
ConfigurationError,
|
||||
} from "./util";
|
||||
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
|
|
@ -26,7 +26,7 @@ const pkg = require("../package.json") as JSONSchemaForNPMPackageJsonFiles;
|
|||
export const getRequiredInput = function (name: string): string {
|
||||
const value = core.getInput(name);
|
||||
if (!value) {
|
||||
throw new UserError(`Input required and not supplied: ${name}`);
|
||||
throw new ConfigurationError(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
|
@ -187,7 +187,7 @@ export async function getRef(): Promise<string> {
|
|||
const hasShaInput = !!shaInput;
|
||||
// If one of 'ref' or 'sha' are provided, both are required
|
||||
if ((hasRefInput || hasShaInput) && !(hasRefInput && hasShaInput)) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
"Both 'ref' and 'sha' are required if one of them is provided.",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ async function run() {
|
|||
}
|
||||
|
||||
if (hasBadExpectErrorInput()) {
|
||||
throw new util.UserError(
|
||||
throw new util.ConfigurationError(
|
||||
"`expect-error` input parameter is for internal use only. It should only be set by codeql-action or a fork.",
|
||||
);
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ async function run() {
|
|||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
logger,
|
||||
{ considerInvalidRequestUserError: false },
|
||||
{ considerInvalidRequestConfigError: false },
|
||||
);
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
} else {
|
||||
|
|
|
|||
209
src/cli-errors.ts
Normal file
209
src/cli-errors.ts
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import { ConfigurationError } from "./util";
|
||||
|
||||
/**
|
||||
* A class of Error that we can classify as an error stemming from a CLI
|
||||
* invocation, with associated exit code, stderr,etc.
|
||||
*/
|
||||
export class CommandInvocationError extends Error {
|
||||
constructor(
|
||||
cmd: string,
|
||||
args: string[],
|
||||
public exitCode: number,
|
||||
public stderr: string,
|
||||
public stdout: string,
|
||||
) {
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: string): string | undefined {
|
||||
const fatalErrorRegex = /.*fatal error occurred:/gi;
|
||||
let fatalErrors: string[] = [];
|
||||
let lastFatalErrorIndex: number | undefined;
|
||||
let match: RegExpMatchArray | null;
|
||||
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: string): string {
|
||||
return text[text.length - 1] === "." ? text : `${text}.`;
|
||||
}
|
||||
|
||||
/** Error messages from the CLI that we consider configuration errors and handle specially. */
|
||||
export enum CliConfigErrorCategory {
|
||||
IncompatibleWithActionVersion = "IncompatibleWithActionVersion",
|
||||
InitCalledTwice = "InitCalledTwice",
|
||||
InvalidSourceRoot = "InvalidSourceRoot",
|
||||
NoJavaScriptTypeScriptCodeFound = "NoJavaScriptTypeScriptCodeFound",
|
||||
}
|
||||
|
||||
type CliErrorConfiguration = {
|
||||
cliErrorMessageSnippets: string[];
|
||||
exitCode?: number;
|
||||
// Error message to prepend for this type of CLI error.
|
||||
// If undefined, use original CLI error message.
|
||||
additionalErrorMessageToPrepend?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* All of our caught CLI error messages that we handle specially: ie. if we
|
||||
* would like to categorize an error as a configuration error or not.
|
||||
*/
|
||||
export const cliErrorsConfig: Record<
|
||||
CliConfigErrorCategory,
|
||||
CliErrorConfiguration
|
||||
> = {
|
||||
// Version of CodeQL CLI is incompatible with this version of the CodeQL Action
|
||||
[CliConfigErrorCategory.IncompatibleWithActionVersion]: {
|
||||
cliErrorMessageSnippets: ["is not compatible with this CodeQL CLI"],
|
||||
},
|
||||
[CliConfigErrorCategory.InitCalledTwice]: {
|
||||
cliErrorMessageSnippets: [
|
||||
"Refusing to create databases",
|
||||
"exists and is not an empty directory",
|
||||
],
|
||||
additionalErrorMessageToPrepend: `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"],
|
||||
},
|
||||
/**
|
||||
* 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]: {
|
||||
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.",
|
||||
},
|
||||
};
|
||||
|
||||
// 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.
|
||||
export function getCliConfigCategoryIfExists(
|
||||
cliError: CommandInvocationError,
|
||||
): CliConfigErrorCategory | undefined {
|
||||
for (const [category, configuration] of Object.entries(cliErrorsConfig)) {
|
||||
if (
|
||||
cliError.exitCode !== undefined &&
|
||||
configuration.exitCode !== undefined &&
|
||||
cliError.exitCode === configuration.exitCode
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (allMessageSnippetsFound === true) {
|
||||
return category as CliConfigErrorCategory;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* simply returns the original error.
|
||||
*/
|
||||
export function wrapCliConfigurationError(cliError: Error): Error {
|
||||
if (!(cliError instanceof CommandInvocationError)) {
|
||||
return cliError;
|
||||
}
|
||||
|
||||
const cliConfigErrorCategory = getCliConfigCategoryIfExists(cliError);
|
||||
if (cliConfigErrorCategory === undefined) {
|
||||
return cliError;
|
||||
}
|
||||
|
||||
const errorMessageWrapperIfExists =
|
||||
cliErrorsConfig[cliConfigErrorCategory].additionalErrorMessageToPrepend;
|
||||
|
||||
return errorMessageWrapperIfExists
|
||||
? new ConfigurationError(
|
||||
`${errorMessageWrapperIfExists} ${cliError.message}`,
|
||||
)
|
||||
: new ConfigurationError(cliError.message);
|
||||
}
|
||||
|
|
@ -875,9 +875,11 @@ test("database finalize recognises JavaScript no code found error on CodeQL 2.11
|
|||
await t.throwsAsync(
|
||||
async () => await codeqlObject.finalizeDatabase("", "", ""),
|
||||
{
|
||||
message:
|
||||
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",
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build.+",
|
||||
),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -892,9 +894,11 @@ test("database finalize overrides no code found error on CodeQL 2.11.6", async (
|
|||
await t.throwsAsync(
|
||||
async () => await codeqlObject.finalizeDatabase("", "", ""),
|
||||
{
|
||||
message:
|
||||
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",
|
||||
"https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build.+",
|
||||
),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
162
src/codeql.ts
162
src/codeql.ts
|
|
@ -12,6 +12,10 @@ import {
|
|||
isAnalyzingDefaultBranch,
|
||||
} from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import {
|
||||
CommandInvocationError,
|
||||
wrapCliConfigurationError,
|
||||
} from "./cli-errors";
|
||||
import type { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import {
|
||||
|
|
@ -48,36 +52,6 @@ interface ExtraOptions {
|
|||
};
|
||||
}
|
||||
|
||||
export class CommandInvocationError extends Error {
|
||||
constructor(
|
||||
cmd: string,
|
||||
args: string[],
|
||||
public exitCode: number,
|
||||
public stderr: string,
|
||||
public stdout: string,
|
||||
) {
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface CodeQL {
|
||||
/**
|
||||
* Get the path of the CodeQL executable.
|
||||
|
|
@ -409,7 +383,9 @@ export async function setupCodeQL(
|
|||
if (process.platform === "win32") {
|
||||
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);
|
||||
|
|
@ -639,20 +615,27 @@ export async function getCodeQLForCmd(
|
|||
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 wrapCliConfigurationError(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
async runAutobuild(language: Language) {
|
||||
const autobuildCmd = path.join(
|
||||
|
|
@ -727,17 +710,13 @@ export async function getCodeQLForCmd(
|
|||
await runTool(cmd, args);
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof CommandInvocationError &&
|
||||
e instanceof Error &&
|
||||
!(await util.codeQlVersionAbove(
|
||||
this,
|
||||
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",
|
||||
);
|
||||
throw wrapCliConfigurationError(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -1128,7 +1107,7 @@ export async function getCodeQLForCmd(
|
|||
checkVersion &&
|
||||
!(await util.codeQlVersionAbove(codeql, CODEQL_MINIMUM_VERSION))
|
||||
) {
|
||||
throw new util.UserError(
|
||||
throw new util.ConfigurationError(
|
||||
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${
|
||||
(await codeql.getVersion()).version
|
||||
}`,
|
||||
|
|
@ -1265,69 +1244,6 @@ async function runTool(
|
|||
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: string): string | undefined {
|
||||
const fatalErrorRegex = /.*fatal error occurred:/gi;
|
||||
let fatalErrors: string[] = [];
|
||||
let lastFatalErrorIndex: number | undefined;
|
||||
let match: RegExpMatchArray | null;
|
||||
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: string): string {
|
||||
return text[text.length - 1] === "." ? text : `${text}.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a code scanning configuration that is to be used for a scan.
|
||||
*
|
||||
|
|
@ -1458,20 +1374,6 @@ export function getGeneratedCodeScanningConfigPath(config: Config): string {
|
|||
return path.resolve(config.tempDir, "user-config.yaml");
|
||||
}
|
||||
|
||||
function isNoCodeFoundError(e: CommandInvocationError): boolean {
|
||||
/**
|
||||
* 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: CodeQL,
|
||||
): Promise<boolean> {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
GitHubVariant,
|
||||
GitHubVersion,
|
||||
prettyPrintPack,
|
||||
UserError,
|
||||
ConfigurationError,
|
||||
withTmpDir,
|
||||
} from "./util";
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ test("load input outside of workspace", async (t) => {
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getConfigFileOutsideWorkspaceErrorMessage(
|
||||
path.join(tempDir, "../input"),
|
||||
),
|
||||
|
|
@ -247,7 +247,7 @@ test("load non-local input with invalid repo syntax", async (t) => {
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getConfigFileRepoFormatInvalidMessage(
|
||||
"octo-org/codeql-config@main",
|
||||
),
|
||||
|
|
@ -277,7 +277,7 @@ test("load non-existent input", async (t) => {
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getConfigFileDoesNotExistErrorMessage(
|
||||
path.join(tempDir, "input"),
|
||||
),
|
||||
|
|
@ -517,7 +517,7 @@ test("Remote config handles the case where a directory is provided", async (t) =
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getConfigFileDirectoryGivenMessage(repoReference),
|
||||
),
|
||||
);
|
||||
|
|
@ -546,7 +546,7 @@ test("Invalid format of remote config handled correctly", async (t) => {
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getConfigFileFormatInvalidMessage(repoReference),
|
||||
),
|
||||
);
|
||||
|
|
@ -576,7 +576,10 @@ test("No detected languages", async (t) => {
|
|||
);
|
||||
throw new Error("initConfig did not throw error");
|
||||
} catch (err) {
|
||||
t.deepEqual(err, new UserError(configUtils.getNoLanguagesError()));
|
||||
t.deepEqual(
|
||||
err,
|
||||
new ConfigurationError(configUtils.getNoLanguagesError()),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -598,7 +601,7 @@ test("Unknown languages", async (t) => {
|
|||
} catch (err) {
|
||||
t.deepEqual(
|
||||
err,
|
||||
new UserError(
|
||||
new ConfigurationError(
|
||||
configUtils.getUnknownLanguagesError(["rubbish", "english"]),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
codeQlVersionAbove,
|
||||
GitHubVersion,
|
||||
prettyPrintPack,
|
||||
UserError,
|
||||
ConfigurationError,
|
||||
} from "./util";
|
||||
|
||||
// Property names from the user-supplied config file.
|
||||
|
|
@ -332,7 +332,7 @@ export async function getLanguages(
|
|||
// If the languages parameter was not given and no languages were
|
||||
// detected then fail here as this is a workflow configuration error.
|
||||
if (languages.length === 0) {
|
||||
throw new UserError(getNoLanguagesError());
|
||||
throw new ConfigurationError(getNoLanguagesError());
|
||||
}
|
||||
|
||||
// Make sure they are supported
|
||||
|
|
@ -350,7 +350,7 @@ export async function getLanguages(
|
|||
// Any unknown languages here would have come directly from the input
|
||||
// since we filter unknown languages coming from the GitHub API.
|
||||
if (unknownLanguages.length > 0) {
|
||||
throw new UserError(getUnknownLanguagesError(unknownLanguages));
|
||||
throw new ConfigurationError(getUnknownLanguagesError(unknownLanguages));
|
||||
}
|
||||
|
||||
return parsedLanguages;
|
||||
|
|
@ -631,7 +631,7 @@ function parseQueriesFromInput(
|
|||
? rawQueriesInput.trim().slice(1).trim()
|
||||
: rawQueriesInput?.trim() ?? "";
|
||||
if (queriesInputCombines && trimmedInput.length === 0) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
getConfigFilePropertyError(
|
||||
undefined,
|
||||
"queries",
|
||||
|
|
@ -664,11 +664,11 @@ export function parsePacksFromInput(
|
|||
}
|
||||
|
||||
if (languages.length > 1) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
"Cannot specify a 'packs' input in a multi-language analysis. Use a codeql-config.yml file instead and specify packs by language.",
|
||||
);
|
||||
} else if (languages.length === 0) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
"No languages specified. Cannot process the packs input.",
|
||||
);
|
||||
}
|
||||
|
|
@ -677,7 +677,7 @@ export function parsePacksFromInput(
|
|||
if (packsInputCombines) {
|
||||
rawPacksInput = rawPacksInput.trim().substring(1).trim();
|
||||
if (!rawPacksInput) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
getConfigFilePropertyError(
|
||||
undefined,
|
||||
"packs",
|
||||
|
|
@ -715,7 +715,7 @@ export function parsePacksFromInput(
|
|||
*/
|
||||
export function parsePacksSpecification(packStr: string): Pack {
|
||||
if (typeof packStr !== "string") {
|
||||
throw new UserError(getPacksStrInvalid(packStr));
|
||||
throw new ConfigurationError(getPacksStrInvalid(packStr));
|
||||
}
|
||||
|
||||
packStr = packStr.trim();
|
||||
|
|
@ -743,14 +743,14 @@ export function parsePacksSpecification(packStr: string): Pack {
|
|||
: undefined;
|
||||
|
||||
if (!PACK_IDENTIFIER_PATTERN.test(packName)) {
|
||||
throw new UserError(getPacksStrInvalid(packStr));
|
||||
throw new ConfigurationError(getPacksStrInvalid(packStr));
|
||||
}
|
||||
if (version) {
|
||||
try {
|
||||
new semver.Range(version);
|
||||
} catch (e) {
|
||||
// The range string is invalid. OK to ignore the caught error
|
||||
throw new UserError(getPacksStrInvalid(packStr));
|
||||
throw new ConfigurationError(getPacksStrInvalid(packStr));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -764,12 +764,12 @@ export function parsePacksSpecification(packStr: string): Pack {
|
|||
path.normalize(packPath).split(path.sep).join("/") !==
|
||||
packPath.split(path.sep).join("/"))
|
||||
) {
|
||||
throw new UserError(getPacksStrInvalid(packStr));
|
||||
throw new ConfigurationError(getPacksStrInvalid(packStr));
|
||||
}
|
||||
|
||||
if (!packPath && pathStart) {
|
||||
// 0 length path
|
||||
throw new UserError(getPacksStrInvalid(packStr));
|
||||
throw new ConfigurationError(getPacksStrInvalid(packStr));
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -852,7 +852,9 @@ function parseRegistries(
|
|||
? (yaml.load(registriesInput) as RegistryConfigWithCredentials[])
|
||||
: undefined;
|
||||
} catch (e) {
|
||||
throw new UserError("Invalid registries input. Must be a YAML string.");
|
||||
throw new ConfigurationError(
|
||||
"Invalid registries input. Must be a YAML string.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -868,12 +870,16 @@ function isLocal(configPath: string): boolean {
|
|||
function getLocalConfig(configFile: string, workspacePath: string): UserConfig {
|
||||
// Error if the config file is now outside of the workspace
|
||||
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) {
|
||||
throw new UserError(getConfigFileOutsideWorkspaceErrorMessage(configFile));
|
||||
throw new ConfigurationError(
|
||||
getConfigFileOutsideWorkspaceErrorMessage(configFile),
|
||||
);
|
||||
}
|
||||
|
||||
// Error if the file does not exist
|
||||
if (!fs.existsSync(configFile)) {
|
||||
throw new UserError(getConfigFileDoesNotExistErrorMessage(configFile));
|
||||
throw new ConfigurationError(
|
||||
getConfigFileDoesNotExistErrorMessage(configFile),
|
||||
);
|
||||
}
|
||||
|
||||
return yaml.load(fs.readFileSync(configFile, "utf8")) as UserConfig;
|
||||
|
|
@ -890,7 +896,9 @@ async function getRemoteConfig(
|
|||
const pieces = format.exec(configFile);
|
||||
// 5 = 4 groups + the whole expression
|
||||
if (pieces === null || pieces.groups === undefined || pieces.length < 5) {
|
||||
throw new UserError(getConfigFileRepoFormatInvalidMessage(configFile));
|
||||
throw new ConfigurationError(
|
||||
getConfigFileRepoFormatInvalidMessage(configFile),
|
||||
);
|
||||
}
|
||||
|
||||
const response = await api
|
||||
|
|
@ -906,9 +914,11 @@ async function getRemoteConfig(
|
|||
if ("content" in response.data && response.data.content !== undefined) {
|
||||
fileContents = response.data.content;
|
||||
} else if (Array.isArray(response.data)) {
|
||||
throw new UserError(getConfigFileDirectoryGivenMessage(configFile));
|
||||
throw new ConfigurationError(
|
||||
getConfigFileDirectoryGivenMessage(configFile),
|
||||
);
|
||||
} else {
|
||||
throw new UserError(getConfigFileFormatInvalidMessage(configFile));
|
||||
throw new ConfigurationError(getConfigFileFormatInvalidMessage(configFile));
|
||||
}
|
||||
|
||||
return yaml.load(
|
||||
|
|
@ -1008,7 +1018,7 @@ function createRegistriesBlock(registries: RegistryConfigWithCredentials[]): {
|
|||
!Array.isArray(registries) ||
|
||||
registries.some((r) => !r.url || !r.packages)
|
||||
) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
"Invalid 'registries' input. Must be an array of objects with 'url' and 'packages' properties.",
|
||||
);
|
||||
}
|
||||
|
|
@ -1071,7 +1081,7 @@ function validateBuildModeInput(
|
|||
}
|
||||
|
||||
if (!Object.values(BuildMode).includes(buildModeInput as BuildMode)) {
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
`Invalid build mode: '${buildModeInput}'. Supported build modes are: ${Object.values(
|
||||
BuildMode,
|
||||
).join(", ")}.`,
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ async function maybeUploadFailedSarif(
|
|||
checkoutPath,
|
||||
category,
|
||||
logger,
|
||||
{ considerInvalidRequestUserError: false },
|
||||
{ considerInvalidRequestConfigError: false },
|
||||
);
|
||||
await uploadLib.waitForProcessing(
|
||||
repositoryNwo,
|
||||
|
|
@ -134,7 +134,7 @@ export async function tryUploadSarifIfRunFailed(
|
|||
// consider this a configuration error.
|
||||
core.exportVariable(
|
||||
EnvVar.JOB_STATUS,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigurationError,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigErrorStatus,
|
||||
);
|
||||
try {
|
||||
return await maybeUploadFailedSarif(
|
||||
|
|
@ -152,7 +152,7 @@ export async function tryUploadSarifIfRunFailed(
|
|||
} else {
|
||||
core.exportVariable(
|
||||
EnvVar.JOB_STATUS,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.Success,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.SuccessStatus,
|
||||
);
|
||||
return {
|
||||
upload_failed_run_skipped_because:
|
||||
|
|
@ -314,7 +314,7 @@ export function getFinalJobStatus(): JobStatus {
|
|||
!jobStatusFromEnvironment ||
|
||||
!Object.values(JobStatus).includes(jobStatusFromEnvironment as JobStatus)
|
||||
) {
|
||||
return JobStatus.Unknown;
|
||||
return JobStatus.UnknownStatus;
|
||||
}
|
||||
return jobStatusFromEnvironment as JobStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ import {
|
|||
getThreadsFlagValue,
|
||||
initializeEnvironment,
|
||||
isHostedRunner,
|
||||
UserError,
|
||||
ConfigurationError,
|
||||
wrapError,
|
||||
checkActionVersion,
|
||||
} from "./util";
|
||||
|
|
@ -317,7 +317,7 @@ async function run() {
|
|||
await sendStatusReport(
|
||||
await createStatusReportBase(
|
||||
"init",
|
||||
error instanceof UserError ? "user-error" : "aborted",
|
||||
error instanceof ConfigurationError ? "user-error" : "aborted",
|
||||
startedAt,
|
||||
await checkDiskUsage(),
|
||||
error.message,
|
||||
|
|
|
|||
81
src/init.ts
81
src/init.ts
|
|
@ -64,32 +64,29 @@ export async function runInit(
|
|||
logger: Logger,
|
||||
): Promise<TracerConfig | undefined> {
|
||||
fs.mkdirSync(config.dbLocation, { recursive: true });
|
||||
try {
|
||||
const { registriesAuthTokens, qlconfigFile } =
|
||||
await configUtils.generateRegistries(
|
||||
registriesInput,
|
||||
config.tempDir,
|
||||
logger,
|
||||
);
|
||||
await configUtils.wrapEnvironment(
|
||||
{
|
||||
GITHUB_TOKEN: apiDetails.auth,
|
||||
CODEQL_REGISTRIES_AUTH: registriesAuthTokens,
|
||||
},
|
||||
|
||||
// Init a database cluster
|
||||
async () =>
|
||||
await codeql.databaseInitCluster(
|
||||
config,
|
||||
sourceRoot,
|
||||
processName,
|
||||
qlconfigFile,
|
||||
logger,
|
||||
),
|
||||
const { registriesAuthTokens, qlconfigFile } =
|
||||
await configUtils.generateRegistries(
|
||||
registriesInput,
|
||||
config.tempDir,
|
||||
logger,
|
||||
);
|
||||
} catch (e) {
|
||||
throw processError(e);
|
||||
}
|
||||
await configUtils.wrapEnvironment(
|
||||
{
|
||||
GITHUB_TOKEN: apiDetails.auth,
|
||||
CODEQL_REGISTRIES_AUTH: registriesAuthTokens,
|
||||
},
|
||||
|
||||
// Init a database cluster
|
||||
async () =>
|
||||
await codeql.databaseInitCluster(
|
||||
config,
|
||||
sourceRoot,
|
||||
processName,
|
||||
qlconfigFile,
|
||||
logger,
|
||||
),
|
||||
);
|
||||
return await getCombinedTracerConfig(codeql, config);
|
||||
}
|
||||
|
||||
|
|
@ -110,42 +107,6 @@ export function printPathFiltersWarning(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly convert this error into a UserError in order to avoid
|
||||
* counting this error towards our internal error budget.
|
||||
*
|
||||
* @param e The error to possibly convert to a UserError.
|
||||
*
|
||||
* @returns A UserError if the error is a known error that can be
|
||||
* attributed to the user, otherwise the original error.
|
||||
*/
|
||||
function processError(e: any): Error {
|
||||
if (!(e instanceof Error)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
if (
|
||||
// Init action called twice
|
||||
e.message?.includes("Refusing to create databases") &&
|
||||
e.message?.includes("exists and is not an empty directory.")
|
||||
) {
|
||||
return new util.UserError(
|
||||
`Is the "init" action called twice in the same job? ${e.message}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
// Version of CodeQL CLI is incompatible with this version of the CodeQL Action
|
||||
e.message?.includes("is not compatible with this CodeQL CLI") ||
|
||||
// Expected source location for database creation does not exist
|
||||
e.message?.includes("Invalid source root")
|
||||
) {
|
||||
return new util.UserError(e.message);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are running python 3.12+ on windows, we need to switch to python 3.11.
|
||||
* This check happens in a powershell script.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { UserError } from "./util";
|
||||
import { ConfigurationError } from "./util";
|
||||
|
||||
// A repository name with owner, parsed into its two parts
|
||||
export interface RepositoryNwo {
|
||||
|
|
@ -9,7 +9,7 @@ export interface RepositoryNwo {
|
|||
export function parseRepositoryNwo(input: string): RepositoryNwo {
|
||||
const parts = input.split("/");
|
||||
if (parts.length !== 2) {
|
||||
throw new UserError(`"${input}" is not a valid repository name`);
|
||||
throw new ConfigurationError(`"${input}" is not a valid repository name`);
|
||||
}
|
||||
return {
|
||||
owner: parts[0],
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getTemporaryDirectory,
|
||||
} from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { CommandInvocationError } from "./codeql";
|
||||
import { CommandInvocationError } from "./cli-errors";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { runResolveBuildEnvironment } from "./resolve-environment";
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export async function runResolveBuildEnvironment(
|
|||
) {
|
||||
const parsedLanguage = parseLanguage(languageInput)?.toString();
|
||||
if (parsedLanguage === undefined) {
|
||||
throw new util.UserError(
|
||||
throw new util.ConfigurationError(
|
||||
`Did not recognize the language '${languageInput}'.`,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ export async function downloadCodeQL(
|
|||
export function getCodeQLURLVersion(url: string): string {
|
||||
const match = url.match(/\/codeql-bundle-(.*)\//);
|
||||
if (match === null || match.length < 2) {
|
||||
throw new util.UserError(
|
||||
throw new util.ConfigurationError(
|
||||
`Malformed tools url: ${url}. Version could not be inferred`,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { getAnalysisKey, getApiClient } from "./api-client";
|
||||
import { EnvVar } from "./environment";
|
||||
import {
|
||||
UserError,
|
||||
ConfigurationError,
|
||||
isHTTPError,
|
||||
getRequiredEnvParam,
|
||||
getCachedCodeQlVersion,
|
||||
|
|
@ -40,10 +40,10 @@ export type ActionStatus =
|
|||
|
||||
/** Overall status of the entire job. String values match the Hydro schema. */
|
||||
export enum JobStatus {
|
||||
Unknown = "JOB_STATUS_UNKNOWN",
|
||||
Success = "JOB_STATUS_SUCCESS",
|
||||
Failure = "JOB_STATUS_FAILURE",
|
||||
ConfigurationError = "JOB_STATUS_CONFIGURATION_ERROR",
|
||||
UnknownStatus = "JOB_STATUS_UNKNOWN",
|
||||
SuccessStatus = "JOB_STATUS_SUCCESS",
|
||||
FailureStatus = "JOB_STATUS_FAILURE",
|
||||
ConfigErrorStatus = "JOB_STATUS_CONFIGURATION_ERROR",
|
||||
}
|
||||
|
||||
export interface StatusReportBase {
|
||||
|
|
@ -135,7 +135,7 @@ export function getActionsStatus(
|
|||
otherFailureCause?: string,
|
||||
): ActionStatus {
|
||||
if (error || otherFailureCause) {
|
||||
return error instanceof UserError ? "user-error" : "failure";
|
||||
return error instanceof ConfigurationError ? "user-error" : "failure";
|
||||
} else {
|
||||
return "success";
|
||||
}
|
||||
|
|
@ -150,12 +150,12 @@ function setJobStatusIfUnsuccessful(actionStatus: ActionStatus) {
|
|||
if (actionStatus === "user-error") {
|
||||
core.exportVariable(
|
||||
EnvVar.JOB_STATUS,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigurationError,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigErrorStatus,
|
||||
);
|
||||
} else if (actionStatus === "failure" || actionStatus === "aborted") {
|
||||
core.exportVariable(
|
||||
EnvVar.JOB_STATUS,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.Failure,
|
||||
process.env[EnvVar.JOB_STATUS] ?? JobStatus.FailureStatus,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import * as fingerprints from "./fingerprints";
|
|||
import { Logger } from "./logging";
|
||||
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
|
||||
import * as util from "./util";
|
||||
import { SarifFile, UserError, wrapError } from "./util";
|
||||
import { SarifFile, ConfigurationError, wrapError } from "./util";
|
||||
|
||||
// Takes a list of paths to sarif files and combines them together,
|
||||
// returning the contents of the combined sarif file.
|
||||
|
|
@ -158,7 +158,7 @@ export function findSarifFilesInDir(sarifPath: string): string[] {
|
|||
* Uploads a single SARIF file or a directory of SARIF files depending on what `sarifPath` refers
|
||||
* to.
|
||||
*
|
||||
* @param considerInvalidRequestUserError Whether an invalid request, for example one with a
|
||||
* @param considerInvalidRequestConfigError Whether an invalid request, for example one with a
|
||||
* `sarifPath` that does not exist, should be considered a
|
||||
* user error.
|
||||
*/
|
||||
|
|
@ -168,8 +168,8 @@ export async function uploadFromActions(
|
|||
category: string | undefined,
|
||||
logger: Logger,
|
||||
{
|
||||
considerInvalidRequestUserError,
|
||||
}: { considerInvalidRequestUserError: boolean },
|
||||
considerInvalidRequestConfigError: considerInvalidRequestConfigError,
|
||||
}: { considerInvalidRequestConfigError: boolean },
|
||||
): Promise<UploadResult> {
|
||||
try {
|
||||
return await uploadFiles(
|
||||
|
|
@ -187,8 +187,8 @@ export async function uploadFromActions(
|
|||
logger,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof InvalidRequestError && considerInvalidRequestUserError) {
|
||||
throw new UserError(e.message);
|
||||
if (e instanceof InvalidRequestError && considerInvalidRequestConfigError) {
|
||||
throw new ConfigurationError(e.message);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
|
@ -489,8 +489,8 @@ export async function waitForProcessing(
|
|||
break;
|
||||
} else if (status === "failed") {
|
||||
const message = `Code Scanning could not process the submitted SARIF file:\n${response.data.errors}`;
|
||||
throw shouldConsiderAsUserError(response.data.errors as string[])
|
||||
? new UserError(message)
|
||||
throw shouldConsiderConfigurationError(response.data.errors as string[])
|
||||
? new ConfigurationError(message)
|
||||
: new InvalidRequestError(message);
|
||||
} else {
|
||||
util.assertNever(status);
|
||||
|
|
@ -508,7 +508,7 @@ export async function waitForProcessing(
|
|||
/**
|
||||
* Returns whether the provided processing errors should be considered a user error.
|
||||
*/
|
||||
function shouldConsiderAsUserError(processingErrors: string[]): boolean {
|
||||
function shouldConsiderConfigurationError(processingErrors: string[]): boolean {
|
||||
return (
|
||||
processingErrors.length === 1 &&
|
||||
processingErrors[0] ===
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ async function run() {
|
|||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
logger,
|
||||
{ considerInvalidRequestUserError: true },
|
||||
{ considerInvalidRequestConfigError: true },
|
||||
);
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
|
||||
|
|
|
|||
16
src/util.ts
16
src/util.ts
|
|
@ -120,7 +120,7 @@ export function getExtraOptionsEnvParam(): object {
|
|||
return JSON.parse(raw);
|
||||
} catch (unwrappedError) {
|
||||
const error = wrapError(unwrappedError);
|
||||
throw new UserError(
|
||||
throw new ConfigurationError(
|
||||
`${varName} environment variable is set, but does not contain valid JSON: ${error.message}`,
|
||||
);
|
||||
}
|
||||
|
|
@ -204,7 +204,9 @@ export function getMemoryFlagValueForPlatform(
|
|||
if (userInput) {
|
||||
memoryToUseMegaBytes = Number(userInput);
|
||||
if (Number.isNaN(memoryToUseMegaBytes) || memoryToUseMegaBytes <= 0) {
|
||||
throw new UserError(`Invalid RAM setting "${userInput}", specified.`);
|
||||
throw new ConfigurationError(
|
||||
`Invalid RAM setting "${userInput}", specified.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const totalMemoryMegaBytes = totalMemoryBytes / (1024 * 1024);
|
||||
|
|
@ -373,7 +375,9 @@ export function getThreadsFlagValue(
|
|||
if (userInput) {
|
||||
numThreads = Number(userInput);
|
||||
if (Number.isNaN(numThreads)) {
|
||||
throw new UserError(`Invalid threads setting "${userInput}", specified.`);
|
||||
throw new ConfigurationError(
|
||||
`Invalid threads setting "${userInput}", specified.`,
|
||||
);
|
||||
}
|
||||
if (numThreads > maxThreads) {
|
||||
logger.info(
|
||||
|
|
@ -500,14 +504,14 @@ export function parseGitHubUrl(inputUrl: string): string {
|
|||
inputUrl = `https://${inputUrl}`;
|
||||
}
|
||||
if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) {
|
||||
throw new UserError(`"${originalUrl}" is not a http or https URL`);
|
||||
throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`);
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(inputUrl);
|
||||
} catch (e) {
|
||||
throw new UserError(`"${originalUrl}" is not a valid URL`);
|
||||
throw new ConfigurationError(`"${originalUrl}" is not a valid URL`);
|
||||
}
|
||||
|
||||
// If we detect this is trying to be to github.com
|
||||
|
|
@ -652,7 +656,7 @@ export class HTTPError extends Error {
|
|||
* An Error class that indicates an error that occurred due to
|
||||
* a misconfiguration of the action or the CodeQL CLI.
|
||||
*/
|
||||
export class UserError extends Error {
|
||||
export class ConfigurationError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue