diff --git a/.gitignore b/.gitignore index ea9e4b1f..aad71333 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ node_modules .nyc_output .idea .vscode -coverage diff --git a/.taprc b/.taprc new file mode 100644 index 00000000..d020e617 --- /dev/null +++ b/.taprc @@ -0,0 +1,4 @@ +statements: 90 +lines: 90 +functions: 78 +branches: 90 diff --git a/dist/index.js b/dist/index.js index e9b762bc..7d67f283 100644 --- a/dist/index.js +++ b/dist/index.js @@ -994,741 +994,6 @@ exports.toCommandProperties = toCommandProperties; /***/ }), -/***/ 1514: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getExecOutput = exports.exec = void 0; -const string_decoder_1 = __nccwpck_require__(1576); -const tr = __importStar(__nccwpck_require__(8159)); -/** - * Exec a command. - * Output will be streamed to the live console. - * Returns promise with return code - * - * @param commandLine command to execute (can include additional args). Must be correctly escaped. - * @param args optional arguments for tool. Escaping is handled by the lib. - * @param options optional exec options. See ExecOptions - * @returns Promise exit code - */ -function exec(commandLine, args, options) { - return __awaiter(this, void 0, void 0, function* () { - const commandArgs = tr.argStringToArray(commandLine); - if (commandArgs.length === 0) { - throw new Error(`Parameter 'commandLine' cannot be null or empty.`); - } - // Path to tool to execute should be first arg - const toolPath = commandArgs[0]; - args = commandArgs.slice(1).concat(args || []); - const runner = new tr.ToolRunner(toolPath, args, options); - return runner.exec(); - }); -} -exports.exec = exec; -/** - * Exec a command and get the output. - * Output will be streamed to the live console. - * Returns promise with the exit code and collected stdout and stderr - * - * @param commandLine command to execute (can include additional args). Must be correctly escaped. - * @param args optional arguments for tool. Escaping is handled by the lib. - * @param options optional exec options. See ExecOptions - * @returns Promise exit code, stdout, and stderr - */ -function getExecOutput(commandLine, args, options) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - let stdout = ''; - let stderr = ''; - //Using string decoder covers the case where a mult-byte character is split - const stdoutDecoder = new string_decoder_1.StringDecoder('utf8'); - const stderrDecoder = new string_decoder_1.StringDecoder('utf8'); - const originalStdoutListener = (_a = options === null || options === void 0 ? void 0 : options.listeners) === null || _a === void 0 ? void 0 : _a.stdout; - const originalStdErrListener = (_b = options === null || options === void 0 ? void 0 : options.listeners) === null || _b === void 0 ? void 0 : _b.stderr; - const stdErrListener = (data) => { - stderr += stderrDecoder.write(data); - if (originalStdErrListener) { - originalStdErrListener(data); - } - }; - const stdOutListener = (data) => { - stdout += stdoutDecoder.write(data); - if (originalStdoutListener) { - originalStdoutListener(data); - } - }; - const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); - const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); - //flush any remaining characters - stdout += stdoutDecoder.end(); - stderr += stderrDecoder.end(); - return { - exitCode, - stdout, - stderr - }; - }); -} -exports.getExecOutput = getExecOutput; -//# sourceMappingURL=exec.js.map - -/***/ }), - -/***/ 8159: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.argStringToArray = exports.ToolRunner = void 0; -const os = __importStar(__nccwpck_require__(2037)); -const events = __importStar(__nccwpck_require__(2361)); -const child = __importStar(__nccwpck_require__(2081)); -const path = __importStar(__nccwpck_require__(1017)); -const io = __importStar(__nccwpck_require__(7436)); -const ioUtil = __importStar(__nccwpck_require__(1962)); -const timers_1 = __nccwpck_require__(9512); -/* eslint-disable @typescript-eslint/unbound-method */ -const IS_WINDOWS = process.platform === 'win32'; -/* - * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. - */ -class ToolRunner extends events.EventEmitter { - constructor(toolPath, args, options) { - super(); - if (!toolPath) { - throw new Error("Parameter 'toolPath' cannot be null or empty."); - } - this.toolPath = toolPath; - this.args = args || []; - this.options = options || {}; - } - _debug(message) { - if (this.options.listeners && this.options.listeners.debug) { - this.options.listeners.debug(message); - } - } - _getCommandString(options, noPrefix) { - const toolPath = this._getSpawnFileName(); - const args = this._getSpawnArgs(options); - let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool - if (IS_WINDOWS) { - // Windows + cmd file - if (this._isCmdFile()) { - cmd += toolPath; - for (const a of args) { - cmd += ` ${a}`; - } - } - // Windows + verbatim - else if (options.windowsVerbatimArguments) { - cmd += `"${toolPath}"`; - for (const a of args) { - cmd += ` ${a}`; - } - } - // Windows (regular) - else { - cmd += this._windowsQuoteCmdArg(toolPath); - for (const a of args) { - cmd += ` ${this._windowsQuoteCmdArg(a)}`; - } - } - } - else { - // OSX/Linux - this can likely be improved with some form of quoting. - // creating processes on Unix is fundamentally different than Windows. - // on Unix, execvp() takes an arg array. - cmd += toolPath; - for (const a of args) { - cmd += ` ${a}`; - } - } - return cmd; - } - _processLineBuffer(data, strBuffer, onLine) { - try { - let s = strBuffer + data.toString(); - let n = s.indexOf(os.EOL); - while (n > -1) { - const line = s.substring(0, n); - onLine(line); - // the rest of the string ... - s = s.substring(n + os.EOL.length); - n = s.indexOf(os.EOL); - } - return s; - } - catch (err) { - // streaming lines to console is best effort. Don't fail a build. - this._debug(`error processing line. Failed with error ${err}`); - return ''; - } - } - _getSpawnFileName() { - if (IS_WINDOWS) { - if (this._isCmdFile()) { - return process.env['COMSPEC'] || 'cmd.exe'; - } - } - return this.toolPath; - } - _getSpawnArgs(options) { - if (IS_WINDOWS) { - if (this._isCmdFile()) { - let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; - for (const a of this.args) { - argline += ' '; - argline += options.windowsVerbatimArguments - ? a - : this._windowsQuoteCmdArg(a); - } - argline += '"'; - return [argline]; - } - } - return this.args; - } - _endsWith(str, end) { - return str.endsWith(end); - } - _isCmdFile() { - const upperToolPath = this.toolPath.toUpperCase(); - return (this._endsWith(upperToolPath, '.CMD') || - this._endsWith(upperToolPath, '.BAT')); - } - _windowsQuoteCmdArg(arg) { - // for .exe, apply the normal quoting rules that libuv applies - if (!this._isCmdFile()) { - return this._uvQuoteCmdArg(arg); - } - // otherwise apply quoting rules specific to the cmd.exe command line parser. - // the libuv rules are generic and are not designed specifically for cmd.exe - // command line parser. - // - // for a detailed description of the cmd.exe command line parser, refer to - // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 - // need quotes for empty arg - if (!arg) { - return '""'; - } - // determine whether the arg needs to be quoted - const cmdSpecialChars = [ - ' ', - '\t', - '&', - '(', - ')', - '[', - ']', - '{', - '}', - '^', - '=', - ';', - '!', - "'", - '+', - ',', - '`', - '~', - '|', - '<', - '>', - '"' - ]; - let needsQuotes = false; - for (const char of arg) { - if (cmdSpecialChars.some(x => x === char)) { - needsQuotes = true; - break; - } - } - // short-circuit if quotes not needed - if (!needsQuotes) { - return arg; - } - // the following quoting rules are very similar to the rules that by libuv applies. - // - // 1) wrap the string in quotes - // - // 2) double-up quotes - i.e. " => "" - // - // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately - // doesn't work well with a cmd.exe command line. - // - // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. - // for example, the command line: - // foo.exe "myarg:""my val""" - // is parsed by a .NET console app into an arg array: - // [ "myarg:\"my val\"" ] - // which is the same end result when applying libuv quoting rules. although the actual - // command line from libuv quoting rules would look like: - // foo.exe "myarg:\"my val\"" - // - // 3) double-up slashes that precede a quote, - // e.g. hello \world => "hello \world" - // hello\"world => "hello\\""world" - // hello\\"world => "hello\\\\""world" - // hello world\ => "hello world\\" - // - // technically this is not required for a cmd.exe command line, or the batch argument parser. - // the reasons for including this as a .cmd quoting rule are: - // - // a) this is optimized for the scenario where the argument is passed from the .cmd file to an - // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. - // - // b) it's what we've been doing previously (by deferring to node default behavior) and we - // haven't heard any complaints about that aspect. - // - // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be - // escaped when used on the command line directly - even though within a .cmd file % can be escaped - // by using %%. - // - // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts - // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. - // - // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would - // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the - // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args - // to an external program. - // - // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. - // % can be escaped within a .cmd file. - let reverse = '"'; - let quoteHit = true; - for (let i = arg.length; i > 0; i--) { - // walk the string in reverse - reverse += arg[i - 1]; - if (quoteHit && arg[i - 1] === '\\') { - reverse += '\\'; // double the slash - } - else if (arg[i - 1] === '"') { - quoteHit = true; - reverse += '"'; // double the quote - } - else { - quoteHit = false; - } - } - reverse += '"'; - return reverse - .split('') - .reverse() - .join(''); - } - _uvQuoteCmdArg(arg) { - // Tool runner wraps child_process.spawn() and needs to apply the same quoting as - // Node in certain cases where the undocumented spawn option windowsVerbatimArguments - // is used. - // - // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV, - // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details), - // pasting copyright notice from Node within this function: - // - // Copyright Joyent, Inc. and other Node contributors. All rights reserved. - // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to - // deal in the Software without restriction, including without limitation the - // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - // sell copies of the Software, and to permit persons to whom the Software is - // furnished to do so, subject to the following conditions: - // - // The above copyright notice and this permission notice shall be included in - // all copies or substantial portions of the Software. - // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - // IN THE SOFTWARE. - if (!arg) { - // Need double quotation for empty argument - return '""'; - } - if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) { - // No quotation needed - return arg; - } - if (!arg.includes('"') && !arg.includes('\\')) { - // No embedded double quotes or backslashes, so I can just wrap - // quote marks around the whole thing. - return `"${arg}"`; - } - // Expected input/output: - // input : hello"world - // output: "hello\"world" - // input : hello""world - // output: "hello\"\"world" - // input : hello\world - // output: hello\world - // input : hello\\world - // output: hello\\world - // input : hello\"world - // output: "hello\\\"world" - // input : hello\\"world - // output: "hello\\\\\"world" - // input : hello world\ - // output: "hello world\\" - note the comment in libuv actually reads "hello world\" - // but it appears the comment is wrong, it should be "hello world\\" - let reverse = '"'; - let quoteHit = true; - for (let i = arg.length; i > 0; i--) { - // walk the string in reverse - reverse += arg[i - 1]; - if (quoteHit && arg[i - 1] === '\\') { - reverse += '\\'; - } - else if (arg[i - 1] === '"') { - quoteHit = true; - reverse += '\\'; - } - else { - quoteHit = false; - } - } - reverse += '"'; - return reverse - .split('') - .reverse() - .join(''); - } - _cloneExecOptions(options) { - options = options || {}; - const result = { - cwd: options.cwd || process.cwd(), - env: options.env || process.env, - silent: options.silent || false, - windowsVerbatimArguments: options.windowsVerbatimArguments || false, - failOnStdErr: options.failOnStdErr || false, - ignoreReturnCode: options.ignoreReturnCode || false, - delay: options.delay || 10000 - }; - result.outStream = options.outStream || process.stdout; - result.errStream = options.errStream || process.stderr; - return result; - } - _getSpawnOptions(options, toolPath) { - options = options || {}; - const result = {}; - result.cwd = options.cwd; - result.env = options.env; - result['windowsVerbatimArguments'] = - options.windowsVerbatimArguments || this._isCmdFile(); - if (options.windowsVerbatimArguments) { - result.argv0 = `"${toolPath}"`; - } - return result; - } - /** - * Exec a tool. - * Output will be streamed to the live console. - * Returns promise with return code - * - * @param tool path to tool to exec - * @param options optional exec options. See ExecOptions - * @returns number - */ - exec() { - return __awaiter(this, void 0, void 0, function* () { - // root the tool path if it is unrooted and contains relative pathing - if (!ioUtil.isRooted(this.toolPath) && - (this.toolPath.includes('/') || - (IS_WINDOWS && this.toolPath.includes('\\')))) { - // prefer options.cwd if it is specified, however options.cwd may also need to be rooted - this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath); - } - // if the tool is only a file name, then resolve it from the PATH - // otherwise verify it exists (add extension on Windows if necessary) - this.toolPath = yield io.which(this.toolPath, true); - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this._debug(`exec tool: ${this.toolPath}`); - this._debug('arguments:'); - for (const arg of this.args) { - this._debug(` ${arg}`); - } - const optionsNonNull = this._cloneExecOptions(this.options); - if (!optionsNonNull.silent && optionsNonNull.outStream) { - optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); - } - const state = new ExecState(optionsNonNull, this.toolPath); - state.on('debug', (message) => { - this._debug(message); - }); - if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) { - return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`)); - } - const fileName = this._getSpawnFileName(); - const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName)); - let stdbuffer = ''; - if (cp.stdout) { - cp.stdout.on('data', (data) => { - if (this.options.listeners && this.options.listeners.stdout) { - this.options.listeners.stdout(data); - } - if (!optionsNonNull.silent && optionsNonNull.outStream) { - optionsNonNull.outStream.write(data); - } - stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => { - if (this.options.listeners && this.options.listeners.stdline) { - this.options.listeners.stdline(line); - } - }); - }); - } - let errbuffer = ''; - if (cp.stderr) { - cp.stderr.on('data', (data) => { - state.processStderr = true; - if (this.options.listeners && this.options.listeners.stderr) { - this.options.listeners.stderr(data); - } - if (!optionsNonNull.silent && - optionsNonNull.errStream && - optionsNonNull.outStream) { - const s = optionsNonNull.failOnStdErr - ? optionsNonNull.errStream - : optionsNonNull.outStream; - s.write(data); - } - errbuffer = this._processLineBuffer(data, errbuffer, (line) => { - if (this.options.listeners && this.options.listeners.errline) { - this.options.listeners.errline(line); - } - }); - }); - } - cp.on('error', (err) => { - state.processError = err.message; - state.processExited = true; - state.processClosed = true; - state.CheckComplete(); - }); - cp.on('exit', (code) => { - state.processExitCode = code; - state.processExited = true; - this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); - state.CheckComplete(); - }); - cp.on('close', (code) => { - state.processExitCode = code; - state.processExited = true; - state.processClosed = true; - this._debug(`STDIO streams have closed for tool '${this.toolPath}'`); - state.CheckComplete(); - }); - state.on('done', (error, exitCode) => { - if (stdbuffer.length > 0) { - this.emit('stdline', stdbuffer); - } - if (errbuffer.length > 0) { - this.emit('errline', errbuffer); - } - cp.removeAllListeners(); - if (error) { - reject(error); - } - else { - resolve(exitCode); - } - }); - if (this.options.input) { - if (!cp.stdin) { - throw new Error('child process missing stdin'); - } - cp.stdin.end(this.options.input); - } - })); - }); - } -} -exports.ToolRunner = ToolRunner; -/** - * Convert an arg string to an array of args. Handles escaping - * - * @param argString string of arguments - * @returns string[] array of arguments - */ -function argStringToArray(argString) { - const args = []; - let inQuotes = false; - let escaped = false; - let arg = ''; - function append(c) { - // we only escape double quotes. - if (escaped && c !== '"') { - arg += '\\'; - } - arg += c; - escaped = false; - } - for (let i = 0; i < argString.length; i++) { - const c = argString.charAt(i); - if (c === '"') { - if (!escaped) { - inQuotes = !inQuotes; - } - else { - append(c); - } - continue; - } - if (c === '\\' && escaped) { - append(c); - continue; - } - if (c === '\\' && inQuotes) { - escaped = true; - continue; - } - if (c === ' ' && !inQuotes) { - if (arg.length > 0) { - args.push(arg); - arg = ''; - } - continue; - } - append(c); - } - if (arg.length > 0) { - args.push(arg.trim()); - } - return args; -} -exports.argStringToArray = argStringToArray; -class ExecState extends events.EventEmitter { - constructor(options, toolPath) { - super(); - this.processClosed = false; // tracks whether the process has exited and stdio is closed - this.processError = ''; - this.processExitCode = 0; - this.processExited = false; // tracks whether the process has exited - this.processStderr = false; // tracks whether stderr was written to - this.delay = 10000; // 10 seconds - this.done = false; - this.timeout = null; - if (!toolPath) { - throw new Error('toolPath must not be empty'); - } - this.options = options; - this.toolPath = toolPath; - if (options.delay) { - this.delay = options.delay; - } - } - CheckComplete() { - if (this.done) { - return; - } - if (this.processClosed) { - this._setResult(); - } - else if (this.processExited) { - this.timeout = timers_1.setTimeout(ExecState.HandleTimeout, this.delay, this); - } - } - _debug(message) { - this.emit('debug', message); - } - _setResult() { - // determine whether there is an error - let error; - if (this.processExited) { - if (this.processError) { - error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`); - } - else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) { - error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`); - } - else if (this.processStderr && this.options.failOnStdErr) { - error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`); - } - } - // clear the timeout - if (this.timeout) { - clearTimeout(this.timeout); - this.timeout = null; - } - this.done = true; - this.emit('done', error, this.processExitCode); - } - static HandleTimeout(state) { - if (state.done) { - return; - } - if (!state.processClosed && state.processExited) { - const message = `The STDIO streams did not close within ${state.delay / - 1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`; - state._debug(message); - } - state._setResult(); - } -} -//# sourceMappingURL=toolrunner.js.map - -/***/ }), - /***/ 4087: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { @@ -2592,656 +1857,124 @@ class HttpClient { const a = new Date(value); if (!isNaN(a.valueOf())) { return a; - } - } - return value; - } - let obj; - let contents; - try { - contents = yield res.readBody(); - if (contents && contents.length > 0) { - if (options && options.deserializeDates) { - obj = JSON.parse(contents, dateTimeDeserializer); - } - else { - obj = JSON.parse(contents); - } - response.result = obj; - } - response.headers = res.message.headers; - } - catch (err) { - // Invalid resource (contents not json); leaving result obj null - } - // note that 3xx redirects are handled by the http layer. - if (statusCode > 299) { - let msg; - // if exception/error in body, attempt to get better error - if (obj && obj.message) { - msg = obj.message; - } - else if (contents && contents.length > 0) { - // it may be the case that the exception is in the body message as string - msg = contents; - } - else { - msg = `Failed request: (${statusCode})`; - } - const err = new HttpClientError(msg, statusCode); - err.result = response.result; - reject(err); - } - else { - resolve(response); - } - })); - }); - } -} -exports.HttpClient = HttpClient; -const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 9835: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.checkBypass = exports.getProxyUrl = void 0; -function getProxyUrl(reqUrl) { - const usingSsl = reqUrl.protocol === 'https:'; - if (checkBypass(reqUrl)) { - return undefined; - } - const proxyVar = (() => { - if (usingSsl) { - return process.env['https_proxy'] || process.env['HTTPS_PROXY']; - } - else { - return process.env['http_proxy'] || process.env['HTTP_PROXY']; - } - })(); - if (proxyVar) { - return new URL(proxyVar); - } - else { - return undefined; - } -} -exports.getProxyUrl = getProxyUrl; -function checkBypass(reqUrl) { - if (!reqUrl.hostname) { - return false; - } - const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; - if (!noProxy) { - return false; - } - // Determine the request port - let reqPort; - if (reqUrl.port) { - reqPort = Number(reqUrl.port); - } - else if (reqUrl.protocol === 'http:') { - reqPort = 80; - } - else if (reqUrl.protocol === 'https:') { - reqPort = 443; - } - // Format the request hostname and hostname with port - const upperReqHosts = [reqUrl.hostname.toUpperCase()]; - if (typeof reqPort === 'number') { - upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); - } - // Compare request host against noproxy - for (const upperNoProxyItem of noProxy - .split(',') - .map(x => x.trim().toUpperCase()) - .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { - return true; - } - } - return false; -} -exports.checkBypass = checkBypass; -//# sourceMappingURL=proxy.js.map - -/***/ }), - -/***/ 1962: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var _a; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rename = exports.readlink = exports.readdir = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0; -const fs = __importStar(__nccwpck_require__(7147)); -const path = __importStar(__nccwpck_require__(1017)); -_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink; -exports.IS_WINDOWS = process.platform === 'win32'; -function exists(fsPath) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield exports.stat(fsPath); - } - catch (err) { - if (err.code === 'ENOENT') { - return false; - } - throw err; - } - return true; - }); -} -exports.exists = exists; -function isDirectory(fsPath, useStat = false) { - return __awaiter(this, void 0, void 0, function* () { - const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath); - return stats.isDirectory(); - }); -} -exports.isDirectory = isDirectory; -/** - * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like: - * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases). - */ -function isRooted(p) { - p = normalizeSeparators(p); - if (!p) { - throw new Error('isRooted() parameter "p" cannot be empty'); - } - if (exports.IS_WINDOWS) { - return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello - ); // e.g. C: or C:\hello - } - return p.startsWith('/'); -} -exports.isRooted = isRooted; -/** - * Best effort attempt to determine whether a file exists and is executable. - * @param filePath file path to check - * @param extensions additional file extensions to try - * @return if file exists and is executable, returns the file path. otherwise empty string. - */ -function tryGetExecutablePath(filePath, extensions) { - return __awaiter(this, void 0, void 0, function* () { - let stats = undefined; - try { - // test file exists - stats = yield exports.stat(filePath); - } - catch (err) { - if (err.code !== 'ENOENT') { - // eslint-disable-next-line no-console - console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); - } - } - if (stats && stats.isFile()) { - if (exports.IS_WINDOWS) { - // on Windows, test for valid extension - const upperExt = path.extname(filePath).toUpperCase(); - if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) { - return filePath; - } - } - else { - if (isUnixExecutable(stats)) { - return filePath; - } - } - } - // try each extension - const originalFilePath = filePath; - for (const extension of extensions) { - filePath = originalFilePath + extension; - stats = undefined; - try { - stats = yield exports.stat(filePath); - } - catch (err) { - if (err.code !== 'ENOENT') { - // eslint-disable-next-line no-console - console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); - } - } - if (stats && stats.isFile()) { - if (exports.IS_WINDOWS) { - // preserve the case of the actual file (since an extension was appended) - try { - const directory = path.dirname(filePath); - const upperName = path.basename(filePath).toUpperCase(); - for (const actualName of yield exports.readdir(directory)) { - if (upperName === actualName.toUpperCase()) { - filePath = path.join(directory, actualName); - break; - } - } - } - catch (err) { - // eslint-disable-next-line no-console - console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`); - } - return filePath; - } - else { - if (isUnixExecutable(stats)) { - return filePath; - } - } - } - } - return ''; - }); -} -exports.tryGetExecutablePath = tryGetExecutablePath; -function normalizeSeparators(p) { - p = p || ''; - if (exports.IS_WINDOWS) { - // convert slashes on Windows - p = p.replace(/\//g, '\\'); - // remove redundant slashes - return p.replace(/\\\\+/g, '\\'); - } - // remove redundant slashes - return p.replace(/\/\/+/g, '/'); -} -// on Mac/Linux, test the execute bit -// R W X R W X R W X -// 256 128 64 32 16 8 4 2 1 -function isUnixExecutable(stats) { - return ((stats.mode & 1) > 0 || - ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || - ((stats.mode & 64) > 0 && stats.uid === process.getuid())); -} -// Get the path of cmd.exe in windows -function getCmdPath() { - var _a; - return (_a = process.env['COMSPEC']) !== null && _a !== void 0 ? _a : `cmd.exe`; -} -exports.getCmdPath = getCmdPath; -//# sourceMappingURL=io-util.js.map - -/***/ }), - -/***/ 7436: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0; -const assert_1 = __nccwpck_require__(9491); -const childProcess = __importStar(__nccwpck_require__(2081)); -const path = __importStar(__nccwpck_require__(1017)); -const util_1 = __nccwpck_require__(3837); -const ioUtil = __importStar(__nccwpck_require__(1962)); -const exec = util_1.promisify(childProcess.exec); -const execFile = util_1.promisify(childProcess.execFile); -/** - * Copies a file or folder. - * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js - * - * @param source source path - * @param dest destination path - * @param options optional. See CopyOptions. - */ -function cp(source, dest, options = {}) { - return __awaiter(this, void 0, void 0, function* () { - const { force, recursive, copySourceDirectory } = readCopyOptions(options); - const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null; - // Dest is an existing file, but not forcing - if (destStat && destStat.isFile() && !force) { - return; - } - // If dest is an existing directory, should copy inside. - const newDest = destStat && destStat.isDirectory() && copySourceDirectory - ? path.join(dest, path.basename(source)) - : dest; - if (!(yield ioUtil.exists(source))) { - throw new Error(`no such file or directory: ${source}`); - } - const sourceStat = yield ioUtil.stat(source); - if (sourceStat.isDirectory()) { - if (!recursive) { - throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`); - } - else { - yield cpDirRecursive(source, newDest, 0, force); - } - } - else { - if (path.relative(source, newDest) === '') { - // a file cannot be copied to itself - throw new Error(`'${newDest}' and '${source}' are the same file`); - } - yield copyFile(source, newDest, force); - } - }); -} -exports.cp = cp; -/** - * Moves a path. - * - * @param source source path - * @param dest destination path - * @param options optional. See MoveOptions. - */ -function mv(source, dest, options = {}) { - return __awaiter(this, void 0, void 0, function* () { - if (yield ioUtil.exists(dest)) { - let destExists = true; - if (yield ioUtil.isDirectory(dest)) { - // If dest is directory copy src into dest - dest = path.join(dest, path.basename(source)); - destExists = yield ioUtil.exists(dest); - } - if (destExists) { - if (options.force == null || options.force) { - yield rmRF(dest); - } - else { - throw new Error('Destination already exists'); - } - } - } - yield mkdirP(path.dirname(dest)); - yield ioUtil.rename(source, dest); - }); -} -exports.mv = mv; -/** - * Remove a path recursively with force - * - * @param inputPath path to remove - */ -function rmRF(inputPath) { - return __awaiter(this, void 0, void 0, function* () { - if (ioUtil.IS_WINDOWS) { - // Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another - // program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del. - // Check for invalid characters - // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file - if (/[*"<>|]/.test(inputPath)) { - throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'); - } - try { - const cmdPath = ioUtil.getCmdPath(); - if (yield ioUtil.isDirectory(inputPath, true)) { - yield exec(`${cmdPath} /s /c "rd /s /q "%inputPath%""`, { - env: { inputPath } - }); + } + } + return value; } - else { - yield exec(`${cmdPath} /s /c "del /f /a "%inputPath%""`, { - env: { inputPath } - }); + let obj; + let contents; + try { + contents = yield res.readBody(); + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, dateTimeDeserializer); + } + else { + obj = JSON.parse(contents); + } + response.result = obj; + } + response.headers = res.message.headers; } - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; - } - // Shelling out fails to remove a symlink folder with missing source, this unlink catches that - try { - yield ioUtil.unlink(inputPath); - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; - } - } - else { - let isDir = false; - try { - isDir = yield ioUtil.isDirectory(inputPath); - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; - return; - } - if (isDir) { - yield execFile(`rm`, [`-rf`, `${inputPath}`]); - } - else { - yield ioUtil.unlink(inputPath); - } - } - }); -} -exports.rmRF = rmRF; -/** - * Make a directory. Creates the full path with folders in between - * Will throw if it fails - * - * @param fsPath path to create - * @returns Promise - */ -function mkdirP(fsPath) { - return __awaiter(this, void 0, void 0, function* () { - assert_1.ok(fsPath, 'a path argument must be provided'); - yield ioUtil.mkdir(fsPath, { recursive: true }); - }); -} -exports.mkdirP = mkdirP; -/** - * Returns path of a tool had the tool actually been invoked. Resolves via paths. - * If you check and the tool does not exist, it will throw. - * - * @param tool name of the tool - * @param check whether to check if tool exists - * @returns Promise path to tool - */ -function which(tool, check) { - return __awaiter(this, void 0, void 0, function* () { - if (!tool) { - throw new Error("parameter 'tool' is required"); - } - // recursive when check=true - if (check) { - const result = yield which(tool, false); - if (!result) { - if (ioUtil.IS_WINDOWS) { - throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`); + catch (err) { + // Invalid resource (contents not json); leaving result obj null + } + // note that 3xx redirects are handled by the http layer. + if (statusCode > 299) { + let msg; + // if exception/error in body, attempt to get better error + if (obj && obj.message) { + msg = obj.message; + } + else if (contents && contents.length > 0) { + // it may be the case that the exception is in the body message as string + msg = contents; + } + else { + msg = `Failed request: (${statusCode})`; + } + const err = new HttpClientError(msg, statusCode); + err.result = response.result; + reject(err); } else { - throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`); + resolve(response); } - } - return result; - } - const matches = yield findInPath(tool); - if (matches && matches.length > 0) { - return matches[0]; - } - return ''; - }); + })); + }); + } } -exports.which = which; -/** - * Returns a list of all occurrences of the given tool on the system path. - * - * @returns Promise the paths of the tool - */ -function findInPath(tool) { - return __awaiter(this, void 0, void 0, function* () { - if (!tool) { - throw new Error("parameter 'tool' is required"); - } - // build the list of extensions to try - const extensions = []; - if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) { - for (const extension of process.env['PATHEXT'].split(path.delimiter)) { - if (extension) { - extensions.push(extension); - } - } - } - // if it's rooted, return it if exists. otherwise return empty. - if (ioUtil.isRooted(tool)) { - const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions); - if (filePath) { - return [filePath]; - } - return []; - } - // if any path separators, return empty - if (tool.includes(path.sep)) { - return []; - } - // build the list of directories - // - // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, - // it feels like we should not do this. Checking the current directory seems like more of a use - // case of a shell, and the which() function exposed by the toolkit should strive for consistency - // across platforms. - const directories = []; - if (process.env.PATH) { - for (const p of process.env.PATH.split(path.delimiter)) { - if (p) { - directories.push(p); - } - } - } - // find all matches - const matches = []; - for (const directory of directories) { - const filePath = yield ioUtil.tryGetExecutablePath(path.join(directory, tool), extensions); - if (filePath) { - matches.push(filePath); - } +exports.HttpClient = HttpClient; +const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 9835: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.checkBypass = exports.getProxyUrl = void 0; +function getProxyUrl(reqUrl) { + const usingSsl = reqUrl.protocol === 'https:'; + if (checkBypass(reqUrl)) { + return undefined; + } + const proxyVar = (() => { + if (usingSsl) { + return process.env['https_proxy'] || process.env['HTTPS_PROXY']; } - return matches; - }); -} -exports.findInPath = findInPath; -function readCopyOptions(options) { - const force = options.force == null ? true : options.force; - const recursive = Boolean(options.recursive); - const copySourceDirectory = options.copySourceDirectory == null - ? true - : Boolean(options.copySourceDirectory); - return { force, recursive, copySourceDirectory }; -} -function cpDirRecursive(sourceDir, destDir, currentDepth, force) { - return __awaiter(this, void 0, void 0, function* () { - // Ensure there is not a run away recursive copy - if (currentDepth >= 255) - return; - currentDepth++; - yield mkdirP(destDir); - const files = yield ioUtil.readdir(sourceDir); - for (const fileName of files) { - const srcFile = `${sourceDir}/${fileName}`; - const destFile = `${destDir}/${fileName}`; - const srcFileStat = yield ioUtil.lstat(srcFile); - if (srcFileStat.isDirectory()) { - // Recurse - yield cpDirRecursive(srcFile, destFile, currentDepth, force); - } - else { - yield copyFile(srcFile, destFile, force); - } + else { + return process.env['http_proxy'] || process.env['HTTP_PROXY']; } - // Change the mode for the newly created directory - yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode); - }); + })(); + if (proxyVar) { + return new URL(proxyVar); + } + else { + return undefined; + } } -// Buffered file copy -function copyFile(srcFile, destFile, force) { - return __awaiter(this, void 0, void 0, function* () { - if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) { - // unlink/re-link it - try { - yield ioUtil.lstat(destFile); - yield ioUtil.unlink(destFile); - } - catch (e) { - // Try to override file permission - if (e.code === 'EPERM') { - yield ioUtil.chmod(destFile, '0666'); - yield ioUtil.unlink(destFile); - } - // other errors = it doesn't exist, no work to do - } - // Copy over symlink - const symlinkFull = yield ioUtil.readlink(srcFile); - yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null); - } - else if (!(yield ioUtil.exists(destFile)) || force) { - yield ioUtil.copyFile(srcFile, destFile); +exports.getProxyUrl = getProxyUrl; +function checkBypass(reqUrl) { + if (!reqUrl.hostname) { + return false; + } + const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; + if (!noProxy) { + return false; + } + // Determine the request port + let reqPort; + if (reqUrl.port) { + reqPort = Number(reqUrl.port); + } + else if (reqUrl.protocol === 'http:') { + reqPort = 80; + } + else if (reqUrl.protocol === 'https:') { + reqPort = 443; + } + // Format the request hostname and hostname with port + const upperReqHosts = [reqUrl.hostname.toUpperCase()]; + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); + } + // Compare request host against noproxy + for (const upperNoProxyItem of noProxy + .split(',') + .map(x => x.trim().toUpperCase()) + .filter(x => x)) { + if (upperReqHosts.some(x => x === upperNoProxyItem)) { + return true; } - }); + } + return false; } -//# sourceMappingURL=io.js.map +exports.checkBypass = checkBypass; +//# sourceMappingURL=proxy.js.map /***/ }), @@ -28164,7 +26897,7 @@ const _template = __nccwpck_require__(417) const core = __nccwpck_require__(2186) const { PR_TITLE_PREFIX } = __nccwpck_require__(6818) -const { execWithOutput } = __nccwpck_require__(8632) +const { runSpawn } = __nccwpck_require__(2137) const { callApi } = __nccwpck_require__(4235) const transformCommitMessage = __nccwpck_require__(6701) const { logInfo } = __nccwpck_require__(653) @@ -28184,7 +26917,8 @@ const addArtifact = async (inputs, releaseId) => { const createDraftRelease = async (inputs, newVersion) => { try { - const releaseCommitHash = await execWithOutput('git', ['rev-parse', 'HEAD']) + const run = runSpawn() + const releaseCommitHash = await run('git', ['rev-parse', 'HEAD']) logInfo(`Creating draft release from commit: ${releaseCommitHash}`) @@ -28210,6 +26944,7 @@ const createDraftRelease = async (inputs, newVersion) => { module.exports = async function ({ context, inputs, packageVersion }) { logInfo('** Starting Opening Release PR **') + const run = runSpawn() if (!packageVersion) { throw new Error('packageVersion is missing!') @@ -28220,15 +26955,15 @@ module.exports = async function ({ context, inputs, packageVersion }) { const branchName = `release/${newVersion}` const messageTemplate = inputs['commit-message'] - await execWithOutput('git', ['checkout', '-b', branchName]) - await execWithOutput('git', ['add', '-A']) - await execWithOutput('git', [ + await run('git', ['checkout', '-b', branchName]) + await run('git', ['add', '-A']) + await run('git', [ 'commit', '-m', `"${transformCommitMessage(messageTemplate, newVersion)}"`, ]) - await execWithOutput('git', ['push', 'origin', branchName]) + await run('git', ['push', 'origin', branchName]) const draftRelease = await createDraftRelease(inputs, newVersion) @@ -28261,7 +26996,6 @@ module.exports = async function ({ context, inputs, packageVersion }) { }, inputs ) - /* istanbul ignore else */ if (response?.status !== 201) { const errMessage = response?.message || 'PR creation failed' throw new Error(errMessage) @@ -28269,7 +27003,7 @@ module.exports = async function ({ context, inputs, packageVersion }) { } catch (err) { let message = `Unable to create the pull request ${err.message}` try { - await execWithOutput('git', ['push', 'origin', '--delete', branchName]) + await run('git', ['push', 'origin', '--delete', branchName]) } catch (error) { message += `\n Unable to delete branch ${branchName}: ${error.message}` } @@ -28294,7 +27028,7 @@ const semver = __nccwpck_require__(1383) const { PR_TITLE_PREFIX } = __nccwpck_require__(6818) const { callApi } = __nccwpck_require__(4235) const { tagVersionInGit } = __nccwpck_require__(9143) -const { execWithOutput } = __nccwpck_require__(8632) +const { runSpawn } = __nccwpck_require__(2137) const { revertCommit } = __nccwpck_require__(5765) const { publishToNpm } = __nccwpck_require__(1433) const { notifyIssues } = __nccwpck_require__(8361) @@ -28348,13 +27082,14 @@ module.exports = async function ({ github, context, inputs }) { return } + const run = runSpawn() const branchName = `release/${version}` try { // We "always" delete the release branch, if anything fails, the whole // workflow has to be restarted from scratch. logInfo(`deleting ${branchName}`) - await execWithOutput('git', ['push', 'origin', '--delete', branchName]) + await run('git', ['push', 'origin', '--delete', branchName]) } catch (err) { // That's not a big problem, so we don't want to mark the action as failed. logWarning('Unable to delete the release branch') @@ -28514,7 +27249,6 @@ const { ZIP_EXTENSION } = __nccwpck_require__(6818) const attach = async (path, releaseId, token) => { const filename = deriveFilename(path, ZIP_EXTENSION) - /* istanbul ignore else */ if (!path.endsWith(ZIP_EXTENSION)) { await archiveItem(path, filename) } @@ -28617,75 +27351,6 @@ const transformCommitMessage = (template, version) => { module.exports = transformCommitMessage -/***/ }), - -/***/ 8632: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -const { StringDecoder } = __nccwpck_require__(6915) - -const { exec } = __nccwpck_require__(1514) - -/** - * - * @param {string} cmd - * @param {string[]} args - * @param {{cwd?: string}} options - * @returns Promise - */ -async function execWithOutput(cmd, args, { cwd } = {}) { - let output = '' - let errorOutput = '' - - const stdoutDecoder = new StringDecoder('utf8') - const stderrDecoder = new StringDecoder('utf8') - - const options = {} - - /* istanbul ignore else */ - if (cwd !== '') { - options.cwd = cwd - } - - options.listeners = { - /** - * - * @param {Buffer} data - */ - stdout: data => { - output += stdoutDecoder.write(data) - }, - /** - * - * @param {Buffer} data - */ - stderr: data => { - errorOutput += stderrDecoder.write(data) - }, - } - - const code = await exec(cmd, args, options) - - output += stdoutDecoder.end() - errorOutput += stderrDecoder.end() - - if (code === 0) { - return output.trim() - } - - throw new Error( - `${cmd} ${args.join( - ' ' - )} returned code ${code} \nSTDOUT: ${output}\nSTDERR: ${errorOutput}` - ) -} - -exports.execWithOutput = execWithOutput - - /***/ }), /***/ 8361: @@ -28842,15 +27507,17 @@ exports.notifyIssues = notifyIssues "use strict"; -const { execWithOutput } = __nccwpck_require__(8632) +const { runSpawn } = __nccwpck_require__(2137) async function allowNpmPublish(version) { + const run = runSpawn() + // We need to check if the package was already published. This can happen if // the action was already executed before, but it failed in its last step // (GH release). let packageName = null try { - const packageInfo = await execWithOutput('npm', ['view', '--json']) + const packageInfo = await run('npm', ['view', '--json']) packageName = packageInfo ? JSON.parse(packageInfo).name : null } catch (error) { if (!error?.message?.match(/code E404/)) { @@ -28871,10 +27538,7 @@ async function allowNpmPublish(version) { try { // npm < v8.13.0 returns empty output, newer versions throw a E404 // We handle both and consider them as package version not existing - packageVersionInfo = await execWithOutput('npm', [ - 'view', - `${packageName}@${version}`, - ]) + packageVersionInfo = await run('npm', ['view', `${packageName}@${version}`]) } catch (error) { if (!error?.message?.match(/code E404/)) { throw error @@ -28891,22 +27555,21 @@ async function publishToNpm({ npmTag, version, }) { - await execWithOutput('npm', [ + const run = runSpawn() + + await run('npm', [ 'config', 'set', `//registry.npmjs.org/:_authToken=${npmToken}`, ]) if (await allowNpmPublish(version)) { - await execWithOutput('npm', ['pack', '--dry-run']) + await run('npm', ['pack', '--dry-run']) if (opticToken) { - const otp = await execWithOutput('curl', [ - '-s', - `${opticUrl}${opticToken}`, - ]) - await execWithOutput('npm', ['publish', '--otp', otp, '--tag', npmTag]) + const otp = await run('curl', ['-s', `${opticUrl}${opticToken}`]) + await run('npm', ['publish', '--otp', otp, '--tag', npmTag]) } else { - await execWithOutput('npm', ['publish', '--tag', npmTag]) + await run('npm', ['publish', '--tag', npmTag]) } } } @@ -29011,16 +27674,62 @@ module.exports = { "use strict"; -const { execWithOutput } = __nccwpck_require__(8632) +const { runSpawn } = __nccwpck_require__(2137) async function revertCommit(baseRef) { - await execWithOutput('git', ['revert', 'HEAD']) - await execWithOutput('git', ['push', 'origin', baseRef]) + const run = runSpawn() + + await run('git', ['revert', 'HEAD']) + await run('git', ['push', 'origin', baseRef]) } exports.revertCommit = revertCommit +/***/ }), + +/***/ 2137: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +const { spawn } = __nccwpck_require__(2081) + +function runSpawn({ cwd } = {}) { + return (cmd, args) => { + return new Promise((resolve, reject) => { + const cli = spawn(cmd, args, { cwd, env: process.env, shell: true }) + cli.stdout.setEncoding('utf8') + cli.stderr.setEncoding('utf8') + + let stdout = '' + let stderr = '' + cli.stdout.on('data', data => { + stdout += data + }) + cli.stderr.on('data', data => { + stderr += data + }) + cli.on('close', (code, signal) => { + if (code === 0) { + return resolve(stdout.trim()) + } + reject( + new Error( + `${cmd} ${args.join( + ' ' + )} returned code ${code} and signal ${signal}\nSTDOUT: ${stdout}\nSTDERR: ${stderr}` + ) + ) + }) + }) + } +} + +exports.runSpawn = runSpawn + + /***/ }), /***/ 9143: @@ -29028,12 +27737,14 @@ exports.revertCommit = revertCommit "use strict"; -const { execWithOutput } = __nccwpck_require__(8632) +const { runSpawn } = __nccwpck_require__(2137) async function tagVersionInGit(version) { - await execWithOutput('git', ['push', 'origin', `:refs/tags/${version}`]) - await execWithOutput('git', ['tag', '-f', `"${version}"`]) - await execWithOutput('git', ['push', 'origin', `--tags`]) + const run = runSpawn() + + await run('git', ['push', 'origin', `:refs/tags/${version}`]) + await run('git', ['tag', '-f', `"${version}"`]) + await run('git', ['push', 'origin', `--tags`]) } exports.tagVersionInGit = tagVersionInGit @@ -29129,14 +27840,6 @@ module.exports = require("net"); /***/ }), -/***/ 6915: -/***/ ((module) => { - -"use strict"; -module.exports = require("node:string_decoder"); - -/***/ }), - /***/ 2037: /***/ ((module) => { @@ -29169,22 +27872,6 @@ module.exports = require("stream"); /***/ }), -/***/ 1576: -/***/ ((module) => { - -"use strict"; -module.exports = require("string_decoder"); - -/***/ }), - -/***/ 9512: -/***/ ((module) => { - -"use strict"; -module.exports = require("timers"); - -/***/ }), - /***/ 4404: /***/ ((module) => { diff --git a/package-lock.json b/package-lock.json index bf5a0ff5..1591d7fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "MIT", "dependencies": { "@actions/core": "^1.10.0", - "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", "adm-zip": "^0.5.9", "lodash.isequal": "^4.5.0", @@ -45,14 +44,6 @@ "uuid": "^8.3.2" } }, - "node_modules/@actions/exec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", - "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", - "dependencies": { - "@actions/io": "^1.0.1" - } - }, "node_modules/@actions/github": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", @@ -72,11 +63,6 @@ "tunnel": "^0.0.6" } }, - "node_modules/@actions/io": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", - "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" - }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -5825,14 +5811,6 @@ "uuid": "^8.3.2" } }, - "@actions/exec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", - "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", - "requires": { - "@actions/io": "^1.0.1" - } - }, "@actions/github": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", @@ -5852,11 +5830,6 @@ "tunnel": "^0.0.6" } }, - "@actions/io": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", - "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" - }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", diff --git a/package.json b/package.json index cfdf7117..92cf9034 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "license": "MIT", "dependencies": { "@actions/core": "^1.10.0", - "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", "adm-zip": "^0.5.9", "lodash.isequal": "^4.5.0", diff --git a/src/openPr.js b/src/openPr.js index 67228b66..ccef452b 100644 --- a/src/openPr.js +++ b/src/openPr.js @@ -6,7 +6,7 @@ const _template = require('lodash.template') const core = require('@actions/core') const { PR_TITLE_PREFIX } = require('./const') -const { execWithOutput } = require('./utils/execWithOutput') +const { runSpawn } = require('./utils/runSpawn') const { callApi } = require('./utils/callApi') const transformCommitMessage = require('./utils/commitMessage') const { logInfo } = require('./log') @@ -26,7 +26,8 @@ const addArtifact = async (inputs, releaseId) => { const createDraftRelease = async (inputs, newVersion) => { try { - const releaseCommitHash = await execWithOutput('git', ['rev-parse', 'HEAD']) + const run = runSpawn() + const releaseCommitHash = await run('git', ['rev-parse', 'HEAD']) logInfo(`Creating draft release from commit: ${releaseCommitHash}`) @@ -52,6 +53,7 @@ const createDraftRelease = async (inputs, newVersion) => { module.exports = async function ({ context, inputs, packageVersion }) { logInfo('** Starting Opening Release PR **') + const run = runSpawn() if (!packageVersion) { throw new Error('packageVersion is missing!') @@ -62,15 +64,15 @@ module.exports = async function ({ context, inputs, packageVersion }) { const branchName = `release/${newVersion}` const messageTemplate = inputs['commit-message'] - await execWithOutput('git', ['checkout', '-b', branchName]) - await execWithOutput('git', ['add', '-A']) - await execWithOutput('git', [ + await run('git', ['checkout', '-b', branchName]) + await run('git', ['add', '-A']) + await run('git', [ 'commit', '-m', `"${transformCommitMessage(messageTemplate, newVersion)}"`, ]) - await execWithOutput('git', ['push', 'origin', branchName]) + await run('git', ['push', 'origin', branchName]) const draftRelease = await createDraftRelease(inputs, newVersion) @@ -103,7 +105,6 @@ module.exports = async function ({ context, inputs, packageVersion }) { }, inputs ) - /* istanbul ignore else */ if (response?.status !== 201) { const errMessage = response?.message || 'PR creation failed' throw new Error(errMessage) @@ -111,7 +112,7 @@ module.exports = async function ({ context, inputs, packageVersion }) { } catch (err) { let message = `Unable to create the pull request ${err.message}` try { - await execWithOutput('git', ['push', 'origin', '--delete', branchName]) + await run('git', ['push', 'origin', '--delete', branchName]) } catch (error) { message += `\n Unable to delete branch ${branchName}: ${error.message}` } diff --git a/src/release.js b/src/release.js index 23078e49..a0ba4b67 100644 --- a/src/release.js +++ b/src/release.js @@ -6,7 +6,7 @@ const semver = require('semver') const { PR_TITLE_PREFIX } = require('./const') const { callApi } = require('./utils/callApi') const { tagVersionInGit } = require('./utils/tagVersion') -const { execWithOutput } = require('./utils/execWithOutput') +const { runSpawn } = require('./utils/runSpawn') const { revertCommit } = require('./utils/revertCommit') const { publishToNpm } = require('./utils/publishToNpm') const { notifyIssues } = require('./utils/notifyIssues') @@ -60,13 +60,14 @@ module.exports = async function ({ github, context, inputs }) { return } + const run = runSpawn() const branchName = `release/${version}` try { // We "always" delete the release branch, if anything fails, the whole // workflow has to be restarted from scratch. logInfo(`deleting ${branchName}`) - await execWithOutput('git', ['push', 'origin', '--delete', branchName]) + await run('git', ['push', 'origin', '--delete', branchName]) } catch (err) { // That's not a big problem, so we don't want to mark the action as failed. logWarning('Unable to delete the release branch') diff --git a/src/utils/artifact.js b/src/utils/artifact.js index 2249e57e..68a12ac6 100644 --- a/src/utils/artifact.js +++ b/src/utils/artifact.js @@ -9,7 +9,6 @@ const { ZIP_EXTENSION } = require('../const') const attach = async (path, releaseId, token) => { const filename = deriveFilename(path, ZIP_EXTENSION) - /* istanbul ignore else */ if (!path.endsWith(ZIP_EXTENSION)) { await archiveItem(path, filename) } diff --git a/src/utils/execWithOutput.js b/src/utils/execWithOutput.js deleted file mode 100644 index 846f4a4d..00000000 --- a/src/utils/execWithOutput.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict' - -const { StringDecoder } = require('node:string_decoder') - -const { exec } = require('@actions/exec') - -/** - * - * @param {string} cmd - * @param {string[]} args - * @param {{cwd?: string}} options - * @returns Promise - */ -async function execWithOutput(cmd, args, { cwd } = {}) { - let output = '' - let errorOutput = '' - - const stdoutDecoder = new StringDecoder('utf8') - const stderrDecoder = new StringDecoder('utf8') - - const options = {} - - /* istanbul ignore else */ - if (cwd !== '') { - options.cwd = cwd - } - - options.listeners = { - /** - * - * @param {Buffer} data - */ - stdout: data => { - output += stdoutDecoder.write(data) - }, - /** - * - * @param {Buffer} data - */ - stderr: data => { - errorOutput += stderrDecoder.write(data) - }, - } - - const code = await exec(cmd, args, options) - - output += stdoutDecoder.end() - errorOutput += stderrDecoder.end() - - if (code === 0) { - return output.trim() - } - - throw new Error( - `${cmd} ${args.join( - ' ' - )} returned code ${code} \nSTDOUT: ${output}\nSTDERR: ${errorOutput}` - ) -} - -exports.execWithOutput = execWithOutput diff --git a/src/utils/publishToNpm.js b/src/utils/publishToNpm.js index 662ec879..d700410e 100644 --- a/src/utils/publishToNpm.js +++ b/src/utils/publishToNpm.js @@ -1,14 +1,16 @@ 'use strict' -const { execWithOutput } = require('./execWithOutput') +const { runSpawn } = require('./runSpawn') async function allowNpmPublish(version) { + const run = runSpawn() + // We need to check if the package was already published. This can happen if // the action was already executed before, but it failed in its last step // (GH release). let packageName = null try { - const packageInfo = await execWithOutput('npm', ['view', '--json']) + const packageInfo = await run('npm', ['view', '--json']) packageName = packageInfo ? JSON.parse(packageInfo).name : null } catch (error) { if (!error?.message?.match(/code E404/)) { @@ -29,10 +31,7 @@ async function allowNpmPublish(version) { try { // npm < v8.13.0 returns empty output, newer versions throw a E404 // We handle both and consider them as package version not existing - packageVersionInfo = await execWithOutput('npm', [ - 'view', - `${packageName}@${version}`, - ]) + packageVersionInfo = await run('npm', ['view', `${packageName}@${version}`]) } catch (error) { if (!error?.message?.match(/code E404/)) { throw error @@ -49,22 +48,21 @@ async function publishToNpm({ npmTag, version, }) { - await execWithOutput('npm', [ + const run = runSpawn() + + await run('npm', [ 'config', 'set', `//registry.npmjs.org/:_authToken=${npmToken}`, ]) if (await allowNpmPublish(version)) { - await execWithOutput('npm', ['pack', '--dry-run']) + await run('npm', ['pack', '--dry-run']) if (opticToken) { - const otp = await execWithOutput('curl', [ - '-s', - `${opticUrl}${opticToken}`, - ]) - await execWithOutput('npm', ['publish', '--otp', otp, '--tag', npmTag]) + const otp = await run('curl', ['-s', `${opticUrl}${opticToken}`]) + await run('npm', ['publish', '--otp', otp, '--tag', npmTag]) } else { - await execWithOutput('npm', ['publish', '--tag', npmTag]) + await run('npm', ['publish', '--tag', npmTag]) } } } diff --git a/src/utils/revertCommit.js b/src/utils/revertCommit.js index f6c1d9f9..2225d0ff 100644 --- a/src/utils/revertCommit.js +++ b/src/utils/revertCommit.js @@ -1,9 +1,11 @@ 'use strict' -const { execWithOutput } = require('./execWithOutput') +const { runSpawn } = require('./runSpawn') async function revertCommit(baseRef) { - await execWithOutput('git', ['revert', 'HEAD']) - await execWithOutput('git', ['push', 'origin', baseRef]) + const run = runSpawn() + + await run('git', ['revert', 'HEAD']) + await run('git', ['push', 'origin', baseRef]) } exports.revertCommit = revertCommit diff --git a/src/utils/runSpawn.js b/src/utils/runSpawn.js new file mode 100644 index 00000000..dbd6b0df --- /dev/null +++ b/src/utils/runSpawn.js @@ -0,0 +1,36 @@ +'use strict' + +const { spawn } = require('child_process') + +function runSpawn({ cwd } = {}) { + return (cmd, args) => { + return new Promise((resolve, reject) => { + const cli = spawn(cmd, args, { cwd, env: process.env, shell: true }) + cli.stdout.setEncoding('utf8') + cli.stderr.setEncoding('utf8') + + let stdout = '' + let stderr = '' + cli.stdout.on('data', data => { + stdout += data + }) + cli.stderr.on('data', data => { + stderr += data + }) + cli.on('close', (code, signal) => { + if (code === 0) { + return resolve(stdout.trim()) + } + reject( + new Error( + `${cmd} ${args.join( + ' ' + )} returned code ${code} and signal ${signal}\nSTDOUT: ${stdout}\nSTDERR: ${stderr}` + ) + ) + }) + }) + } +} + +exports.runSpawn = runSpawn diff --git a/src/utils/tagVersion.js b/src/utils/tagVersion.js index 9cd1791d..9e87b6b2 100644 --- a/src/utils/tagVersion.js +++ b/src/utils/tagVersion.js @@ -1,10 +1,12 @@ 'use strict' -const { execWithOutput } = require('./execWithOutput') +const { runSpawn } = require('./runSpawn') async function tagVersionInGit(version) { - await execWithOutput('git', ['push', 'origin', `:refs/tags/${version}`]) - await execWithOutput('git', ['tag', '-f', `"${version}"`]) - await execWithOutput('git', ['push', 'origin', `--tags`]) + const run = runSpawn() + + await run('git', ['push', 'origin', `:refs/tags/${version}`]) + await run('git', ['tag', '-f', `"${version}"`]) + await run('git', ['push', 'origin', `--tags`]) } exports.tagVersionInGit = tagVersionInGit diff --git a/test/archiver.test.js b/test/archiver.test.js index 3c4c2a59..ae612133 100644 --- a/test/archiver.test.js +++ b/test/archiver.test.js @@ -38,49 +38,3 @@ tap.test('does not throw any errors', async t => { t.resolves(archiverModule.archiveItem('path', 'out.zip')) }) - -tap.test('throws if writing to zip file fails', async t => { - const archiverModule = tap.mock('../src/utils/archiver.js', { - 'fs/promises': { - lstat: async () => ({ - isDirectory: () => true, - }), - }, - 'adm-zip': function Mocked() { - this.addLocalFolderPromise = async function () { - return null - } - this.addLocalFile = function () { - return null - } - this.writeZipPromise = async function () { - return Promise.reject() - } - }, - }) - - t.rejects(archiverModule.archiveItem('path', 'out.zip')) -}) - -tap.test('resolves if a path is not a directory', async t => { - const archiverModule = tap.mock('../src/utils/archiver.js', { - 'fs/promises': { - lstat: async () => ({ - isDirectory: () => false, - }), - }, - 'adm-zip': function Mocked() { - this.addLocalFolderPromise = async function () { - return Promise.reject() - } - this.addLocalFile = function () { - return undefined - } - this.writeZipPromise = async function () { - return null - } - }, - }) - - t.resolves(archiverModule.archiveItem('path', 'out.zip')) -}) diff --git a/test/bump.test.js b/test/bump.test.js index 7fc77e95..7d314f83 100644 --- a/test/bump.test.js +++ b/test/bump.test.js @@ -6,21 +6,21 @@ const sinon = require('sinon') const core = require('@actions/core') const clone = require('lodash.clonedeep') +const runSpawnAction = require('../src/utils/runSpawn') const callApiAction = require('../src/utils/callApi') const artifactAction = require('../src/utils/artifact') const { PR_TITLE_PREFIX } = require('../src/const') const TEST_VERSION = '3.1.1' const TEST_COMMIT_HASH = 'c86b0a35014a7036b245f81ff9de9bd738a5fe95' -const execWithOutputStub = sinon.stub() +const runSpawnStub = sinon.stub() -execWithOutputStub.resolves(TEST_VERSION) -execWithOutputStub - .withArgs('git', ['rev-parse', 'HEAD']) - .resolves(TEST_COMMIT_HASH) +runSpawnStub.returns(TEST_VERSION) +runSpawnStub.withArgs('git', ['rev-parse', 'HEAD']).returns(TEST_COMMIT_HASH) function setup() { const coreStub = sinon.stub(core) + const utilStub = sinon.stub(runSpawnAction, 'runSpawn').returns(runSpawnStub) const callApiStub = sinon .stub(callApiAction, 'callApi') .resolves({ data: {} }) @@ -33,7 +33,7 @@ function setup() { }) const openPr = proxyquire('../src/openPr', { - './utils/execWithOutput': { execWithOutput: execWithOutputStub }, + './utils/runSpawn': utilStub, './utils/artifact': attachArtifactStub, '@actions/core': coreStub, }) @@ -41,7 +41,8 @@ function setup() { return { openPr, stubs: { - execWithOutputStub, + utilStub, + runSpawnStub, callApiStub, coreStub, attachArtifactStub, @@ -99,18 +100,18 @@ tap.test('should create a new git branch', async () => { const branchName = `release/v${TEST_VERSION}` - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'checkout', '-b', branchName, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', ['add', '-A']) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', ['add', '-A']) + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'commit', '-m', `"Release v${TEST_VERSION}"`, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'push', 'origin', branchName, @@ -126,17 +127,17 @@ tap.test('should handle custom commit messages', async () => { const branchName = `release/v${TEST_VERSION}` - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'checkout', '-b', branchName, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'commit', '-m', `"[v${TEST_VERSION}] The brand new v${TEST_VERSION} has been released"`, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'push', 'origin', branchName, @@ -159,18 +160,18 @@ tap.test('should work with a custom version-prefix', async () => { const branchName = `release/${TEST_VERSION}` // git - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'checkout', '-b', branchName, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', ['add', '-A']) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', ['add', '-A']) + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'commit', '-m', `"Release v${TEST_VERSION}"`, ]) - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'push', 'origin', branchName, @@ -272,7 +273,7 @@ tap.test( async () => { const localVersion = '2.0.0' const { openPr, stubs } = setup() - execWithOutputStub.returns(localVersion) + runSpawnStub.returns(localVersion) await openPr({ ...DEFAULT_ACTION_DATA, packageVersion: localVersion, @@ -328,7 +329,7 @@ tap.test( async () => { const localVersion = '0.0.5' const { openPr, stubs } = setup() - execWithOutputStub.returns(localVersion) + runSpawnStub.returns(localVersion) await openPr({ ...DEFAULT_ACTION_DATA, packageVersion: localVersion, @@ -387,7 +388,7 @@ tap.test('should delete branch in case of pr failure', async t => { await openPr({ context, inputs, packageVersion: localVersion }) const branchName = `release/v${localVersion}` - sinon.assert.calledWithExactly(stubs.execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub, 'git', [ 'push', 'origin', '--delete', @@ -397,16 +398,9 @@ tap.test('should delete branch in case of pr failure', async t => { }) tap.test('Should call core.setFailed if it fails to create a PR', async t => { - const branchName = `release/v${TEST_VERSION}` - const { openPr, stubs } = setup() const { context, inputs, packageVersion } = DEFAULT_ACTION_DATA stubs.callApiStub.onCall(1).rejects() - - stubs.execWithOutputStub - .withArgs('git', ['push', 'origin', '--delete', branchName]) - .rejects() - await openPr({ context, inputs, packageVersion }) sinon.assert.calledOnce(stubs.coreStub.setFailed) diff --git a/test/execWithOutput.test.js b/test/execWithOutput.test.js deleted file mode 100644 index 8a338d3a..00000000 --- a/test/execWithOutput.test.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict' - -const tap = require('tap') -const sinon = require('sinon') -const proxyquire = require('proxyquire') - -const execStub = sinon.stub() - -const execWithOutputModule = proxyquire('../src/utils/execWithOutput', { - '@actions/exec': { - exec: execStub, - }, -}) - -tap.afterEach(() => { - sinon.restore() -}) - -tap.test( - 'resolves with output of the exec command if exit code is 0', - async t => { - const output = 'output' - - execStub.callsFake((_, __, options) => { - options.listeners.stdout(Buffer.from(output, 'utf8')) - - return Promise.resolve(0) - }) - - t.resolves(execWithOutputModule.execWithOutput('ls', ['-al']), output) - execStub.calledWith('ls', ['-al']) - } -) - -tap.test('provides cwd to exec function', async () => { - const cwd = './' - - execStub.resolves(0) - - execWithOutputModule.execWithOutput('command', [], cwd) - execStub.calledWith('command', [], { cwd }) -}) - -tap.test('rejects if exit code is not 0', async t => { - const errorOutput = 'error output' - - execStub.callsFake((_, __, options) => { - options.listeners.stderr(Buffer.from(errorOutput, 'utf8')) - - return Promise.resolve(1) - }) - - t.rejects(execWithOutputModule.execWithOutput('command')) - execStub.calledWith('command') -}) diff --git a/test/notifyIssues.test.js b/test/notifyIssues.test.js index 1350fea3..0c699503 100644 --- a/test/notifyIssues.test.js +++ b/test/notifyIssues.test.js @@ -250,54 +250,3 @@ tap.test( }) } ) - -tap.test("Shouldn't fail if createComment on an issue fails", async t => { - const { notifyIssues } = setup() - - const releaseNotes = ` - ## What's Changed\n + - * chore 15 by @people in https://github.com/owner/repo/pull/13\n - \n - \n - **Full Changelog**: https://github.com/owner/repo/compare/v1.0.20...v1.1.0 - ` - - const release = { body: releaseNotes, html_url: 'some_url' } - - const graphqlStub = sinon.stub().resolves({ - repository: { - pullRequest: { - closingIssuesReferences: { - nodes: [ - { - number: '10', - repository: { - name: 'repo', - owner: { login: 'owner' }, - }, - }, - { - number: '15', - repository: { - name: 'repo', - owner: { login: 'owner' }, - }, - }, - ], - }, - }, - }, - }) - - createCommentStub.rejects() - - t.resolves( - notifyIssues( - { ...DEFAULT_GITHUB_CLIENT, graphql: graphqlStub }, - true, - 'owner', - 'repo', - release - ) - ) -}) diff --git a/test/publishToNpm.test.js b/test/publishToNpm.test.js index 51045cf6..82ce0d8f 100644 --- a/test/publishToNpm.test.js +++ b/test/publishToNpm.test.js @@ -3,27 +3,29 @@ const tap = require('tap') const sinon = require('sinon') const proxyquire = require('proxyquire') +const runSpawnAction = require('../src/utils/runSpawn') const setup = () => { - const execWithOutputStub = sinon.stub() - execWithOutputStub + const runSpawnStub = sinon.stub() + runSpawnStub .withArgs('curl', [ '-s', 'https://optic-test.run.app/api/generate/optic-token', ]) .returns('otp123') - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', '--json']) .returns('{"name":"fakeTestPkg"}') // npm behavior < v8.13.0 - execWithOutputStub.withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']).returns('') + runSpawnStub.withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']).returns('') + const utilStub = sinon.stub(runSpawnAction, 'runSpawn').returns(runSpawnStub) const publishToNpmProxy = proxyquire('../src/utils/publishToNpm', { - './execWithOutput': { execWithOutput: execWithOutputStub }, + './runSpawn': utilStub, }) - return { execWithOutputStub, publishToNpmProxy } + return { runSpawnStub, publishToNpmProxy } } tap.afterEach(() => { @@ -31,7 +33,7 @@ tap.afterEach(() => { }) tap.test('Should publish to npm with optic', async t => { - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() await publishToNpmProxy.publishToNpm({ npmToken: 'a-token', opticToken: 'optic-token', @@ -40,7 +42,7 @@ tap.test('Should publish to npm with optic', async t => { version: 'v5.1.3', }) - sinon.assert.calledWithExactly(execWithOutputStub.getCall(0), 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub.getCall(0), 'npm', [ 'config', 'set', '//registry.npmjs.org/:_authToken=a-token', @@ -50,19 +52,19 @@ tap.test('Should publish to npm with optic', async t => { // We skip calls in these checks: // - 1 used to get the package name // - 2 used to check if the package version is already published - sinon.assert.calledWithExactly(execWithOutputStub.getCall(3), 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub.getCall(3), 'npm', [ 'pack', '--dry-run', ]) t.pass('npm pack called') - sinon.assert.calledWithExactly(execWithOutputStub.getCall(4), 'curl', [ + sinon.assert.calledWithExactly(runSpawnStub.getCall(4), 'curl', [ '-s', 'https://optic-test.run.app/api/generate/optic-token', ]) t.pass('curl called') - sinon.assert.calledWithExactly(execWithOutputStub.getCall(5), 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub.getCall(5), 'npm', [ 'publish', '--otp', 'otp123', @@ -72,34 +74,8 @@ tap.test('Should publish to npm with optic', async t => { t.pass('npm publish called') }) -tap.test( - "Should publish to npm when package hasn't been published before", - async () => { - const { publishToNpmProxy, execWithOutputStub } = setup() - - execWithOutputStub.withArgs('npm', ['view', '--json']).resolves('') - - await publishToNpmProxy.publishToNpm({ - npmToken: 'a-token', - opticUrl: 'https://optic-test.run.app/api/generate/', - npmTag: 'latest', - version: 'v5.1.3', - }) - - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ - 'pack', - '--dry-run', - ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ - 'publish', - '--tag', - 'latest', - ]) - } -) - tap.test('Should publish to npm without optic', async () => { - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() await publishToNpmProxy.publishToNpm({ npmToken: 'a-token', opticUrl: 'https://optic-test.run.app/api/generate/', @@ -107,11 +83,8 @@ tap.test('Should publish to npm without optic', async () => { version: 'v5.1.3', }) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ - 'pack', - '--dry-run', - ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub, 'npm', ['pack', '--dry-run']) + sinon.assert.calledWithExactly(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', @@ -121,9 +94,9 @@ tap.test('Should publish to npm without optic', async () => { tap.test( 'Should skip npm package publication when it was already published', async () => { - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']) .returns('fake package data that says it was published') @@ -134,14 +107,14 @@ tap.test( version: 'v5.1.3', }) - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--otp', 'otp123', '--tag', 'latest', ]) - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', @@ -151,9 +124,9 @@ tap.test( tap.test('Should stop action if package info retrieval fails', async t => { t.plan(3) - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', '--json']) .throws(new Error('Network Error')) @@ -168,7 +141,7 @@ tap.test('Should stop action if package info retrieval fails', async t => { t.equal(e.message, 'Network Error') } - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--otp', 'otp123', @@ -177,7 +150,7 @@ tap.test('Should stop action if package info retrieval fails', async t => { ]) t.pass('package is not published with otp code') - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', @@ -189,9 +162,9 @@ tap.test( 'Should stop action if package version info retrieval fails', async t => { t.plan(3) - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']) .throws(new Error('Network Error')) @@ -206,7 +179,7 @@ tap.test( t.equal(e.message, 'Network Error') } - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--otp', 'otp123', @@ -215,7 +188,7 @@ tap.test( ]) t.pass('package is not published with otp code') - sinon.assert.neverCalledWith(execWithOutputStub, 'npm', [ + sinon.assert.neverCalledWith(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', @@ -227,15 +200,13 @@ tap.test( tap.test( 'Should continue action if package info returns not found', async () => { - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', '--json']) .throws(new Error('code E404')) - execWithOutputStub - .withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']) - .returns('') + runSpawnStub.withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']).returns('') await publishToNpmProxy.publishToNpm({ npmToken: 'a-token', @@ -244,11 +215,8 @@ tap.test( version: 'v5.1.3', }) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ - 'pack', - '--dry-run', - ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub, 'npm', ['pack', '--dry-run']) + sinon.assert.calledWithExactly(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', @@ -259,9 +227,9 @@ tap.test( tap.test( 'Should continue action if package version info returns not found', async () => { - const { publishToNpmProxy, execWithOutputStub } = setup() + const { publishToNpmProxy, runSpawnStub } = setup() - execWithOutputStub + runSpawnStub .withArgs('npm', ['view', 'fakeTestPkg@v5.1.3']) .throws(new Error('code E404')) @@ -272,11 +240,8 @@ tap.test( version: 'v5.1.3', }) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ - 'pack', - '--dry-run', - ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'npm', [ + sinon.assert.calledWithExactly(runSpawnStub, 'npm', ['pack', '--dry-run']) + sinon.assert.calledWithExactly(runSpawnStub, 'npm', [ 'publish', '--tag', 'latest', diff --git a/test/release.test.js b/test/release.test.js index 70d4efd6..45150fe9 100644 --- a/test/release.test.js +++ b/test/release.test.js @@ -6,6 +6,7 @@ const sinon = require('sinon') const core = require('@actions/core') const clone = require('lodash.clonedeep') +const runSpawnAction = require('../src/utils/runSpawn') const tagVersionAction = require('../src/utils/tagVersion') const publishToNpmAction = require('../src/utils/publishToNpm') const notifyIssuesAction = require('../src/utils/notifyIssues') @@ -69,14 +70,18 @@ function setup() { deleteReleaseStub.resetHistory() deleteReleaseStub.resolves() - const execWithOutputStub = sinon.stub() - execWithOutputStub + const runSpawnStub = sinon.stub() + runSpawnStub .withArgs('curl', [ '-s', 'https://optic-test.run.app/api/generate/optic-token', ]) .returns('otp123') + const runSpawnProxy = sinon + .stub(runSpawnAction, 'runSpawn') + .returns(runSpawnStub) + const tagVersionStub = sinon.stub(tagVersionAction, 'tagVersionInGit') const revertCommitStub = sinon.stub(revertCommitAction, 'revertCommit') const publishToNpmStub = sinon.stub(publishToNpmAction, 'publishToNpm') @@ -87,7 +92,7 @@ function setup() { .resolves({ data: { body: 'test_body', html_url: 'test_url' } }) const release = proxyquire('../src/release', { - './utils/execWithOutput': { execWithOutput: execWithOutputStub }, + './utils/runSpawn': runSpawnProxy, './utils/tagVersion': tagVersionStub, './utils/revertCommit': revertCommitStub, './utils/publishToNpm': publishToNpmStub, @@ -102,7 +107,8 @@ function setup() { revertCommitStub, publishToNpmStub, notifyIssuesStub, - execWithOutputStub, + runSpawnProxy, + runSpawnStub, callApiStub, logStub, coreStub, @@ -133,9 +139,7 @@ tap.test( const { release, stubs } = setup() const data = clone(DEFAULT_ACTION_DATA) data.context.payload.pull_request.merged = false - stubs.execWithOutputStub.rejects( - new Error('Something went wrong in the branch') - ) + stubs.runSpawnStub.rejects(new Error('Something went wrong in the branch')) await release(data) @@ -168,9 +172,7 @@ tap.test( const { release, stubs } = setup() const data = clone(DEFAULT_ACTION_DATA) data.context.payload.pull_request.merged = false - stubs.execWithOutputStub.rejects( - new Error('Something went wrong in the branch') - ) + stubs.runSpawnStub.rejects(new Error('Something went wrong in the branch')) deleteReleaseStub.rejects(new Error('Something went wrong in the release')) await release(data) @@ -337,7 +339,7 @@ tap.test( await release(data) sinon.assert.notCalled(stubs.callApiStub) - sinon.assert.notCalled(stubs.execWithOutputStub) + sinon.assert.notCalled(stubs.runSpawnStub) } ) @@ -349,7 +351,7 @@ tap.test('Should fail if the release metadata is incorrect', async () => { sinon.assert.calledOnce(stubs.logStub.logError) sinon.assert.notCalled(stubs.callApiStub) - sinon.assert.notCalled(stubs.execWithOutputStub) + sinon.assert.notCalled(stubs.runSpawnStub) }) tap.test( @@ -496,7 +498,7 @@ tap.test( }) // We check that it's actually the first command line command to be executed - sinon.assert.calledWithExactly(stubs.execWithOutputStub.getCall(0), 'git', [ + sinon.assert.calledWithExactly(stubs.runSpawnStub.getCall(0), 'git', [ 'push', 'origin', '--delete', @@ -521,7 +523,7 @@ tap.test( await release(data) - sinon.assert.neverCalledWith(stubs.execWithOutputStub, 'git', [ + sinon.assert.neverCalledWith(stubs.runSpawnStub, 'git', [ 'push', 'origin', '--delete', @@ -571,43 +573,6 @@ tap.test( } ) -tap.test('Should not reject when notifyIssues fails', async t => { - const { release, stubs } = setup() - - stubs.notifyIssuesStub.rejects() - - t.resolves( - release({ - ...DEFAULT_ACTION_DATA, - inputs: { - 'app-name': APP_NAME, - 'npm-token': 'a-token', - 'notify-linked-issues': 'true', - }, - }) - ) -}) - -tap.test('Should fail when getting draft release fails', async () => { - const { release, stubs } = setup() - - await release({ - ...DEFAULT_ACTION_DATA, - github: { - ...DEFAULT_ACTION_DATA.github, - rest: { - ...DEFAULT_ACTION_DATA.github.rest, - repos: { - ...DEFAULT_ACTION_DATA.github.rest, - getRelease: sinon.stub().rejects(), - }, - }, - }, - }) - - sinon.assert.called(stubs.coreStub.setFailed) -}) - tap.test('Should fail when release is not found', async () => { const { release, stubs } = setup() @@ -619,16 +584,13 @@ tap.test('Should fail when release is not found', async () => { ...DEFAULT_ACTION_DATA.github.rest, repos: { ...DEFAULT_ACTION_DATA.github.rest, - getRelease: sinon.stub().returns({ data: undefined }), + getRelease: sinon.stub().returns(undefined), }, }, }, }) - sinon.assert.calledWith( - stubs.coreStub.setFailed, - `Couldn't find draft release to publish. Aborting.` - ) + sinon.assert.called(stubs.coreStub.setFailed) }) tap.test('Should not fail when release is not a draft', async () => { diff --git a/test/revertCommit.test.js b/test/revertCommit.test.js index 86439d63..a00f48b6 100644 --- a/test/revertCommit.test.js +++ b/test/revertCommit.test.js @@ -3,14 +3,16 @@ const tap = require('tap') const sinon = require('sinon') const proxyquire = require('proxyquire') +const runSpawnAction = require('../src/utils/runSpawn') const setup = () => { - const execWithOutputStub = sinon.stub() + const runSpawnStub = sinon.stub() + const utilStub = sinon.stub(runSpawnAction, 'runSpawn').returns(runSpawnStub) const revertCommitProxy = proxyquire('../src/utils/revertCommit', { - './execWithOutput': { execWithOutput: execWithOutputStub }, + './runSpawn': utilStub, }) - return { execWithOutputStub, revertCommitProxy } + return { runSpawnStub, revertCommitProxy } } tap.afterEach(() => { @@ -18,14 +20,14 @@ tap.afterEach(() => { }) tap.test('Revert commit', async t => { - const { revertCommitProxy, execWithOutputStub } = setup() + const { revertCommitProxy, runSpawnStub } = setup() const baseRef = 'master' await revertCommitProxy.revertCommit(baseRef) - t.ok(execWithOutputStub.callCount === 2) + t.ok(runSpawnStub.callCount === 2) - sinon.assert.calledWithExactly(execWithOutputStub, 'git', ['revert', 'HEAD']) - sinon.assert.calledWithExactly(execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(runSpawnStub, 'git', ['revert', 'HEAD']) + sinon.assert.calledWithExactly(runSpawnStub, 'git', [ 'push', 'origin', `${baseRef}`, diff --git a/test/tagVersion.test.js b/test/tagVersion.test.js index bc93b131..d7c848e1 100644 --- a/test/tagVersion.test.js +++ b/test/tagVersion.test.js @@ -3,14 +3,16 @@ const tap = require('tap') const sinon = require('sinon') const proxyquire = require('proxyquire') +const runSpawnAction = require('../src/utils/runSpawn') const setup = () => { - const execWithOutputStub = sinon.stub() + const runSpawnStub = sinon.stub() + const utilStub = sinon.stub(runSpawnAction, 'runSpawn').returns(runSpawnStub) const tagVersionProxy = proxyquire('../src/utils/tagVersion', { - './execWithOutput': { execWithOutput: execWithOutputStub }, + './runSpawn': utilStub, }) - return { execWithOutputStub, tagVersionProxy } + return { runSpawnStub, tagVersionProxy } } tap.afterEach(() => { @@ -18,23 +20,23 @@ tap.afterEach(() => { }) tap.test('Tag version in git', async t => { - const { tagVersionProxy, execWithOutputStub } = setup() + const { tagVersionProxy, runSpawnStub } = setup() const version = 'v3.0.0' await tagVersionProxy.tagVersionInGit(version) - t.ok(execWithOutputStub.callCount === 3) + t.ok(runSpawnStub.callCount === 3) - sinon.assert.calledWithExactly(execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(runSpawnStub, 'git', [ 'push', 'origin', `:refs/tags/${version}`, ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(runSpawnStub, 'git', [ 'tag', '-f', `"${version}"`, ]) - sinon.assert.calledWithExactly(execWithOutputStub, 'git', [ + sinon.assert.calledWithExactly(runSpawnStub, 'git', [ 'push', 'origin', `--tags`,