Skip to content

Commit

Permalink
feat(runtime): print exit code info and signal on error (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4spar authored Jul 10, 2022
1 parent 2059567 commit 9cc7ec9
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 23 deletions.
59 changes: 59 additions & 0 deletions src/runtime/lib/exit_code_info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export function getExitCodeInfo(exitCode: number): string | undefined {
return {
2: "Misuse of shell builtins",
126: "Invoked command not executable",
127: "Command not found",
128: "Invalid exit argument",
// SIGHUP
129: "Hangup",
// SIGINT
130: "Interrupt (Ctrl + C)",
// SIGQUIT
131: "Quit and dump core",
// SIGILL
132: "Illegal instruction",
// SIGTRAP
133: "Trace/breakpoint trap",
// SIGABRT
134: "Process aborted",
// SIGEMT
135: 'Bus error: "access to undefined portion of memory object"',
// SIGFPE
136: 'Floating point exception: "erroneous arithmetic operation"',
// SIGKILL
137: "Kill (terminate immediately)",
// SIGBUS
138: "Bus error (bad memory access)",
// SIGSEGV
139: "Segmentation violation",
// SIGSYS
140: "Bad system call (SVr4)",
// SIGPIPE
141: "Write to pipe with no one reading",
// SIGALRM
142: "Signal raised by alarm",
// SIGTERM
143: "Termination (request to terminate)",
145: "Child process terminated, stopped (or continued*)",
146: "Continue if stopped",
147: "Stop executing temporarily",
148: "Terminal stop signal",
149: 'Background process attempting to read from tty ("in")',
150: 'Background process attempting to write to tty ("out")',
151: "Urgent data available on socket",
// SIGXCPU
152: "CPU time limit exceeded",
// SIGXFSZ
153: "File size limit exceeded",
// SIGVTALRM
154:
'Signal raised by timer counting virtual time: "virtual timer expired"',
// SIGPROF
155: "Profiling timer expired",
157: "Pollable event",
// SIGUSR1
158: "User-defined 1",
// SIGUSR2
159: "User-defined 2",
}[exitCode];
}
77 changes: 65 additions & 12 deletions src/runtime/process_error.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,71 @@
import { ProcessOutput, ProcessOutputOptions } from "./process_output.ts";
import { colors } from "./deps.ts";
import { getExitCodeInfo } from "./lib/exit_code_info.ts";
import { ProcessOutputOptions } from "./process_output.ts";

export class ProcessError extends Error implements ProcessOutput {
readonly stdout: string;
readonly stderr: string;
readonly combined: string;
readonly status: Deno.ProcessStatus;
export type ProcessErrorOptions = ProcessOutputOptions;

constructor({ stdout, stderr, combined, status }: ProcessOutputOptions) {
export class ProcessError extends Error {
#name = "ProcessError";
#stdout!: string;
#stderr!: string;
#combined!: string;
#status!: Deno.ProcessStatus;

constructor(options: ProcessErrorOptions) {
super();
Object.setPrototypeOf(this, ProcessError.prototype);
this.stdout = stdout;
this.stderr = stderr;
this.combined = combined;
this.status = status;
this.message = combined;
this.#stdout = options.stdout;
this.#stderr = options.stderr;
this.#combined = options.combined;
this.#status = options.status;
this.message = this.#getErrorMessage();
}

get name(): string {
return this.#name;
}

get stdout(): string {
return this.#stdout;
}

get stderr(): string {
return this.#stderr;
}

get combined(): string {
return this.#combined;
}

get status(): Deno.ProcessStatus {
return this.#status;
}

#getErrorMessage(): string {
let message = colors.bold("Command failed.");

if (this.#combined.trim()) {
message += "\n" + this.#combined.trim();
}

const exitCodeInfo = getExitCodeInfo(this.#status.code)
? ` (${colors.italic(getExitCodeInfo(this.#status.code)!)})`
: "";

message += colors.bold(
`\n${colors.white("Exit code:")} ${
colors.yellow(this.#status.code.toString())
}${exitCodeInfo}`,
);

if (typeof this.#status.signal === "number") {
message += colors.bold(
`\n${colors.white("Signal:")} ${
colors.yellow(this.#status.signal.toString())
}`,
);
}

return colors.red(message);
}
}
24 changes: 13 additions & 11 deletions src/runtime/process_error_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ function createError(): ProcessError {
stderr: "bar",
combined: "baz",
status: {
code: 0,
success: true,
code: 1,
success: false,
signal: undefined,
},
});
}
Expand All @@ -31,14 +32,13 @@ Deno.test({
name: "[process error] should have all properties defined",
fn() {
const error = createError();
assertObjectMatch(error, {
stdout: "foo",
stderr: "bar",
combined: "baz",
status: {
code: 0,
success: true,
},
assertEquals(error.stdout, "foo");
assertEquals(error.stderr, "bar");
assertEquals(error.combined, "baz");
assertObjectMatch(error.status, {
code: 1,
success: false,
signal: undefined,
});
},
});
Expand All @@ -60,7 +60,9 @@ Deno.test({
Deno.test({
name: "[process error] should have correct exit code",
async fn() {
const statusCode = await $`exit 2`.catch((error) => error.status.code);
const statusCode = await $`exit 2`.catch((error) =>
error instanceof ProcessError ? error.status.code : null
);
assertEquals(statusCode, 2);
},
});

0 comments on commit 9cc7ec9

Please sign in to comment.