From 40b74c984eeba538eeee0388d67a380c099c3a2f Mon Sep 17 00:00:00 2001 From: Nick Fields <46869826+nick-fields@users.noreply.github.com> Date: Fri, 5 Aug 2022 21:58:31 -0400 Subject: [PATCH] minor: refactor to make testing easier --- dist/index.js | 204 ++++++++++++++++++++++++++++++---------------- src/index.test.ts | 0 src/index.ts | 70 ++++------------ src/inputs.ts | 51 ++++++++++++ src/util.ts | 9 ++ 5 files changed, 211 insertions(+), 123 deletions(-) create mode 100644 src/index.test.ts create mode 100644 src/inputs.ts diff --git a/dist/index.js b/dist/index.js index 8346935..6cdeea1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -527,7 +527,7 @@ module.exports = require("https"); /***/ }), /***/ 322: -/***/ (function(__unusedmodule, exports) { +/***/ (function(__unusedmodule, exports, __webpack_require__) { "use strict"; @@ -568,7 +568,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) { } }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.wait = void 0; +exports.retryWait = exports.wait = void 0; +var core_1 = __webpack_require__(470); function wait(ms) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { @@ -577,6 +578,24 @@ function wait(ms) { }); } exports.wait = wait; +function retryWait(retryWaitSeconds) { + return __awaiter(this, void 0, void 0, function () { + var waitStart; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + waitStart = Date.now(); + return [4 /*yield*/, wait(retryWaitSeconds)]; + case 1: + _a.sent(); + (0, core_1.debug)("Waited ".concat(Date.now() - waitStart, "ms")); + (0, core_1.debug)("Configured wait: ".concat(retryWaitSeconds, "ms")); + return [2 /*return*/]; + } + }); + }); +} +exports.retryWait = retryWait; /***/ }), @@ -630,82 +649,28 @@ var core_1 = __webpack_require__(470); var child_process_1 = __webpack_require__(129); var milliseconds_1 = __importDefault(__webpack_require__(156)); var tree_kill_1 = __importDefault(__webpack_require__(791)); +var inputs_1 = __webpack_require__(679); var util_1 = __webpack_require__(322); // inputs -var TIMEOUT_MINUTES = getInputNumber('timeout_minutes', false); -var TIMEOUT_SECONDS = getInputNumber('timeout_seconds', false); -var MAX_ATTEMPTS = getInputNumber('max_attempts', true) || 3; +var TIMEOUT_MINUTES = (0, inputs_1.getInputNumber)('timeout_minutes', false); +var TIMEOUT_SECONDS = (0, inputs_1.getInputNumber)('timeout_seconds', false); +var MAX_ATTEMPTS = (0, inputs_1.getInputNumber)('max_attempts', true) || 3; var COMMAND = (0, core_1.getInput)('command', { required: true }); -var RETRY_WAIT_SECONDS = getInputNumber('retry_wait_seconds', false) || 10; +var RETRY_WAIT_SECONDS = (0, inputs_1.getInputNumber)('retry_wait_seconds', false) || 10; var SHELL = (0, core_1.getInput)('shell'); -var POLLING_INTERVAL_SECONDS = getInputNumber('polling_interval_seconds', false) || 1; +var POLLING_INTERVAL_SECONDS = (0, inputs_1.getInputNumber)('polling_interval_seconds', false) || 1; var RETRY_ON = (0, core_1.getInput)('retry_on') || 'any'; var WARNING_ON_RETRY = (0, core_1.getInput)('warning_on_retry').toLowerCase() === 'true'; var ON_RETRY_COMMAND = (0, core_1.getInput)('on_retry_command'); -var CONTINUE_ON_ERROR = getInputBoolean('continue_on_error'); +var CONTINUE_ON_ERROR = (0, inputs_1.getInputBoolean)('continue_on_error'); var NEW_COMMAND_ON_RETRY = (0, core_1.getInput)('new_command_on_retry'); -var RETRY_ON_EXIT_CODE = getInputNumber('retry_on_exit_code', false); +var RETRY_ON_EXIT_CODE = (0, inputs_1.getInputNumber)('retry_on_exit_code', false); var OS = process.platform; var OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts'; var OUTPUT_EXIT_CODE_KEY = 'exit_code'; var OUTPUT_EXIT_ERROR_KEY = 'exit_error'; var exit; var done; -function getInputNumber(id, required) { - var input = (0, core_1.getInput)(id, { required: required }); - var num = Number.parseInt(input); - // empty is ok - if (!input && !required) { - return; - } - if (!Number.isInteger(num)) { - throw "Input ".concat(id, " only accepts numbers. Received ").concat(input); - } - return num; -} -function getInputBoolean(id) { - var input = (0, core_1.getInput)(id); - if (!['true', 'false'].includes(input.toLowerCase())) { - throw "Input ".concat(id, " only accepts boolean values. Received ").concat(input); - } - return input.toLowerCase() === 'true'; -} -function retryWait() { - return __awaiter(this, void 0, void 0, function () { - var waitStart; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - waitStart = Date.now(); - return [4 /*yield*/, (0, util_1.wait)(milliseconds_1.default.seconds(RETRY_WAIT_SECONDS))]; - case 1: - _a.sent(); - (0, core_1.debug)("Waited ".concat(Date.now() - waitStart, "ms")); - (0, core_1.debug)("Configured wait: ".concat(milliseconds_1.default.seconds(RETRY_WAIT_SECONDS), "ms")); - return [2 /*return*/]; - } - }); - }); -} -function validateInputs() { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - if ((!TIMEOUT_MINUTES && !TIMEOUT_SECONDS) || (TIMEOUT_MINUTES && TIMEOUT_SECONDS)) { - throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); - } - return [2 /*return*/]; - }); - }); -} -function getTimeout() { - if (TIMEOUT_MINUTES) { - return milliseconds_1.default.minutes(TIMEOUT_MINUTES); - } - else if (TIMEOUT_SECONDS) { - return milliseconds_1.default.seconds(TIMEOUT_SECONDS); - } - throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); -} function getExecutable() { if (!SHELL) { return OS === 'win32' ? 'powershell' : 'bash'; @@ -772,7 +737,7 @@ function runCmd(attempt) { return __generator(this, function (_c) { switch (_c.label) { case 0: - end_time = Date.now() + getTimeout(); + end_time = Date.now() + (0, inputs_1.getTimeout)({ timeoutMinutes: TIMEOUT_MINUTES, timeoutSeconds: TIMEOUT_SECONDS }); executable = getExecutable(); exit = 0; done = false; @@ -809,13 +774,16 @@ function runCmd(attempt) { case 4: if (!(!done && child.pid)) return [3 /*break*/, 6]; (0, tree_kill_1.default)(child.pid); - return [4 /*yield*/, retryWait()]; + return [4 /*yield*/, (0, util_1.retryWait)(milliseconds_1.default.seconds(RETRY_WAIT_SECONDS))]; case 5: _c.sent(); - throw new Error("Timeout of ".concat(getTimeout(), "ms hit")); + throw new Error("Timeout of ".concat((0, inputs_1.getTimeout)({ + timeoutMinutes: TIMEOUT_MINUTES, + timeoutSeconds: TIMEOUT_SECONDS, + }), "ms hit")); case 6: if (!(exit > 0)) return [3 /*break*/, 8]; - return [4 /*yield*/, retryWait()]; + return [4 /*yield*/, (0, util_1.retryWait)(milliseconds_1.default.seconds(RETRY_WAIT_SECONDS))]; case 7: _c.sent(); throw new Error("Child_process exited with error code ".concat(exit)); @@ -829,7 +797,10 @@ function runAction() { var attempt, error_2; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, validateInputs()]; + case 0: return [4 /*yield*/, (0, inputs_1.validateInputs)({ + timeoutMinutes: TIMEOUT_MINUTES, + timeoutSeconds: TIMEOUT_SECONDS, + })]; case 1: _a.sent(); attempt = 1; @@ -2443,6 +2414,101 @@ exports.summary = _summary; module.exports = require("util"); +/***/ }), + +/***/ 679: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +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 __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getTimeout = exports.validateInputs = exports.getInputBoolean = exports.getInputNumber = void 0; +var core_1 = __webpack_require__(470); +var milliseconds_1 = __importDefault(__webpack_require__(156)); +function getInputNumber(id, required) { + var input = (0, core_1.getInput)(id, { required: required }); + var num = Number.parseInt(input); + // empty is ok + if (!input && !required) { + return; + } + if (!Number.isInteger(num)) { + throw "Input ".concat(id, " only accepts numbers. Received ").concat(input); + } + return num; +} +exports.getInputNumber = getInputNumber; +function getInputBoolean(id) { + var input = (0, core_1.getInput)(id); + if (!['true', 'false'].includes(input.toLowerCase())) { + throw "Input ".concat(id, " only accepts boolean values. Received ").concat(input); + } + return input.toLowerCase() === 'true'; +} +exports.getInputBoolean = getInputBoolean; +function validateInputs(inputs) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if ((!inputs.timeoutMinutes && !inputs.timeoutSeconds) || + (inputs.timeoutMinutes && inputs.timeoutSeconds)) { + throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); + } + return [2 /*return*/]; + }); + }); +} +exports.validateInputs = validateInputs; +function getTimeout(inputs) { + if (inputs.timeoutMinutes) { + return milliseconds_1.default.minutes(inputs.timeoutMinutes); + } + else if (inputs.timeoutSeconds) { + return milliseconds_1.default.seconds(inputs.timeoutSeconds); + } + throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); +} +exports.getTimeout = getTimeout; + + /***/ }), /***/ 742: diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/index.ts b/src/index.ts index c1b67ac..db172d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,9 @@ import { getInput, error, warning, info, debug, setOutput } from '@actions/core' import { execSync, spawn } from 'child_process'; import ms from 'milliseconds'; import kill from 'tree-kill'; +import { getInputBoolean, getInputNumber, getTimeout, validateInputs } from './inputs'; -import { wait } from './util'; +import { retryWait, wait } from './util'; // inputs const TIMEOUT_MINUTES = getInputNumber('timeout_minutes', false); @@ -28,54 +29,6 @@ const OUTPUT_EXIT_ERROR_KEY = 'exit_error'; let exit: number; let done: boolean; -function getInputNumber(id: string, required: boolean): number | undefined { - const input = getInput(id, { required }); - const num = Number.parseInt(input); - - // empty is ok - if (!input && !required) { - return; - } - - if (!Number.isInteger(num)) { - throw `Input ${id} only accepts numbers. Received ${input}`; - } - - return num; -} - -function getInputBoolean(id: string): boolean { - const input = getInput(id); - - if (!['true', 'false'].includes(input.toLowerCase())) { - throw `Input ${id} only accepts boolean values. Received ${input}`; - } - return input.toLowerCase() === 'true'; -} - -async function retryWait() { - const waitStart = Date.now(); - await wait(ms.seconds(RETRY_WAIT_SECONDS)); - debug(`Waited ${Date.now() - waitStart}ms`); - debug(`Configured wait: ${ms.seconds(RETRY_WAIT_SECONDS)}ms`); -} - -async function validateInputs() { - if ((!TIMEOUT_MINUTES && !TIMEOUT_SECONDS) || (TIMEOUT_MINUTES && TIMEOUT_SECONDS)) { - throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); - } -} - -function getTimeout(): number { - if (TIMEOUT_MINUTES) { - return ms.minutes(TIMEOUT_MINUTES); - } else if (TIMEOUT_SECONDS) { - return ms.seconds(TIMEOUT_SECONDS); - } - - throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); -} - function getExecutable(): string { if (!SHELL) { return OS === 'win32' ? 'powershell' : 'bash'; @@ -128,7 +81,8 @@ async function runRetryCmd(): Promise { } async function runCmd(attempt: number) { - const end_time = Date.now() + getTimeout(); + const end_time = + Date.now() + getTimeout({ timeoutMinutes: TIMEOUT_MINUTES, timeoutSeconds: TIMEOUT_SECONDS }); const executable = getExecutable(); exit = 0; @@ -166,10 +120,15 @@ async function runCmd(attempt: number) { if (!done && child.pid) { kill(child.pid); - await retryWait(); - throw new Error(`Timeout of ${getTimeout()}ms hit`); + await retryWait(ms.seconds(RETRY_WAIT_SECONDS)); + throw new Error( + `Timeout of ${getTimeout({ + timeoutMinutes: TIMEOUT_MINUTES, + timeoutSeconds: TIMEOUT_SECONDS, + })}ms hit` + ); } else if (exit > 0) { - await retryWait(); + await retryWait(ms.seconds(RETRY_WAIT_SECONDS)); throw new Error(`Child_process exited with error code ${exit}`); } else { return; @@ -177,7 +136,10 @@ async function runCmd(attempt: number) { } async function runAction() { - await validateInputs(); + await validateInputs({ + timeoutMinutes: TIMEOUT_MINUTES, + timeoutSeconds: TIMEOUT_SECONDS, + }); for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { try { diff --git a/src/inputs.ts b/src/inputs.ts new file mode 100644 index 0000000..7fca12b --- /dev/null +++ b/src/inputs.ts @@ -0,0 +1,51 @@ +import { getInput, error, warning, info, debug, setOutput } from '@actions/core'; +import ms from 'milliseconds'; + +interface TimeoutInputs { + timeoutMinutes: number | undefined; + timeoutSeconds: number | undefined; +} + +export function getInputNumber(id: string, required: boolean): number | undefined { + const input = getInput(id, { required }); + const num = Number.parseInt(input); + + // empty is ok + if (!input && !required) { + return; + } + + if (!Number.isInteger(num)) { + throw `Input ${id} only accepts numbers. Received ${input}`; + } + + return num; +} + +export function getInputBoolean(id: string): boolean { + const input = getInput(id); + + if (!['true', 'false'].includes(input.toLowerCase())) { + throw `Input ${id} only accepts boolean values. Received ${input}`; + } + return input.toLowerCase() === 'true'; +} + +export async function validateInputs(inputs: TimeoutInputs) { + if ( + (!inputs.timeoutMinutes && !inputs.timeoutSeconds) || + (inputs.timeoutMinutes && inputs.timeoutSeconds) + ) { + throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); + } +} + +export function getTimeout(inputs: TimeoutInputs): number { + if (inputs.timeoutMinutes) { + return ms.minutes(inputs.timeoutMinutes); + } else if (inputs.timeoutSeconds) { + return ms.seconds(inputs.timeoutSeconds); + } + + throw new Error('Must specify either timeout_minutes or timeout_seconds inputs'); +} diff --git a/src/util.ts b/src/util.ts index ef398e0..a296b60 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,3 +1,12 @@ +import { getInput, error, warning, info, debug, setOutput } from '@actions/core'; + export async function wait(ms: number) { return new Promise((r) => setTimeout(r, ms)); } + +export async function retryWait(retryWaitSeconds: number) { + const waitStart = Date.now(); + await wait(retryWaitSeconds); + debug(`Waited ${Date.now() - waitStart}ms`); + debug(`Configured wait: ${retryWaitSeconds}ms`); +}