From 5d47e3d695793db309166fa556fa90b8f5d3c1da Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:22:22 +1100 Subject: [PATCH] feat: Added Git Runbook support (#432) * Added Git Runbook support * Upgraded to latest API client * Include built action file --- README.md | 19 +-- action.yml | 2 + dist/index.js | 341 +++++++++++++++++++++++++++++++++++----- package-lock.json | 14 +- package.json | 2 +- src/api-wrapper.ts | 48 ++++++ src/input-parameters.ts | 4 +- 7 files changed, 372 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 254a2a71..13641338 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,16 @@ steps: ## 📥 Inputs -| Name | Description | -| :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `project` | **Required.** The name of the project associated with this runbook. | -| `runbook` | **Required.** The name of the runbook. | -| `environments` | **Required.** A list of environments in Octopus Deploy in which to run (i.e. Dev, Test, Prod). Each environment should be added on a new line. | -| `variables` | A multi-line list of prompted variable values. Format: name:value. | -| `server` | The instance URL hosting Octopus Deploy (i.e. ""). The instance URL is required, but you may also use the OCTOPUS_URL environment variable. | -| `api_key` | The API key used to access Octopus Deploy. An API key is required, but you may also use the OCTOPUS_API_KEY environment variable. It is strongly recommended that this value retrieved from a GitHub secret. | -| `space` | The name of a space within which this command will be executed. The space name is required, but you may also use the OCTOPUS_SPACE environment variable. | +| Name | Description | +| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `project` | **Required.** The name of the project associated with this runbook. | +| `runbook` | **Required.** The name of the runbook. | +| `environments` | **Required.** A list of environments in Octopus Deploy in which to run (i.e. Dev, Test, Prod). Each environment should be added on a new line. | +| `variables` | A multi-line list of prompted variable values. Format: name:value. | +| `server` | The instance URL hosting Octopus Deploy (i.e. ""). The instance URL is required, but you may also use the OCTOPUS_URL environment variable. | +| `api_key` | The API key used to access Octopus Deploy. An API key is required, but you may also use the OCTOPUS_API_KEY environment variable. It is strongly recommended that this value retrieved from a GitHub secret. | +| `space` | The name of a space within which this command will be executed. The space name is required, but you may also use the OCTOPUS_SPACE environment variable. | +| `git_ref` | Git branch reference to the specific resources of a version controlled Octopus Project. This is required for version controlled projects. E.g. ${{ github.ref }} to use the branch or tag ref that triggered the workflow. | ## 📤 Outputs diff --git a/action.yml b/action.yml index d2509f54..9e9fe8ab 100644 --- a/action.yml +++ b/action.yml @@ -30,6 +30,8 @@ inputs: description: 'The API key used to access Octopus Deploy. An API key is required, but you may also use the OCTOPUS_API_KEY environment variable. It is strongly recommended that this value retrieved from a GitHub secret.' space: description: 'The name of a space within which this command will be executed. The space name is required, but you may also use the OCTOPUS_SPACE environment variable.' + git_ref: + description: 'The Git Reference on which to run the Runbook. If not populate the action assumes you are running a DB Runbook. In most cases when running a Git Runbook this will be your default branch refs/heads/main' outputs: server_tasks: diff --git a/dist/index.js b/dist/index.js index 1005bc04..7ea97354 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2748,10 +2748,10 @@ var BuildInformationRepository = /** @class */ (function () { BuildInformationRepository.prototype.push = function (buildInformation, overwriteMode) { if (overwriteMode === void 0) { overwriteMode = overwriteMode_1.OverwriteMode.FailIfExists; } return __awaiter(this, void 0, void 0, function () { - var tasks, _a, _b, pkg; - var e_1, _c; - return __generator(this, function (_d) { - switch (_d.label) { + var tasks, _a, _b, pkg, rejectedTasks, completedTasks, completedTasks_1, completedTasks_1_1, t, errors, rejectedTasks_1, rejectedTasks_1_1, e, error; + var e_1, _c, e_2, _d, e_3, _e; + return __generator(this, function (_f) { + switch (_f.label) { case 0: tasks = []; try { @@ -2781,9 +2781,48 @@ var BuildInformationRepository = /** @class */ (function () { } finally { if (e_1) throw e_1.error; } } + rejectedTasks = []; return [4 /*yield*/, Promise.allSettled(tasks)]; case 1: - _d.sent(); + completedTasks = _f.sent(); + try { + for (completedTasks_1 = __values(completedTasks), completedTasks_1_1 = completedTasks_1.next(); !completedTasks_1_1.done; completedTasks_1_1 = completedTasks_1.next()) { + t = completedTasks_1_1.value; + if (t.status === "rejected") { + rejectedTasks.push(t.reason); + } + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (completedTasks_1_1 && !completedTasks_1_1.done && (_d = completedTasks_1.return)) _d.call(completedTasks_1); + } + finally { if (e_2) throw e_2.error; } + } + errors = []; + try { + for (rejectedTasks_1 = __values(rejectedTasks), rejectedTasks_1_1 = rejectedTasks_1.next(); !rejectedTasks_1_1.done; rejectedTasks_1_1 = rejectedTasks_1.next()) { + e = rejectedTasks_1_1.value; + if (e instanceof Error) { + errors.push(e); + } + else { + errors.push(new Error("unexpected error: ".concat(e))); + } + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (rejectedTasks_1_1 && !rejectedTasks_1_1.done && (_e = rejectedTasks_1.return)) _e.call(rejectedTasks_1); + } + finally { if (e_3) throw e_3.error; } + } + if (errors.length > 0) { + error = errors.map(function (e) { return "".concat(e); }); + throw new Error(error.join("\n")); + } return [2 /*return*/]; } }); @@ -3858,12 +3897,12 @@ var globp = (0, util_1.promisify)(glob_1.glob); * @param {boolean} overwrite Whether to overwrite the Zip file if it already exists. Defaults to true if not specified. */ function doZip(basePath, inputFilePatterns, outputFolder, zipFilename, logger, compressionLevel, overwrite) { - var _a, _b, _c; + var _a, _b, _c, _d; return __awaiter(this, void 0, void 0, function () { var archivePath, initialWorkingDirectory, zip, files, files_1, files_1_1, file, dirName; - var e_1, _d; - return __generator(this, function (_e) { - switch (_e.label) { + var e_1, _e; + return __generator(this, function (_f) { + switch (_f.label) { case 0: archivePath = path_1.default.resolve(outputFolder, zipFilename); (_a = logger.info) === null || _a === void 0 ? void 0 : _a.call(logger, "Writing to package: ".concat(archivePath, "...")); @@ -3872,7 +3911,7 @@ function doZip(basePath, inputFilePatterns, outputFolder, zipFilename, logger, c zip = new adm_zip_1.default(); return [4 /*yield*/, expandGlobs(inputFilePatterns)]; case 1: - files = _e.sent(); + files = _f.sent(); try { for (files_1 = __values(files), files_1_1 = files_1.next(); !files_1_1.done; files_1_1 = files_1.next()) { file = files_1_1.value; @@ -3889,7 +3928,7 @@ function doZip(basePath, inputFilePatterns, outputFolder, zipFilename, logger, c catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { - if (files_1_1 && !files_1_1.done && (_d = files_1.return)) _d.call(files_1); + if (files_1_1 && !files_1_1.done && (_e = files_1.return)) _e.call(files_1); } finally { if (e_1) throw e_1.error; } } @@ -3898,10 +3937,11 @@ function doZip(basePath, inputFilePatterns, outputFolder, zipFilename, logger, c } setCompressionLevel(zip, compressionLevel || 8); process.chdir(initialWorkingDirectory); - return [4 /*yield*/, zip.writeZipPromise(archivePath, { overwrite: overwrite || true })]; - case 2: - _e.sent(); - return [2 /*return*/]; + if (fs_1.default.existsSync(archivePath) && overwrite === false) { + (_d = logger.info) === null || _d === void 0 ? void 0 : _d.call(logger, "Found an existing archive at ".concat(archivePath, " and overwrite is disabled. The existing archive will not be overwritten.")); + return [2 /*return*/]; + } + return [2 /*return*/, zip.writeZip(archivePath, function () { })]; } }); }); @@ -5633,6 +5673,24 @@ var RunbookProcessRepository = /** @class */ (function () { }); }); }; + RunbookProcessRepository.prototype.getWithGitRef = function (runbook, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.request("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbookProcesses{/id}"), { + spaceName: this.spaceName, + projectId: this.projectId, + id: runbook.RunbookProcessId, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; RunbookProcessRepository.prototype.update = function (runbookProcess) { return __awaiter(this, void 0, void 0, function () { var response; @@ -5650,6 +5708,24 @@ var RunbookProcessRepository = /** @class */ (function () { }); }); }; + RunbookProcessRepository.prototype.updateWithGitRef = function (runbookProcess, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.doUpdate("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbookProcesses{/id}"), runbookProcess, { + spaceName: this.spaceName, + projectId: this.projectId, + id: runbookProcess.Id, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; return RunbookProcessRepository; }()); exports.RunbookProcessRepository = RunbookProcessRepository; @@ -5677,6 +5753,42 @@ var __extends = (this && this.__extends) || (function () { d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); +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 (g && (g = 0, op[0] && (_ = 0)), _) 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 }; + } +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RunbookRepository = void 0; var spaceScopedRoutePrefix_1 = __nccwpck_require__(7218); @@ -5684,8 +5796,81 @@ var spaceScopedBasicRepository_1 = __nccwpck_require__(3496); var RunbookRepository = /** @class */ (function (_super) { __extends(RunbookRepository, _super); function RunbookRepository(client, spaceName, project) { - return _super.call(this, client, spaceName, "".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/").concat(project.Id, "/runbooks"), "skip,take,ids,partialName") || this; + var _this = _super.call(this, client, spaceName, "".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/").concat(project.Id, "/runbooks"), "skip,take,ids,partialName") || this; + _this.projectId = project.Id; + return _this; } + RunbookRepository.prototype.getWithGitRef = function (slug, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.request("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbooks{/id}"), { + spaceName: this.spaceName, + projectId: this.projectId, + id: slug, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; + RunbookRepository.prototype.createWithGitRef = function (runbook, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.doCreate("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbooks/v2"), runbook, { + spaceName: this.spaceName, + projectId: this.projectId, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; + RunbookRepository.prototype.modifyWithGitRef = function (runbook, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.doUpdate("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbooks{/id}"), runbook, { + spaceName: this.spaceName, + projectId: this.projectId, + id: runbook.Id, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; + RunbookRepository.prototype.deleteWithGitRef = function (runbook, gitRef) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.client.del("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/projects/{projectId}/{gitRef}/runbooks{/id}"), { + spaceName: this.spaceName, + projectId: this.projectId, + id: runbook.Id, + gitRef: gitRef, + })]; + case 1: + response = _a.sent(); + return [2 /*return*/, response]; + } + }); + }); + }; return RunbookRepository; }(spaceScopedBasicRepository_1.SpaceScopedBasicRepository)); exports.RunbookRepository = RunbookRepository; @@ -5699,6 +5884,12 @@ exports.RunbookRepository = RunbookRepository; "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.RunbookRetentionUnit = void 0; +var RunbookRetentionUnit; +(function (RunbookRetentionUnit) { + RunbookRetentionUnit["Days"] = "Days"; + RunbookRetentionUnit["Items"] = "Items"; +})(RunbookRetentionUnit = exports.RunbookRetentionUnit || (exports.RunbookRetentionUnit = {})); /***/ }), @@ -5944,6 +6135,40 @@ var RunbookRunRepository = /** @class */ (function () { }); }); }; + RunbookRunRepository.prototype.createGit = function (command, gitRef) { + var _a, _b; + return __awaiter(this, void 0, void 0, function () { + var serverInformation, response, mappedTasks; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, this.client.getServerInformation()]; + case 1: + serverInformation = _c.sent(); + if ((0, semver_1.lt)(serverInformation.version, "2022.3.5512")) { + (_b = (_a = this.client).error) === null || _b === void 0 ? void 0 : _b.call(_a, "The Octopus instance doesn't support running runbooks using the Executions API, it will need to be upgraded to at least 2022.3.5512 in order to access this API."); + throw new Error("The Octopus instance doesn't support running runbooks using the Executions API, it will need to be upgraded to at least 2022.3.5512 in order to access this API."); + } + this.client.debug("Running a runbook..."); + return [4 /*yield*/, this.client.doCreate("".concat(spaceScopedRoutePrefix_1.spaceScopedRoutePrefix, "/runbook-runs/git/create/v1"), __assign({ spaceIdOrName: command.spaceName, gitRef: gitRef }, command))]; + case 2: + response = _c.sent(); + if (response.RunbookRunServerTasks.length == 0) { + throw new Error("No server task details returned"); + } + mappedTasks = response.RunbookRunServerTasks.map(function (x) { + return { + RunbookRunId: x.RunbookRunId || x.runbookRunId, + ServerTaskId: x.ServerTaskId || x.serverTaskId, + }; + }); + this.client.debug("Runbook executed successfully. [".concat(mappedTasks.map(function (t) { return t.ServerTaskId; }).join(", "), "]")); + return [2 /*return*/, { + RunbookRunServerTasks: mappedTasks, + }]; + } + }); + }); + }; return RunbookRunRepository; }()); exports.RunbookRunRepository = RunbookRunRepository; @@ -6160,30 +6385,31 @@ var ServerTaskWaiter = /** @class */ (function () { stop = true; }, timeout); completedTasks = []; + _a.label = 1; + case 1: + _a.trys.push([1, , 5, 6]); _loop_1 = function () { - var tasks_2, unknownTaskIds, nowCompletedTaskIds_1, tasks_1, tasks_1_1, task; + var tasks, unknownTaskIds, nowCompletedTaskIds, tasks_1, tasks_1_1, task; var e_1, _b; return __generator(this, function (_c) { switch (_c.label) { - case 0: - _c.trys.push([0, , 2, 3]); - return [4 /*yield*/, spaceServerTaskRepository.getByIds(serverTaskIds)]; + case 0: return [4 /*yield*/, spaceServerTaskRepository.getByIds(serverTaskIds)]; case 1: - tasks_2 = _c.sent(); - unknownTaskIds = serverTaskIds.filter(function (id) { return tasks_2.filter(function (t) { return t.Id === id; }).length == 0; }); + tasks = _c.sent(); + unknownTaskIds = serverTaskIds.filter(function (id) { return tasks.filter(function (t) { return t.Id === id; }).length == 0; }); if (unknownTaskIds.length) { throw new Error("Unknown task Id(s) ".concat(unknownTaskIds.join(", "))); } - nowCompletedTaskIds_1 = []; + nowCompletedTaskIds = []; try { - for (tasks_1 = (e_1 = void 0, __values(tasks_2)), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) { + for (tasks_1 = (e_1 = void 0, __values(tasks)), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) { task = tasks_1_1.value; if (pollingCallback) { pollingCallback(task); } // once the task is complete if (task.IsCompleted) { - nowCompletedTaskIds_1.push(task.Id); + nowCompletedTaskIds.push(task.Id); completedTasks.push(task); } } @@ -6196,30 +6422,31 @@ var ServerTaskWaiter = /** @class */ (function () { finally { if (e_1) throw e_1.error; } } // filter down the ids to only those that haven't completed for the next time around the loop - serverTaskIds = serverTaskIds.filter(function (id) { return nowCompletedTaskIds_1.indexOf(id) < 0; }); + serverTaskIds = serverTaskIds.filter(function (id) { return nowCompletedTaskIds.indexOf(id) < 0; }); // once all tasks have completed we can stop the loop - if (serverTaskIds.length === 0 || tasks_2.length === 0) { + if (serverTaskIds.length === 0 || tasks.length === 0) { stop = true; + clearTimeout(t); } - return [3 /*break*/, 3]; + return [4 /*yield*/, sleep(statusCheckSleepCycle)]; case 2: - clearTimeout(t); - return [7 /*endfinally*/]; - case 3: return [4 /*yield*/, sleep(statusCheckSleepCycle)]; - case 4: _c.sent(); return [2 /*return*/]; } }); }; - _a.label = 1; - case 1: - if (!!stop) return [3 /*break*/, 3]; - return [5 /*yield**/, _loop_1()]; + _a.label = 2; case 2: + if (!!stop) return [3 /*break*/, 4]; + return [5 /*yield**/, _loop_1()]; + case 3: _a.sent(); - return [3 /*break*/, 1]; - case 3: return [2 /*return*/, completedTasks]; + return [3 /*break*/, 2]; + case 4: return [3 /*break*/, 6]; + case 5: + clearTimeout(t); + return [7 /*endfinally*/]; + case 6: return [2 /*return*/, completedTasks]; } }); }); @@ -41744,6 +41971,35 @@ const api_client_1 = __nccwpck_require__(586); function runRunbookFromInputs(client, parameters) { return __awaiter(this, void 0, void 0, function* () { client.info('🐙 Running runbooks in Octopus Deploy...'); + if (parameters.gitRef) { + return yield runGitRunbookFromInputs(client, parameters); + } + return yield runDbRunbookFromInputs(client, parameters); + }); +} +exports.runRunbookFromInputs = runRunbookFromInputs; +function runGitRunbookFromInputs(client, parameters) { + return __awaiter(this, void 0, void 0, function* () { + const command = { + spaceName: parameters.space, + ProjectName: parameters.project, + RunbookName: parameters.runbook, + EnvironmentNames: parameters.environments, + Tenants: parameters.tenants, + TenantTags: parameters.tenantTags, + UseGuidedFailure: parameters.useGuidedFailure, + Variables: parameters.variables + }; + if (!parameters.gitRef) { + throw new Error('gitRef is required for running a Git runbook.'); + } + const runbookRunRepository = new api_client_1.RunbookRunRepository(client, parameters.space); + const response = yield runbookRunRepository.createGit(command, parameters.gitRef); + return yield parseResult(client, response, runbookRunRepository, parameters); + }); +} +function runDbRunbookFromInputs(client, parameters) { + return __awaiter(this, void 0, void 0, function* () { const command = { spaceName: parameters.space, ProjectName: parameters.project, @@ -41756,6 +42012,11 @@ function runRunbookFromInputs(client, parameters) { }; const runbookRunRepository = new api_client_1.RunbookRunRepository(client, parameters.space); const response = yield runbookRunRepository.create(command); + return yield parseResult(client, response, runbookRunRepository, parameters); + }); +} +function parseResult(client, response, runbookRunRepository, parameters) { + return __awaiter(this, void 0, void 0, function* () { client.info(`🎉 ${response.RunbookRunServerTasks.length} Runbook run${response.RunbookRunServerTasks.length > 1 ? 's' : ''} queued successfully!`); if (response.RunbookRunServerTasks.length === 0) { throw new Error('Expected at least one deployment to be queued.'); @@ -41788,7 +42049,6 @@ function runRunbookFromInputs(client, parameters) { return results; }); } -exports.runRunbookFromInputs = runRunbookFromInputs; /***/ }), @@ -41828,7 +42088,8 @@ function getInputParameters() { tenants: (0, core_1.getMultilineInput)('tenants').map(p => p.trim()) || undefined, tenantTags: (0, core_1.getMultilineInput)('tenant_tags').map(p => p.trim()) || undefined, useGuidedFailure: (0, core_1.getBooleanInput)('use_guided_failure') || undefined, - variables: variablesMap + variables: variablesMap, + gitRef: (0, core_1.getInput)('git_ref') || undefined }; const errors = []; if (!parameters.server) { diff --git a/package-lock.json b/package-lock.json index 6a002e59..39e33f2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@actions/core": "1.10.1", - "@octopusdeploy/api-client": "3.2.0" + "@octopusdeploy/api-client": "3.5.1" }, "devDependencies": { "@types/jest": "29.5.14", @@ -1358,9 +1358,9 @@ } }, "node_modules/@octopusdeploy/api-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@octopusdeploy/api-client/-/api-client-3.2.0.tgz", - "integrity": "sha512-0TrbsXaFc/Xi3yfsEEAvf5mOveZx4K347LmBRwCuLd2pLl8tj3NJYJn7nRzz3kr4j6o46oUv1RukemEXqu91Dg==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octopusdeploy/api-client/-/api-client-3.5.1.tgz", + "integrity": "sha512-JKPx+L1QNjtX1txpj+0+JJR3Q5D/IwKdocih8WTGXGYNYC8jdEDgaTkPb701gD/Jzu14foNn67W4CDDBvyPZeA==", "dependencies": { "adm-zip": "^0.5.9", "axios": "^1.2.1", @@ -7690,9 +7690,9 @@ } }, "@octopusdeploy/api-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@octopusdeploy/api-client/-/api-client-3.2.0.tgz", - "integrity": "sha512-0TrbsXaFc/Xi3yfsEEAvf5mOveZx4K347LmBRwCuLd2pLl8tj3NJYJn7nRzz3kr4j6o46oUv1RukemEXqu91Dg==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octopusdeploy/api-client/-/api-client-3.5.1.tgz", + "integrity": "sha512-JKPx+L1QNjtX1txpj+0+JJR3Q5D/IwKdocih8WTGXGYNYC8jdEDgaTkPb701gD/Jzu14foNn67W4CDDBvyPZeA==", "requires": { "adm-zip": "^0.5.9", "axios": "^1.2.1", diff --git a/package.json b/package.json index bdf04bb2..f203cf7c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@actions/core": "1.10.1", - "@octopusdeploy/api-client": "3.2.0" + "@octopusdeploy/api-client": "3.5.1" }, "description": "GitHub Action to Run a Runbook in Octopus Deploy", "devDependencies": { diff --git a/src/api-wrapper.ts b/src/api-wrapper.ts index 7cf7e357..50706141 100644 --- a/src/api-wrapper.ts +++ b/src/api-wrapper.ts @@ -1,3 +1,4 @@ +import { RunGitRunbookCommand } from '@octopusdeploy/api-client/dist/features/projects/runbooks/runs/RunGitRunbookCommand' import { InputParameters } from './input-parameters' import { Client, @@ -18,6 +19,37 @@ export interface RunbookRunResult { export async function runRunbookFromInputs(client: Client, parameters: InputParameters): Promise { client.info('🐙 Running runbooks in Octopus Deploy...') + if (parameters.gitRef) { + return await runGitRunbookFromInputs(client, parameters) + } + + return await runDbRunbookFromInputs(client, parameters) +} + +async function runGitRunbookFromInputs(client: Client, parameters: InputParameters): Promise { + const command: RunGitRunbookCommand = { + spaceName: parameters.space, + ProjectName: parameters.project, + RunbookName: parameters.runbook, + EnvironmentNames: parameters.environments, + Tenants: parameters.tenants, + TenantTags: parameters.tenantTags, + UseGuidedFailure: parameters.useGuidedFailure, + Variables: parameters.variables + } + + if (!parameters.gitRef) { + throw new Error('gitRef is required for running a Git runbook.') + } + + const runbookRunRepository = new RunbookRunRepository(client, parameters.space) + + const response = await runbookRunRepository.createGit(command, parameters.gitRef) + + return await parseResult(client, response, runbookRunRepository, parameters) +} + +async function runDbRunbookFromInputs(client: Client, parameters: InputParameters): Promise { const command: CreateRunbookRunCommandV1 = { spaceName: parameters.space, ProjectName: parameters.project, @@ -32,6 +64,22 @@ export async function runRunbookFromInputs(client: Client, parameters: InputPara const runbookRunRepository = new RunbookRunRepository(client, parameters.space) const response = await runbookRunRepository.create(command) + return await parseResult(client, response, runbookRunRepository, parameters) +} + +interface CreateRunbookRunResponse { + RunbookRunServerTasks: { + RunbookRunId: string + ServerTaskId: string + }[] +} + +async function parseResult( + client: Client, + response: CreateRunbookRunResponse, + runbookRunRepository: RunbookRunRepository, + parameters: InputParameters +): Promise { client.info( `🎉 ${response.RunbookRunServerTasks.length} Runbook run${ response.RunbookRunServerTasks.length > 1 ? 's' : '' diff --git a/src/input-parameters.ts b/src/input-parameters.ts index cb6155aa..f2bcdd0c 100644 --- a/src/input-parameters.ts +++ b/src/input-parameters.ts @@ -20,6 +20,7 @@ export interface InputParameters { apiKey?: string accessToken?: string space: string + gitRef?: string } export function getInputParameters(): InputParameters { @@ -44,7 +45,8 @@ export function getInputParameters(): InputParameters { tenants: getMultilineInput('tenants').map(p => p.trim()) || undefined, tenantTags: getMultilineInput('tenant_tags').map(p => p.trim()) || undefined, useGuidedFailure: getBooleanInput('use_guided_failure') || undefined, - variables: variablesMap + variables: variablesMap, + gitRef: getInput('git_ref') || undefined } const errors: string[] = []