From 5de305202656a172b187dac87543d6c1c69a2958 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 23 May 2024 12:55:53 +0100 Subject: [PATCH] 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. --- packages/phoenix/src/ansi-shell/ANSIShell.js | 8 ++++++++ .../phoenix/src/ansi-shell/pipeline/Pipeline.js | 17 +++++++++++------ .../providers/PuterAppCommandProvider.js | 9 ++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/phoenix/src/ansi-shell/ANSIShell.js b/packages/phoenix/src/ansi-shell/ANSIShell.js index 68f260a4..b1b4a1c7 100644 --- a/packages/phoenix/src/ansi-shell/ANSIShell.js +++ b/packages/phoenix/src/ansi-shell/ANSIShell.js @@ -160,6 +160,7 @@ export class ANSIShell extends EventTarget { } this.ctx.externs.out.write('error: ' + e.message + '\n'); console.log(e); + this.ctx.locals.exit = -1; return; } } @@ -225,6 +226,13 @@ export class ANSIShell extends EventTarget { const pipeline = await Pipeline.createFromAST(executionCtx, ast); 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) { diff --git a/packages/phoenix/src/ansi-shell/pipeline/Pipeline.js b/packages/phoenix/src/ansi-shell/pipeline/Pipeline.js index 186c8cab..590d96e4 100644 --- a/packages/phoenix/src/ansi-shell/pipeline/Pipeline.js +++ b/packages/phoenix/src/ansi-shell/pipeline/Pipeline.js @@ -247,14 +247,14 @@ export class PreparedCommand { if ( ! ctx.cmdExecState.valid ) { ctx.locals.exit = -1; await ctx.externs.out.close(); - return; + return 1; } if ( ctx.cmdExecState.printHelpAndExit ) { ctx.locals.exit = 0; await printUsage(command, ctx.externs.out, ctx.vars); await ctx.externs.out.close(); - return; + return 0; } let execute = command.execute.bind(command); @@ -291,13 +291,14 @@ export class PreparedCommand { command.name + ': ' + e.message + '\x1B[0m\n' ); + exit_code = -1; } else { await ctx.externs.err.write( '\x1B[31;1m' + command.name + ': ' + e.toString() + '\x1B[0m\n' ); - ctx.locals.exit = -1; + exit_code = -1; } if ( ! (e instanceof Exit) ) console.error(e); } @@ -316,6 +317,8 @@ export class PreparedCommand { 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); 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. for ( let i=0 ; i < preparedCommands.length ; i++ ) { @@ -381,9 +384,11 @@ export class Pipeline { const command = preparedCommands[i]; 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; valve.close(); } -} \ No newline at end of file +} diff --git a/packages/phoenix/src/puter-shell/providers/PuterAppCommandProvider.js b/packages/phoenix/src/puter-shell/providers/PuterAppCommandProvider.js index 2467d780..53875ea2 100644 --- a/packages/phoenix/src/puter-shell/providers/PuterAppCommandProvider.js +++ b/packages/phoenix/src/puter-shell/providers/PuterAppCommandProvider.js @@ -57,9 +57,12 @@ export class PuterAppCommandProvider { // Wait for app to close. const app_close_promise = new Promise((resolve, reject) => { - child.on('close', () => { - // TODO: Exit codes for apps - resolve({ done: true }); + child.on('close', (data) => { + if ((data.statusCode ?? 0) != 0) { + reject(new Exit(data.statusCode)); + } else { + resolve({ done: true }); + } }); });