// // Thin promise wrappers around Nova's Process API. // // Runs a command to completion and resolves with its captured output. // Rejects with an Error carrying the exit status and stderr when the // command fails, so callers can surface a useful message. function exec(path, args, options = {}) { return new Promise((resolve, reject) => { let stdout = ""; let stderr = ""; let process; try { process = new Process(path, { args: args, cwd: options.cwd, env: options.env, stdio: ["ignore", "pipe", "pipe"] }); } catch (error) { reject(error); return; } process.onStdout((line) => { stdout += line; }); process.onStderr((line) => { stderr += line; }); process.onDidExit((status) => { if (status === 0 || options.ignoreStatus) { resolve({ status: status, stdout: stdout, stderr: stderr }); return; } let error = new Error(cleanMessage(stderr) || `${path} exited with status ${status}.`); error.status = status; error.stdout = stdout; error.stderr = stderr; reject(error); }); try { process.start(); } catch (error) { reject(error); } }); } // Runs a command and parses its stdout as JSON. async function execJSON(path, args, options = {}) { let result = await exec(path, args, options); if (!result.stdout.trim()) { return []; } try { return JSON.parse(result.stdout); } catch (error) { throw new Error(`Unable to parse output of ${path}: ${error.message}`); } } // Apple Container prefixes failures with "Error: " and appends a hint about // starting the system services. Keep the first meaningful line only. function cleanMessage(stderr) { if (!stderr) { return ""; } let line = stderr .split("\n") .map((entry) => entry.trim()) .filter((entry) => entry.length > 0)[0]; if (!line) { return ""; } return line.replace(/^Error:\s*/, ""); } module.exports = { exec, execJSON, cleanMessage };