Skip to content

Commit 48190c7

Browse files
authored
Add support for running Git runbooks (#338)
1 parent 1fed324 commit 48190c7

File tree

7 files changed

+102
-24
lines changed

7 files changed

+102
-24
lines changed

package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"yargs": "^17.5.1"
3939
},
4040
"dependencies": {
41-
"@octopusdeploy/api-client": "^3.4.1",
41+
"@octopusdeploy/api-client": "^3.5.1",
4242
"azure-devops-node-api": "11.2.0",
4343
"azure-pipelines-task-lib": "3.3.1",
4444
"azure-pipelines-tool-lib": "1.3.2",

source/tasks/RunRunbook/RunRunbookV6/inputCommandBuilder.test.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Logger } from "@octopusdeploy/api-client";
2-
import { createCommandFromInputs } from "./inputCommandBuilder";
2+
import { createCommandFromInputs, CreateGitRunbookRunCommandV1, isCreateGitRunbookRunCommand } from "./inputCommandBuilder";
33
import { MockTaskWrapper } from "../../Utils/MockTaskWrapper";
44

55
describe("getInputCommand", () => {
@@ -20,6 +20,7 @@ describe("getInputCommand", () => {
2020
task.addVariableString("Variables", "var1: value1\nvar2: value2");
2121

2222
const command = createCommandFromInputs(logger, task);
23+
expect(isCreateGitRunbookRunCommand(command)).toBe(false);
2324
expect(command.spaceName).toBe("Default");
2425
expect(command.ProjectName).toBe("Awesome project");
2526
expect(command.RunbookName).toBe("A runbook");
@@ -32,4 +33,30 @@ describe("getInputCommand", () => {
3233
expect(task.lastResultMessage).toBeUndefined();
3334
expect(task.lastResultDone).toBeUndefined();
3435
});
36+
37+
test("when gitRef is supplied, the command contains the ref plus all regular fields supplied", () => {
38+
task.addVariableString("Space", "Default");
39+
task.addVariableString("Project", "Awesome project");
40+
task.addVariableString("Runbook", "A runbook");
41+
task.addVariableString("Environments", "dev\nStaging");
42+
task.addVariableString("Tenants", "Tenant 1\nTenant 2");
43+
task.addVariableString("TenantTags", "tag set 1/tag 1\ntag set 1/tag 2");
44+
task.addVariableString("Variables", "var1: value1\nvar2: value2");
45+
task.addVariableString("GitRef", "some-ref");
46+
47+
const command = createCommandFromInputs(logger, task);
48+
expect(isCreateGitRunbookRunCommand(command)).toBe(true);
49+
expect(command.spaceName).toBe("Default");
50+
expect(command.ProjectName).toBe("Awesome project");
51+
expect(command.RunbookName).toBe("A runbook");
52+
expect(command.EnvironmentNames).toStrictEqual(["dev", "Staging"]);
53+
expect(command.Tenants).toStrictEqual(["Tenant 1", "Tenant 2"]);
54+
expect(command.TenantTags).toStrictEqual(["tag set 1/tag 1", "tag set 1/tag 2"]);
55+
expect(command.Variables).toStrictEqual({ var1: "value1", var2: "value2" });
56+
expect((command as CreateGitRunbookRunCommandV1).GitRef).toBe("some-ref");
57+
58+
expect(task.lastResult).toBeUndefined();
59+
expect(task.lastResultMessage).toBeUndefined();
60+
expect(task.lastResultDone).toBeUndefined();
61+
});
3562
});

source/tasks/RunRunbook/RunRunbookV6/inputCommandBuilder.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
import { getLineSeparatedItems } from "../../Utils/inputs";
22
import { CreateRunbookRunCommandV1, Logger, PromptedVariableValues } from "@octopusdeploy/api-client";
3+
import { RunGitRunbookCommand } from "@octopusdeploy/api-client/dist/features/projects/runbooks/runs/RunGitRunbookCommand";
34
import { TaskWrapper } from "tasks/Utils/taskInput";
45

5-
export function createCommandFromInputs(logger: Logger, task: TaskWrapper): CreateRunbookRunCommandV1 {
6+
// The api-client doesn't have a type for this command that we can differentiate from CreateRunbookRunCommandV1
7+
// so we'll wrap it to make things easier.
8+
export type CreateGitRunbookRunCommandV1 = RunGitRunbookCommand & {
9+
GitRef: string;
10+
};
11+
12+
export function isCreateGitRunbookRunCommand(command: CreateRunbookRunCommandV1 | CreateGitRunbookRunCommandV1): command is CreateGitRunbookRunCommandV1 {
13+
return (command as CreateGitRunbookRunCommandV1).GitRef !== undefined;
14+
}
15+
16+
export function createCommandFromInputs(logger: Logger, task: TaskWrapper): CreateRunbookRunCommandV1 | CreateGitRunbookRunCommandV1 {
617
const variablesMap: PromptedVariableValues | undefined = {};
718

819
const variablesField = task.getInput("Variables");
@@ -25,6 +36,27 @@ export function createCommandFromInputs(logger: Logger, task: TaskWrapper): Crea
2536
logger.debug?.("Tenant Tags: " + tagsField);
2637
const tags = getLineSeparatedItems(tagsField || "")?.map((t: string) => t.trim()) || [];
2738

39+
const gitRef = task.getInput("GitRef");
40+
logger.debug?.("GitRef: " + gitRef);
41+
42+
if (gitRef) {
43+
const command: CreateGitRunbookRunCommandV1 = {
44+
spaceName: task.getInput("Space") || "",
45+
ProjectName: task.getInput("Project", true) || "",
46+
RunbookName: task.getInput("Runbook", true) || "",
47+
EnvironmentNames: getLineSeparatedItems(environmentsField || "")?.map((t: string) => t.trim()) || [],
48+
Tenants: getLineSeparatedItems(tenantsField || "")?.map((t: string) => t.trim()) || [],
49+
TenantTags: tags,
50+
UseGuidedFailure: task.getBoolean("UseGuidedFailure") || undefined,
51+
Variables: variablesMap || undefined,
52+
GitRef: gitRef,
53+
};
54+
55+
logger.debug?.(JSON.stringify(command));
56+
57+
return command;
58+
}
59+
2860
const command: CreateRunbookRunCommandV1 = {
2961
spaceName: task.getInput("Space") || "",
3062
ProjectName: task.getInput("Project", true) || "",

source/tasks/RunRunbook/RunRunbookV6/runRunbook.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import { Client, CreateRunbookRunCommandV1, RunbookRunRepository, Logger, TenantRepository, EnvironmentRepository } from "@octopusdeploy/api-client";
1+
import { Client, CreateRunbookRunCommandV1, RunbookRunRepository, Logger, TenantRepository, EnvironmentRepository, CreateRunbookRunResponseV1 } from "@octopusdeploy/api-client";
22
import os from "os";
33
import { TaskWrapper } from "tasks/Utils/taskInput";
44
import { ExecutionResult } from "../../Utils/executionResult";
5+
import { CreateGitRunbookRunCommandV1, isCreateGitRunbookRunCommand } from "./inputCommandBuilder";
6+
import { RunGitRunbookResponse } from "@octopusdeploy/api-client/dist/features/projects/runbooks/runs/RunGitRunbookResponse";
57

6-
export async function createRunbookRunFromInputs(client: Client, command: CreateRunbookRunCommandV1, task: TaskWrapper, logger: Logger): Promise<ExecutionResult[]> {
8+
export async function createRunbookRunFromInputs(client: Client, command: CreateRunbookRunCommandV1 | CreateGitRunbookRunCommandV1, task: TaskWrapper, logger: Logger): Promise<ExecutionResult[]> {
79
logger.info?.("🐙 Running a Runbook in Octopus Deploy...");
810

911
try {
1012
const repository = new RunbookRunRepository(client, command.spaceName);
11-
const response = await repository.create(command);
13+
14+
const response = await createRunbookRun(repository, command);
1215

1316
logger.info?.(`🎉 ${response.RunbookRunServerTasks.length} Run${response.RunbookRunServerTasks.length > 1 ? "s" : ""} queued successfully!`);
1417

@@ -58,3 +61,10 @@ export async function createRunbookRunFromInputs(client: Client, command: Create
5861
throw error;
5962
}
6063
}
64+
65+
async function createRunbookRun(repository: RunbookRunRepository, command: CreateRunbookRunCommandV1 | CreateGitRunbookRunCommandV1): Promise<CreateRunbookRunResponseV1 | RunGitRunbookResponse> {
66+
if (isCreateGitRunbookRunCommand(command)) {
67+
return await repository.createGit(command, command.GitRef);
68+
}
69+
return await repository.create(command);
70+
}

source/tasks/RunRunbook/RunRunbookV6/task.json

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@
5555
"required": true,
5656
"helpMarkDown": "The environment names to run the runbook for."
5757
},
58+
{
59+
"name": "GitRef",
60+
"type": "string",
61+
"label": "Git Reference",
62+
"defaultValue": "",
63+
"required": false,
64+
"helpMarkDown": "The Git reference to run the runbook for. Only applies when runbooks are stored in a Git repository for config-as-code enabled projects."
65+
},
5866
{
5967
"name": "Tenants",
6068
"type": "multiLine",

source/vsts.md

+12-11
Original file line numberDiff line numberDiff line change
@@ -273,17 +273,18 @@ From version 6, the deploy release step is split into two seperate functions for
273273

274274
#### 📥 Inputs
275275

276-
| Name | Description |
277-
| :------------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
278-
| `OctoConnectedServiceName` | **Required.** Name of the Octopus Server connection. |
279-
| `Space` | **Required.** The Octopus space name the release is in. |
280-
| `Project` | **Required.** The Octopus project name to deploy. |
281-
| `Runbook` | **Required.** Runbook name to run. |
282-
| `Environments` | **Required.** The environment names to run the runbook for. One tenant name per line. |
283-
| `Tenants` | The tenant names to run the runbook for. One tenant name per line. |
284-
| `TenantTags` | Run for all tenants with the given tag(s). One tenant tag per line in the format `tag set name/tag name`. |
285-
| `Variables` | List of prompted variable values, one variable-value pair per line. Each variable should be in format `variable name: value` |
286-
| `UseGuidedFailure` | Whether to use guided failure mode if errors occur during the run. |
276+
| Name | Description |
277+
| :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
278+
| `OctoConnectedServiceName` | **Required.** Name of the Octopus Server connection. |
279+
| `Space` | **Required.** The Octopus space name the release is in. |
280+
| `Project` | **Required.** The Octopus project name to deploy. |
281+
| `Runbook` | **Required.** Runbook name to run. |
282+
| `Environments` | **Required.** The environment names to run the runbook for. One environment name per line. |
283+
| `GitRef` | The Git reference to run the runbook for e.g. `main`. Only applies when runbooks are stored in a Git repository for config-as-code enabled projects. |
284+
| `Tenants` | The tenant names to run the runbook for. One tenant name per line. |
285+
| `TenantTags` | Run for all tenants with the given tag(s). One tenant tag per line in the format `tag set name/tag name`. |
286+
| `Variables` | List of prompted variable values, one variable-value pair per line. Each variable should be in format `variable name: value` |
287+
| `UseGuidedFailure` | Whether to use guided failure mode if errors occur during the run. |
287288

288289
#### 📤 Outputs
289290

0 commit comments

Comments
 (0)