UNPKG

5.92 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright © 2018 Atomist, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19 return new (P || (P = Promise))(function (resolve, reject) {
20 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
23 step((generator = generator.apply(thisArg, _arguments || [])).next());
24 });
25};
26Object.defineProperty(exports, "__esModule", { value: true });
27const spawn = require("cross-spawn");
28const path = require("path");
29const sprintf_js_1 = require("sprintf-js");
30const strip_ansi = require("strip-ansi");
31const logger_1 = require("../internal/util/logger");
32/**
33 * Default ErrorFinder that regards return code 0 as success
34 * @param {number} code
35 * @return {boolean}
36 * @constructor
37 */
38exports.SuccessIsReturn0ErrorFinder = code => code !== 0;
39/**
40 * Spawn a process and watch
41 * @param {SpawnCommand} spawnCommand
42 * @param options options
43 * @param {ProgressLog} log
44 * @param {Partial<SpawnWatchOptions>} spOpts
45 * @return {Promise<ChildProcessResult>}
46 */
47function spawnAndWatch(spawnCommand, options, log, spOpts = {}) {
48 return __awaiter(this, void 0, void 0, function* () {
49 const childProcess = spawn(spawnCommand.command, spawnCommand.args || [], options);
50 if (spOpts.logCommand === false) {
51 logger_1.logger.debug(`${options.cwd || path.resolve(".")} > ${stringifySpawnCommand(spawnCommand)} (pid '${childProcess.pid}')`);
52 }
53 else {
54 log.write(`---\n`);
55 log.write(`${options.cwd || path.resolve(".")} > ${stringifySpawnCommand(spawnCommand)} (pid '${childProcess.pid}')\n`);
56 log.write(`---\n`);
57 }
58 return watchSpawned(childProcess, log, spOpts);
59 });
60}
61exports.spawnAndWatch = spawnAndWatch;
62/**
63 * Handle the result of a spawned process, streaming back
64 * output to log
65 * @param childProcess
66 * @param {ProgressLog} log to write stdout and stderr to
67 * @param opts: Options for error parsing, ANSI code stripping etc.
68 * @return {Promise<ChildProcessResult>}
69 */
70function watchSpawned(childProcess, log, opts = {}) {
71 let timer;
72 let running = true;
73 if (opts.timeout) {
74 timer = setTimeout(() => {
75 if (running) {
76 logger_1.logger.warn("Spawn timeout expired. Killing command with pid '%s'", childProcess.pid);
77 childProcess.kill();
78 }
79 }, opts.timeout);
80 }
81 return new Promise((resolve, reject) => {
82 const optsToUse = Object.assign({ errorFinder: exports.SuccessIsReturn0ErrorFinder, stripAnsi: false }, opts);
83 if (!optsToUse.errorFinder) {
84 // The caller specified undefined, which is an error. Ignore them, for they know not what they do.
85 optsToUse.errorFinder = exports.SuccessIsReturn0ErrorFinder;
86 }
87 function sendToLog(data) {
88 const formatted = optsToUse.stripAnsi ? strip_ansi(data.toString()) : data.toString();
89 return log.write(formatted);
90 }
91 childProcess.stdout.on("data", sendToLog);
92 childProcess.stderr.on("data", sendToLog);
93 childProcess.addListener("exit", (code, signal) => {
94 running = false;
95 logger_1.logger.info("Spawn exit with pid '%d': code '%d', signal '%d'", childProcess.pid, code, signal);
96 clearTimeout(timer);
97 resolve({
98 error: optsToUse.errorFinder(code, signal, log),
99 code,
100 childProcess,
101 });
102 });
103 childProcess.addListener("error", err => {
104 running = false;
105 // Process could not be spawned or killed
106 logger_1.logger.warn("Spawn failure: %s", err);
107 clearTimeout(timer);
108 reject(err);
109 });
110 });
111}
112/**
113 * toString for a SpawnCommand. Used for logging.
114 * @param {SpawnCommand} sc
115 * @return {string}
116 */
117function stringifySpawnCommand(sc) {
118 return sprintf_js_1.sprintf("%s %s", sc.command, !!sc.args ? sc.args.join(" ") : "");
119}
120exports.stringifySpawnCommand = stringifySpawnCommand;
121/**
122 * Convenient function to create a spawn command from a sentence such as "npm run compile"
123 * Does not respect quoted arguments
124 * @param {string} sentence
125 * @param options
126 * @return {SpawnCommand}
127 */
128function asSpawnCommand(sentence, options = {}) {
129 const split = sentence.split(" ");
130 return {
131 command: split[0],
132 args: split.slice(1),
133 options,
134 };
135}
136exports.asSpawnCommand = asSpawnCommand;
137/**
138 * Kill the child process and wait for it to shut down. This can take a while as child processes
139 * may have shut down hooks.
140 * @param {module:child_process.ChildProcess} childProcess
141 * @return {Promise<any>}
142 */
143function poisonAndWait(childProcess) {
144 childProcess.kill();
145 return new Promise((resolve, reject) => childProcess.on("close", () => {
146 resolve();
147 }));
148}
149exports.poisonAndWait = poisonAndWait;
150//# sourceMappingURL=spawned.js.map
\No newline at end of file