diff --git a/lib/commands/stack/ssh.js b/lib/commands/stack/ssh.js index b3e7f97..6938b09 100644 --- a/lib/commands/stack/ssh.js +++ b/lib/commands/stack/ssh.js @@ -10,7 +10,7 @@ const IDLE_INTERVAL = 30_000; const INITIAL_SIZE_WAIT = 700; -function connect( data ) { +function connect( data, porcelain = false ) { let idleTimer; let lastWasNewline = true; let startedSshControl = false; @@ -85,7 +85,6 @@ function connect( data ) { msg += char; } - session.write( msg ); } ); }, 1000 ); @@ -109,11 +108,28 @@ function connect( data ) { } const message = reason ? `${ reason }. Disconnected.` : 'Disconnected.'; - process.stderr.write( message ); + if ( ! porcelain ) { + process.stdout.write( message ); + } session.close(); process.exit(); } ); + + let firstOutput = true; session.on( 'output', ( data ) => { + + // If this is the first output chunk and we're in porcelain mode, strip the initial 2 lines + // this contain somethign like: + // exec sudo sandbox-exec 'clear; $command' + // sh-4.2$ exec sudo sandbox-exec 'clear; $command' + if ( porcelain && firstOutput ) { + data = data.split( '\n' ).slice(2).join('\n').trim(); + } + firstOutput = false; + // Strip the ANSI escape codes if we're in porcelain mode. + if ( porcelain ) { + data = data.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); + } process.stdout.write( data ); } ); } @@ -123,39 +139,62 @@ const handler = function ( argv ) { getStack( argv ).then( stack => { const v = new Vantage( config ); - const status = new ora( `Starting session on ${stack}` ); - status.start(); + + if ( ! argv.command ) { + status.start(); + } const url = `stack/applications/${stack}/cli/sessions`; - v.fetch( url, { method: 'POST' } ).then( resp => { + const args = { + motd: ! argv.command, + }; + + if ( argv.command ) { + // Set the SHELL_PIPE=1 env var when running the command if the current process is piped. + args.command = `${ process.stdout.isTTY ? '' : 'SHELL_PIPE=1 ' }${ argv.command }`; + } + + const options = { + method: 'POST', + body: JSON.stringify( args ), + headers: { + 'Content-Type': 'application/json', + }, + }; + + v.fetch( url, options ).then( resp => { // hm-stack currently returns a 500 for invalid stacks, so we have to // assume any failure is a 404: https://github.com/humanmade/hm-stack/issues/367 if ( ! resp.ok ) { status.fail( `Invalid stack ${stack}` ); return; } - - return resp.json().then( data => { + return resp.text().then( text => { + const data = JSON.parse( text ); status.stop(); - connect( data ); - }); + connect( data, !! argv.command ); + } ); }).catch( e => { - status.fail( `Could not fetch details for ${stack}` ); + status.fail( `Could create session ${stack} (${e.message})` ); throw e; }); } ); }; module.exports = { - command: 'ssh [stack]', - description: 'SSH into a stack.', - builder: subcommand => { - subcommand.option( 'app-server', { - description: 'Use an app server instead of the sandbox.', - default: false, - type: 'boolean', - } ); - }, + command: 'ssh [stack]', + description: 'SSH into a stack.', + builder: subcommand => { + subcommand.option( 'app-server', { + description: 'Use an app server instead of the sandbox.', + default: false, + type: 'boolean', + } ); + subcommand.option( 'command', { + description: 'Run a given command and exit.', + type: 'string', + } ); + }, handler, };