UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

130 lines 6.49 kB
"use strict"; /* * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.killAndWait = exports.spawnLog = exports.SuccessIsReturn0ErrorFinder = exports.spawnPromise = exports.spawn = exports.killProcess = exports.ExecPromiseError = exports.execPromise = void 0; const child_process_1 = require("@atomist/automation-client/lib/util/child_process"); Object.defineProperty(exports, "execPromise", { enumerable: true, get: function () { return child_process_1.execPromise; } }); Object.defineProperty(exports, "ExecPromiseError", { enumerable: true, get: function () { return child_process_1.ExecPromiseError; } }); Object.defineProperty(exports, "killProcess", { enumerable: true, get: function () { return child_process_1.killProcess; } }); Object.defineProperty(exports, "spawn", { enumerable: true, get: function () { return child_process_1.spawn; } }); Object.defineProperty(exports, "spawnPromise", { enumerable: true, get: function () { return child_process_1.spawnPromise; } }); const logger_1 = require("@atomist/automation-client/lib/util/logger"); const os = require("os"); const sdmGoal_1 = require("../goal/sdmGoal"); const DelimitedWriteProgressLogDecorator_1 = require("../log/DelimitedWriteProgressLogDecorator"); /** * Default ErrorFinder that regards everything but a return code of 0 * as failure. * * @param code process exit status * @return true if exit status is not zero */ const SuccessIsReturn0ErrorFinder = code => code !== 0; exports.SuccessIsReturn0ErrorFinder = SuccessIsReturn0ErrorFinder; /** * Spawn a process, logging its standard output and standard error, * and return a Promise of its results. The command is spawned using * cross-spawn. A DelimitedWriteProgressLogDecorator, using newlines * as delimiters, is created from the provided `opts.log`. The default * command timeout is 10 minutes. The default * [[SpawnLogOptions#errorFinder]] sets the `error` property if the * command exits with a non-zero status or is killed by a signal. If * the process is killed due to a signal or the `errorFinder` returns * `true`, the returned `code` property will be non-zero. * * @param cmd Command to run. * @param args Arguments to command. * @param opts Options for spawn, spawnPromise, and spawnLog. * @return A promise that provides information on the child process and * its execution result, including if the exit status was non-zero * or the process was killed by a signal. The promise is only * rejected with an `ExecPromiseError` if there is an error * spawning the process. */ async function spawnLog(cmd, args, opts) { opts.errorFinder = opts.errorFinder ? opts.errorFinder : exports.SuccessIsReturn0ErrorFinder; opts.log = new DelimitedWriteProgressLogDecorator_1.DelimitedWriteProgressLogDecorator(opts.log, "\n"); opts.timeout = opts.timeout ? opts.timeout : sdmGoal_1.sdmGoalTimeout(); const spResult = await child_process_1.spawnPromise(cmd, args, opts); const slResult = Object.assign(Object.assign({}, spResult), { code: spResult.signal ? 128 + 15 : spResult.status }); if (slResult.error) { throw child_process_1.ExecPromiseError.fromSpawnReturns(slResult); } else if (opts.errorFinder(slResult.code, slResult.signal, opts.log)) { slResult.code = slResult.code ? slResult.code : 99; slResult.error = new Error(`Error finder found error in results from ${slResult.cmdString}`); } return slResult; } exports.spawnLog = spawnLog; /** * Clear provided timers, checking to make sure the timers are defined * before clearing them. * * @param timers the timers to clear. */ function clearTimers(...timers) { timers.filter(t => !!t).map(clearTimeout); } /** * Kill the process and wait for it to shut down. This can take a * while as processes may have shut down hooks. On win32, the process * is killed and the Promise is rejected if the process does not exit * within `wait` milliseconds. On other platforms, first the process * is sent the default signal, SIGTERM. After `wait` milliseconds, it * is sent SIGKILL. After another `wait` milliseconds, an error is * thrown. * * @param childProcess Child process to kill * @param wait Number of milliseconds to wait before sending SIGKILL and * then erroring, default is 30000 ms */ async function killAndWait(childProcess, wait = 30000) { return new Promise((resolve, reject) => { const pid = childProcess.pid; let killTimer; const termTimer = setTimeout(() => { if (os.platform() === "win32") { reject(new Error(`Failed to kill child process ${pid} in ${wait} ms`)); } else { logger_1.logger.debug(`Child process ${pid} did not exit in ${wait} ms, sending SIGKILL`); child_process_1.killProcess(pid, "SIGKILL"); killTimer = setTimeout(() => { reject(new Error(`Failed to detect child process ${pid} exit after sending SIGKILL`)); }, wait); } }, wait); childProcess.on("close", (code, signal) => { clearTimers(termTimer, killTimer); logger_1.logger.debug(`Child process ${pid} closed with code '${code}' and signal '${signal}'`); resolve(); }); childProcess.on("exit", (code, signal) => { logger_1.logger.debug(`Child process ${pid} exited with code '${code}' and signal '${signal}'`); }); childProcess.on("error", err => { clearTimers(termTimer, killTimer); err.message = `Child process ${pid} errored: ${err.message}`; logger_1.logger.error(err.message); reject(err); }); child_process_1.killProcess(pid); }); } exports.killAndWait = killAndWait; //# sourceMappingURL=child_process.js.map