@intuit/judo
Version:
Test command line interfaces.
131 lines (105 loc) • 6.53 kB
JavaScript
;Object.defineProperty(exports, "__esModule", { value: true });exports.execute = exports.executePrerequisites = void 0;
var _logger = require("./logger");
var _child_process = require("child_process");
var _path = _interopRequireDefault(require("path"));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function ownKeys(object, enumerableOnly) {var keys = Object.keys(object);if (Object.getOwnPropertySymbols) {var symbols = Object.getOwnPropertySymbols(object);if (enumerableOnly) symbols = symbols.filter(function (sym) {return Object.getOwnPropertyDescriptor(object, sym).enumerable;});keys.push.apply(keys, symbols);}return keys;}function _objectSpread(target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i] != null ? arguments[i] : {};if (i % 2) {ownKeys(Object(source), true).forEach(function (key) {_defineProperty(target, key, source[key]);});} else if (Object.getOwnPropertyDescriptors) {Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));} else {ownKeys(Object(source)).forEach(function (key) {Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));});}}return target;}function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {try {var info = gen[key](arg);var value = info.value;} catch (error) {reject(error);return;}if (info.done) {resolve(value);} else {Promise.resolve(value).then(_next, _throw);}}function _asyncToGenerator(fn) {return function () {var self = this,args = arguments;return new Promise(function (resolve, reject) {var gen = fn.apply(self, args);function _next(value) {asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);}function _throw(err) {asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);}_next(undefined);});};}
/**
* Spawns a new process to execute all of the prerequisite commands provided.
* @param {Object} params
* @param {Array.<String>} params.prerequisites strings of commands to run
* @param {String} params.cwd directory to run the prereqs process in
*/
const executePrerequisites = ({ prerequisites, cwd }) => {
return new Promise((resolve, reject) => {
_logger.logger.info(`Running prerequisite commands in: ${_path.default.resolve(cwd)}`);
const commandsChained = prerequisites.join(' && ');
const child = (0, _child_process.exec)(commandsChained, { cwd });
child.stdout.on('data', data => {
_logger.logger.logOutput(data.toString());
});
child.stderr.on('data', data => {
_logger.logger.logOutput(data.toString());
});
child.on('close', code => {
_logger.logger.info('Prerequisites complete');
if (code !== 0) {
return reject(
new Error(`Failed to run prerequisites, exit code: ${code}`));
}
return resolve();
});
});
};
// timeout to cancel execution if no command output is found within time limit
exports.executePrerequisites = executePrerequisites;const startNoOutputTimeout = (timeout, commandStr, reject) =>
setTimeout(
() => reject(new Error(`Timeout of ${timeout}ms reached without any command output when running: "${commandStr}"`)),
timeout);
/**
* Spawns a new process to execute a command. Responds to any "when" responses specified in the opts by
* sending the response to the new process' stdin. If every "when" is not found, rejects an error, otherwise
* resolves.
* @param {String} command command to be run
* @param {Array.<String>} args args for the command
* @param {Object} opts node-pty spawn options, as well as "when" assertions and "timeout"
*/
const execute = (command, args = [], opts = {}) => {
return new Promise( /*#__PURE__*/function () {var _ref = _asyncToGenerator(function* (resolve, reject) {
// argString?
const commandArgs = opts.argsString || args.join(' ');
const commandStr = `${command} ${commandArgs}`;
const cwdStr = opts.cwd ? ` in ${_path.default.resolve(opts.cwd)}` : '';
const child = (0, _child_process.spawn)(command, args, _objectSpread(_objectSpread({},
opts), {}, {
stdio: ['pipe', 'pipe', 'pipe'] }));
_logger.logger.info(`Executing: ${commandStr}${cwdStr}`);
let timeoutControl = startNoOutputTimeout(opts.timeout, commandStr, reject);
let output = '';
let childInputWrite;
const processOutput = data => {
clearTimeout(timeoutControl);
timeoutControl = startNoOutputTimeout(opts.timeout, commandStr, reject);
output += data;
if (opts.when) {
opts.when.forEach(whenStep => {
if (
!childInputWrite &&
data.indexOf(whenStep.output) !== -1 &&
!whenStep.done)
{
whenStep.done = true;
// store response to send back to child stdin
childInputWrite = whenStep.response + '\r';
}
});
}
};
child.stdout.on('data', data => {
const childData = data.toString();
_logger.logger.logStdout(childData);
processOutput(childData);
});
child.stderr.on('data', data => {
const childData = data.toString();
_logger.logger.logStderr(childData);
processOutput(childData);
});
// write inputs to child process on an interval, avoids immediate execution race condition
const interval = setInterval(() => {
if (childInputWrite) {
_logger.logger.info(`responding with: ${childInputWrite}`);
child.stdin.write(childInputWrite);
childInputWrite = '';
}
}, 500);
// listen for end of process
child.on('exit', code => {
const numWhenAssertionsComplete = opts.when.filter(w => w.done).length;
clearInterval(interval);
_logger.logger.info(`exited: ${code}`);
if (opts.when && numWhenAssertionsComplete < opts.when.length) {
return reject(new Error(`Expected ${opts.when.length} different strings in output from run step "when", encountered ${numWhenAssertionsComplete} expected outputs`));
}
return resolve({ output, code });
});
});return function (_x, _x2) {return _ref.apply(this, arguments);};}());
};exports.execute = execute;