const { spawn, spawnSync } = require('child_process'); const fs = require('fs') function nodeShellExec() { var args = Array.from(arguments); if(args.length > 1){ var opts = args[2] = args[2] || {} opts.title ? null : opts.title = `${args[0]} ${args[1] }` } else { var opts = { title : `${args[0]}` // , inherit: true, shell: true } } opts.stdin = 'pipe'; // // const spawn = require('child_process').spawn; // const s = spawn( // 'C:\\Program Files\\Git\\bin\\sh.exe' // , ['notepad', 'index'], { cwd: __dirname }); // var interval = null; // var t = setTimeout(function(){ // interval = setInterval(function(){ // console.log('Awaiting close : ' + child.spawnargs) // }, 1500) // // console.log('Awaiting close : ' + child.spawnargs) // }, 0) // child.on('close', (code) => { // console.error('Prematurely closed even before promise...') // }) // D:\chess\instances\elixir_01\elxr/.elxr/run-1630002739610/download.bat https://github.com/git-for-windows/git/releases/download/v2.33.0.windows.2/Git-2.33.0.2-64-bit.exe D:\\chess\\instances\\elixir_01\\elxr/Downloads/Git-2.33.0.2-64-bit.exe // D:\\chess\\instances\\elixir_01\\elxr/.elxr/run-1630002923584/download.bat https://github.com/git-for-windows/git/releases/download/v2.33.0.windows.2/Git-2.33.0.2-64-bit.exe D:\\chess\\instances\\elixir_01\\elxr/Downloads/Git-2.33.0.2-64-bit.exe // console.dir(args[2]) console.log('nodeshellexec ::: ') console.log(`${args[0]} ${args[1].join(' ') }`) const child = spawn(...args); var p = new Promise(function(resolve, reject){ // console.log(...args) if(!opts.detached) { var messages = []; // PB : TODO -- Explore stream for Task level aggregation to prevent interleaved messages from multiple tasks... var success = true; if(opts.stdio !== 'ignore') { child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8'); child.stdout.on('data', (chunk) => { chunk.trim() === '' ? null : messages.push(chunk); /* console.log('d: ' + chunk) */ process.stdout.write( chunk ) }); child.on('error', (chunk) => { success = false; messages.push(chunk); /* console.error('e: ' + chunk) */ console.log('Error exit not handled.') } ); child.stderr.on('data', (chunk) => { if(messages.join('').indexOf('fatal: not a git repository') > -1) opts.haserrors = true; messages.push(chunk); // process.stdout.write( chunk ) // console.error('stderr e: ' + chunk) }); } child.on('close', (code) => { // console.log('Proper close was fired') var logEntry = { code, success } if(+code !== 0 || opts.haserrors) { success = false; logEntry = { messages, result: `${opts.title} exited with code ${code}`, success, code } }; if(opts.stdio !== 'ignore') { logEntry = { result: `${opts.title} exited with code ${code}`, messages, code } logEntry.success = success; if(opts.evaluateResult) logEntry = opts.evaluateResult(success, logEntry); if(opts.runas){ // success ? logEntry.success = true : null; fs.writeFileSync('run.log', ', ' + JSON.stringify(logEntry), {'flag':'a+'} ) } else { // console.log( messages.join('') ) // process.stdout.write( JSON.stringify(logEntry) ) } } else if(opts.evaluateResult) { try { logEntry = opts.evaluateResult(false, logEntry); } catch(e){ reject(e) } } // clearInterval(interval) if(code !== 0 || opts.haserrors) { args[2].benign || args[2].ignorefailures ? (logEntry.benign = true, logEntry.ignorefailures = true) : null return reject(logEntry) } resolve(logEntry) }); } else { child.unref() // clearInterval(interval) resolve(true); } }); p.process = child; return p; } var prompt = function(choices, label, defaultchoice, selectedchoice){ // prompt accepts either an array or an object as choices. var choices = choices || []; defaultchoice = defaultchoice || choices[0] || selectedchoice || choices[Object.keys(choices)[0]] var ci = 0; return this.prompter.ask( `${label} \n` + Object.keys(choices).map( choice => { ++ci; var choice_label = isNaN(+choice) ? `${ci}) (${choice})` : ci + ')'; return ` ${choice_label} ${choices[choice]} ` }).join('\n') + `\n d) default ( <= ${ defaultchoice || choices[0]} ) : ` + `\n selected ( <= ${ selectedchoice || defaultchoice || choices[0]} ) : ` ).then(choice => { // propName = promptable.interpret(propValue) if(!choice && selectedchoice) return selectedchoice; if(!choice) return defaultchoice || choices[0]; if(choice && isNaN(+choice)) { if(choice === 'd') return defaultchoice || choices[0]; try { return JSON.parse(choice); } catch(e) { // console.error(e) return choice // Treat it as a string if it is not parsable } } return choices[(+choice) - 1]; }) } const readline = require("readline"); var cli = { nodeShellExec , get prompter() { var prompt_interface = { ask : function(q){ // Needs to be serialized. Parallel asks are not possible. const clii = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve, reject)=>{ clii.question(q, (answer)=>{ console.log(answer) try { clii.close(); console.log("readline.createInterface closed"); resolve(answer) } catch(e) { reject(e) } }) }) } } return prompt_interface } , prompt , createTask(task, str){ var tasks = { getTaskCheckExists : (str)=>{ return (command, options) => { options = options || {} return () => { var p = nodeShellExec.apply(null, [str, [command]]) if (options.ignorefailures) { return p.then(() => { return true }).catch(e => { // Ignore. Not a major error. return false; }) } else return p.then(() => { return true }); } } } } return tasks[task](str) } } module.exports = cli