-
Notifications
You must be signed in to change notification settings - Fork 193
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3259 from github/koesie10/improve-error-message
Improve CLI error messages
- Loading branch information
Showing
5 changed files
with
223 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { asError, getErrorMessage } from "../common/helpers-pure"; | ||
|
||
// https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/exit-codes | ||
const EXIT_CODE_USER_ERROR = 2; | ||
const EXIT_CODE_CANCELLED = 98; | ||
|
||
export class ExitCodeError extends Error { | ||
constructor(public readonly exitCode: number | null) { | ||
super(`Process exited with code ${exitCode}`); | ||
} | ||
} | ||
|
||
export class CliError extends Error { | ||
constructor( | ||
message: string, | ||
public readonly stderr: string | undefined, | ||
public readonly cause: Error, | ||
public readonly commandDescription: string, | ||
public readonly commandArgs: string[], | ||
) { | ||
super(message); | ||
} | ||
} | ||
|
||
export function getCliError( | ||
e: unknown, | ||
stderr: string | undefined, | ||
commandDescription: string, | ||
commandArgs: string[], | ||
): CliError { | ||
const error = asError(e); | ||
|
||
if (!(error instanceof ExitCodeError) || !stderr) { | ||
return formatCliErrorFallback( | ||
error, | ||
stderr, | ||
commandDescription, | ||
commandArgs, | ||
); | ||
} | ||
|
||
switch (error.exitCode) { | ||
case EXIT_CODE_USER_ERROR: { | ||
// This is an error that we should try to format nicely | ||
const fatalErrorIndex = stderr.lastIndexOf("A fatal error occurred: "); | ||
if (fatalErrorIndex !== -1) { | ||
return new CliError( | ||
stderr.slice(fatalErrorIndex), | ||
stderr, | ||
error, | ||
commandDescription, | ||
commandArgs, | ||
); | ||
} | ||
|
||
break; | ||
} | ||
case EXIT_CODE_CANCELLED: { | ||
const cancellationIndex = stderr.lastIndexOf( | ||
"Computation was cancelled: ", | ||
); | ||
if (cancellationIndex !== -1) { | ||
return new CliError( | ||
stderr.slice(cancellationIndex), | ||
stderr, | ||
error, | ||
commandDescription, | ||
commandArgs, | ||
); | ||
} | ||
|
||
break; | ||
} | ||
} | ||
|
||
return formatCliErrorFallback(error, stderr, commandDescription, commandArgs); | ||
} | ||
|
||
function formatCliErrorFallback( | ||
error: Error, | ||
stderr: string | undefined, | ||
commandDescription: string, | ||
commandArgs: string[], | ||
): CliError { | ||
if (stderr) { | ||
return new CliError( | ||
stderr, | ||
undefined, | ||
error, | ||
commandDescription, | ||
commandArgs, | ||
); | ||
} | ||
|
||
return new CliError( | ||
getErrorMessage(error), | ||
undefined, | ||
error, | ||
commandDescription, | ||
commandArgs, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
extensions/ql-vscode/test/unit-tests/codeql-cli/cli-errors.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { | ||
CliError, | ||
ExitCodeError, | ||
getCliError, | ||
} from "../../../src/codeql-cli/cli-errors"; | ||
import { EOL } from "os"; | ||
|
||
describe("getCliError", () => { | ||
it("returns an error with an unknown error", () => { | ||
const error = new Error("foo"); | ||
|
||
expect(getCliError(error, undefined, "bar", ["baz"])).toEqual( | ||
new CliError("foo", undefined, error, "bar", ["baz"]), | ||
); | ||
}); | ||
|
||
it("returns an error with an unknown error with stderr", () => { | ||
const error = new Error("foo"); | ||
|
||
expect(getCliError(error, "Something failed", "bar", ["baz"])).toEqual( | ||
new CliError("Something failed", "Something failed", error, "bar", [ | ||
"baz", | ||
]), | ||
); | ||
}); | ||
|
||
it("returns an error with an unknown error with stderr", () => { | ||
const error = new Error("foo"); | ||
|
||
expect(getCliError(error, "Something failed", "bar", ["baz"])).toEqual( | ||
new CliError("Something failed", "Something failed", error, "bar", [ | ||
"baz", | ||
]), | ||
); | ||
}); | ||
|
||
it("returns an error with an exit code error with unhandled exit code", () => { | ||
const error = new ExitCodeError(99); // OOM | ||
|
||
expect(getCliError(error, "OOM!", "bar", ["baz"])).toEqual( | ||
new CliError("OOM!", "OOM!", error, "bar", ["baz"]), | ||
); | ||
}); | ||
|
||
it("returns an error with an exit code error with handled exit code without string", () => { | ||
const error = new ExitCodeError(2); | ||
|
||
expect(getCliError(error, "Something happened!", "bar", ["baz"])).toEqual( | ||
new CliError("Something happened!", "Something happened!", error, "bar", [ | ||
"baz", | ||
]), | ||
); | ||
}); | ||
|
||
it("returns an error with a user code error with identifying string", () => { | ||
const error = new ExitCodeError(2); | ||
const stderr = `Something happened!${EOL}A fatal error occurred: The query did not run successfully.${EOL}The correct columns were not present.`; | ||
|
||
expect(getCliError(error, stderr, "bar", ["baz"])).toEqual( | ||
new CliError( | ||
`A fatal error occurred: The query did not run successfully.${EOL}The correct columns were not present.`, | ||
stderr, | ||
error, | ||
"bar", | ||
["baz"], | ||
), | ||
); | ||
}); | ||
|
||
it("returns an error with a user code error with cancelled string", () => { | ||
const error = new ExitCodeError(2); | ||
const stderr = `Running query...${EOL}Something is happening...${EOL}Computation was cancelled: Cancelled by user`; | ||
|
||
expect(getCliError(error, stderr, "bar", ["baz"])).toEqual( | ||
new CliError(stderr, stderr, error, "bar", ["baz"]), | ||
); | ||
}); | ||
|
||
it("returns an error with a cancelled error with identifying string", () => { | ||
const error = new ExitCodeError(98); | ||
const stderr = `Running query...${EOL}Something is happening...${EOL}Computation was cancelled: Cancelled by user`; | ||
|
||
expect(getCliError(error, stderr, "bar", ["baz"])).toEqual( | ||
new CliError( | ||
"Computation was cancelled: Cancelled by user", | ||
stderr, | ||
error, | ||
"bar", | ||
["baz"], | ||
), | ||
); | ||
}); | ||
}); |