feat(phoenix): Respond to exit status codes

- Detect exit status of Puter apps, now that that's available.
- Store the exit status of each pipeline.
- Display a message when the exit status was non-zero.

That message is temporary, until we have a better way of displaying or
querying it, such as the `$?` shell variable.
This commit is contained in:
Sam Atkins 2024-05-23 12:55:53 +01:00
parent 7674da4cd2
commit 5de3052026
3 changed files with 25 additions and 9 deletions

View File

@ -160,6 +160,7 @@ export class ANSIShell extends EventTarget {
} }
this.ctx.externs.out.write('error: ' + e.message + '\n'); this.ctx.externs.out.write('error: ' + e.message + '\n');
console.log(e); console.log(e);
this.ctx.locals.exit = -1;
return; return;
} }
} }
@ -225,6 +226,13 @@ export class ANSIShell extends EventTarget {
const pipeline = await Pipeline.createFromAST(executionCtx, ast); const pipeline = await Pipeline.createFromAST(executionCtx, ast);
await pipeline.execute(executionCtx); await pipeline.execute(executionCtx);
// Store exit code for the next pipeline
// TODO: This feels like a hacky way of doing this.
this.ctx.locals.exit = executionCtx.locals.exit;
if ( this.ctx.locals.exit ) {
this.ctx.externs.out.write(`Exited with code ${this.ctx.locals.exit}\n`);
}
} }
expandPromptString (str) { expandPromptString (str) {

View File

@ -247,14 +247,14 @@ export class PreparedCommand {
if ( ! ctx.cmdExecState.valid ) { if ( ! ctx.cmdExecState.valid ) {
ctx.locals.exit = -1; ctx.locals.exit = -1;
await ctx.externs.out.close(); await ctx.externs.out.close();
return; return 1;
} }
if ( ctx.cmdExecState.printHelpAndExit ) { if ( ctx.cmdExecState.printHelpAndExit ) {
ctx.locals.exit = 0; ctx.locals.exit = 0;
await printUsage(command, ctx.externs.out, ctx.vars); await printUsage(command, ctx.externs.out, ctx.vars);
await ctx.externs.out.close(); await ctx.externs.out.close();
return; return 0;
} }
let execute = command.execute.bind(command); let execute = command.execute.bind(command);
@ -291,13 +291,14 @@ export class PreparedCommand {
command.name + ': ' + command.name + ': ' +
e.message + '\x1B[0m\n' e.message + '\x1B[0m\n'
); );
exit_code = -1;
} else { } else {
await ctx.externs.err.write( await ctx.externs.err.write(
'\x1B[31;1m' + '\x1B[31;1m' +
command.name + ': ' + command.name + ': ' +
e.toString() + '\x1B[0m\n' e.toString() + '\x1B[0m\n'
); );
ctx.locals.exit = -1; exit_code = -1;
} }
if ( ! (e instanceof Exit) ) console.error(e); if ( ! (e instanceof Exit) ) console.error(e);
} }
@ -316,6 +317,8 @@ export class PreparedCommand {
await filesystem.write(path, outputMemWriters[i].getAsBlob()); await filesystem.write(path, outputMemWriters[i].getAsBlob());
} }
return exit_code;
} }
} }
@ -348,7 +351,7 @@ export class Pipeline {
const valve = new Coupler(nextIn, pipeline_input_pipe.in); const valve = new Coupler(nextIn, pipeline_input_pipe.in);
nextIn = pipeline_input_pipe.out; nextIn = pipeline_input_pipe.out;
// TOOD: this will eventually defer piping of certain // TODO: this will eventually defer piping of certain
// sub-pipelines to the Puter Shell. // sub-pipelines to the Puter Shell.
for ( let i=0 ; i < preparedCommands.length ; i++ ) { for ( let i=0 ; i < preparedCommands.length ; i++ ) {
@ -381,9 +384,11 @@ export class Pipeline {
const command = preparedCommands[i]; const command = preparedCommands[i];
commandPromises.push(command.execute()); commandPromises.push(command.execute());
} }
await Promise.all(commandPromises); const results = await Promise.all(commandPromises);
// TODO: Consider what to do about intermediate exit codes
ctx.locals.exit = results[results.length-1];
await coupler.isDone; await coupler.isDone;
valve.close(); valve.close();
} }
} }

View File

@ -57,9 +57,12 @@ export class PuterAppCommandProvider {
// Wait for app to close. // Wait for app to close.
const app_close_promise = new Promise((resolve, reject) => { const app_close_promise = new Promise((resolve, reject) => {
child.on('close', () => { child.on('close', (data) => {
// TODO: Exit codes for apps if ((data.statusCode ?? 0) != 0) {
resolve({ done: true }); reject(new Exit(data.statusCode));
} else {
resolve({ done: true });
}
}); });
}); });