Skip to content

Commit 9cc4221

Browse files
committed
Moved files back to src directory, still working on readme
1 parent 9c07e15 commit 9cc4221

16 files changed

Lines changed: 268 additions & 18 deletions

File tree

README.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,134 @@ The directory structure of a step package project is shown below:
4141
* `package-lock.json` - The [specific package versions](https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json) to be retrieved by npm.
4242
* `tsconfig.json` - The [TypeScript compiler options file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
4343

44+
## Creating a new target
45+
46+
Creating a new target involves creating the following files under the `steps/<target-name>-target/src` directory. In the case of this sample step package, we'll create them under `steps/hello-world-target/src`:
47+
48+
* `metadata.json`
49+
* `inputs.ts`
50+
* `executor.js`
51+
* `ui.ts`
52+
* `validation.ts`
53+
54+
### `metadata.json`
55+
56+
The `metadata.json` provides details about the target. A sample is shown below:
57+
58+
```json
59+
{
60+
"schemaVersion": "1.0.0",
61+
"version": "0.0.0",
62+
"type": "deployment-target",
63+
"id": "hello-world-target",
64+
"name": "Hello world target",
65+
"description": "An example target that does nothing useful",
66+
"categories": [
67+
"Linux"
68+
],
69+
"launcher": "node"
70+
}
71+
```
72+
73+
* `schemaVersion` is the version of the metadata file. `1.0.0` is the only version available.
74+
* `version` is the version of the target. Versioning is covered in detail [here](https://github.com/OctopusDeploy/Architecture/blob/main/Steps/Concepts/Versioning.md).
75+
* `type` defines the type of resource tobe created. It must be `deployment-target` for a target.
76+
* `id` is the resource ID.
77+
* `name` is the name of the target displayed by the Octopus web UI.
78+
* `description` is the description of the target displayed by the Octopus UI.
79+
* `categories` is an array containing one or more step categories display by the Octopus UI where the target will be listed.
80+
* `launcher` defines how the step is executed. A value of `node` means the step is executed by Node.js.
81+
82+
### `inputs.ts`
83+
84+
The `inputs.ts` file exports an interface defining the input fields required by the target. This interface is consumed by both `executor.ts` to read the values when performing the target's health check, and `ui.ts` to build up the form exposed in the Octopus web UI.
85+
86+
An example is shown below exposing a single string field:
87+
88+
```typescript
89+
export default interface HelloWorldTargetInputs {
90+
greetingPrefix: string;
91+
}
92+
```
93+
94+
### `executor.ts`
95+
96+
Targets perform a health check to validate their inputs and check the state of the system they represent. This health check is performed by the function exposed by the `executor.ts` file.
97+
98+
The example below prints some text to the log during a health check, and will always pass, meaning the target is always healthy:
99+
100+
```typescript
101+
import { Handler } from "@octopusdeploy/step-api";
102+
import HelloWorldTargetInputs from "./inputs";
103+
104+
const HelloWorldDeploymentTargetHealthCheckExecutor: Handler<HelloWorldTargetInputs> = async (_, context) => {
105+
context.print("Hello world target is healthy");
106+
};
107+
108+
export default HelloWorldDeploymentTargetHealthCheckExecutor;
109+
```
110+
111+
### `ui.ts`
112+
113+
The form to be exposed by the Octopus web UI is defined in the `ui.ts` file. The form is defined as an instance of the `DeploymentTargetUI` interface, which has two functions: `createInitialInputs` and `editInputsForm`.
114+
115+
The `createInitialInputs` function allows the initial default field values to be defined.
116+
117+
The `editInputsForm` function provides a DSL for building the user interface. The first parameter is the inputs defined in `inputs.ts`. The second parameter is an instance of `AvailableStepComponents`, which has factory functions for creating various input widgets like text fields, lists, radio buttons etc.
118+
119+
Here we define the initial value of the `greetingPrefix` input to be `Hello`, and build the form with a single `text` input:
120+
121+
```typescript
122+
import { DeploymentTargetUI } from "@octopusdeploy/step-api";
123+
import HelloWorldTargetInputs from "./inputs";
124+
125+
export const HelloWorldTargetUI: DeploymentTargetUI<HelloWorldTargetInputs> = {
126+
createInitialInputs: () => {
127+
return {
128+
greetingPrefix: "Hello"
129+
};
130+
},
131+
editInputsForm: (inputs, { section, text }) => [
132+
section({
133+
title: "Greeting",
134+
content: [
135+
text({
136+
input: inputs.greetingPrefix,
137+
label: "Greeting Prefix",
138+
helpText: "The beginning of the greeting",
139+
}),
140+
],
141+
}),
142+
],
143+
};
144+
145+
export default HelloWorldTargetUI;
146+
```
147+
148+
### `validation.ts`
149+
150+
Form validation is defined in the `validate.ts` file. This file exports a function the returns an array of `ValueValidator` objects, and takes two parameters:
151+
1. The step inputs as [input paths](https://github.com/OctopusDeploy/Architecture/blob/main/Steps/Concepts/InputsAndOutputs.md#input-paths).
152+
2. A validation function that returns a `ValueValidator` and takes two parameters:
153+
1. An input path.
154+
2. A function the returns a string containing the error code (or returns nothing if there is no validation error) and provides the input value as the first parameter.
155+
156+
Here is an example:
157+
158+
```typescript
159+
import { ValidateInputs } from "@octopusdeploy/step-api";
160+
import HelloWorldTargetInputs from "./inputs";
161+
162+
const validateInputs: ValidateInputs<HelloWorldTargetInputs> = (inputs, validate) => {
163+
return [
164+
validate(inputs.greetingPrefix, (greeting) => {
165+
if (greeting === "") return "Greeting can not be empty";
166+
}),
167+
];
168+
};
169+
export default validateInputs;
170+
```
171+
44172
## Creating a new step
45173

46174
Creating a new step involves creating the following files under the `steps/<step-name>/src` directory. In the case of this sample step package, we'll create them under `steps/hello-world/src`:
@@ -53,3 +181,118 @@ Creating a new step involves creating the following files under the `steps/<step
53181

54182
### `metadata.json`
55183

184+
The `metadata.json` provides details about the step. A sample is shown below:
185+
186+
```json
187+
{
188+
"schemaVersion": "1.0.0",
189+
"version": "0.0.0",
190+
"type": "step",
191+
"id": "hello-world-upload",
192+
"name": "Hello World",
193+
"description": "An example step",
194+
"categories": [
195+
"BuiltInStep"
196+
],
197+
"canRunOnDeploymentTarget": false,
198+
"launcher": "node"
199+
}
200+
```
201+
202+
The following fields make up this file:
203+
204+
* `schemaVersion` is the version of the metadata file. `1.0.0` is the only version available.
205+
* `version` is the version of the step. Versioning is covered in detail [here](https://github.com/OctopusDeploy/Architecture/blob/main/Steps/Concepts/Versioning.md).
206+
* `type` defines the type of resource tobe created. Valid values are `step` and `deployment-target`.
207+
* `id` is the resource ID.
208+
* `name` is the name of the step displayed by the Octopus web UI.
209+
* `description` is the description of the step displayed by the Octopus UI.
210+
* `categories` is an array containing one or more step categories display by the Octopus UI where the step will be listed.
211+
* `canRunOnDeploymentTarget` can be set to `true` to run the step on a target accessible via a tentacle, or `false` to be run on a worker.
212+
* `launcher` defines how the step is executed. A value of `node` means the step is executed by Node.js.
213+
214+
### `inputs.ts`
215+
216+
This file performs the same function as it does when defining a new target.
217+
218+
An example is shown below exposing a single string field:
219+
220+
```typescript
221+
export default interface HelloWorldStepInputs {
222+
name: string;
223+
}
224+
```
225+
226+
### `executor.ts`
227+
228+
The `executor.ts` file contains the logic to be executed when the step is run as part of a deployment process.
229+
230+
Unlike the function returned by the target `executor.ts` function, a step `executor.ts` function can receive inputs from both the step and the target. The combination of the inputs defined by these two resources allows a step executor to perform a common action on multiple targets.
231+
232+
For this example we print the greeting defined in the target and the name defined on the step to the output log:
233+
234+
```typescript
235+
import HelloWorldStepInputs from "./inputs";
236+
import {ExecutionInputs, Handler, OctopusContext, TargetInputs} from "@octopusdeploy/step-api";
237+
import HelloWorldTargetInputs from "../../targets/hello-world-target/inputs";
238+
239+
const HelloWorldStepExecutor: Handler<HelloWorldStepInputs, HelloWorldTargetInputs> = async (
240+
inputs: ExecutionInputs<HelloWorldStepInputs>,
241+
target: TargetInputs<HelloWorldTargetInputs>,
242+
context: OctopusContext
243+
) => {
244+
context.print(target.greetingPrefix + " " + inputs.name);
245+
};
246+
247+
export default HelloWorldStepExecutor;
248+
```
249+
250+
### `ui.ts`
251+
252+
The step form to be displayed by the Octopus web UI is defined the same was it was with the target. There is one small difference though, where the first parameter to the `createInitialInputs` function can be an instance of `InitialInputFactories`, which provides the ability to create blank package references.
253+
254+
Here we define the initial value of the `name` input to be a blank string, and build the form with a single `text` input:
255+
256+
```typescript
257+
import { StepUI } from "@octopusdeploy/step-api";
258+
import HelloWorldStepInputs from "./inputs";
259+
260+
export const HelloWorldStepUI: StepUI<HelloWorldStepInputs> = {
261+
createInitialInputs: () => {
262+
return {
263+
name: ""
264+
};
265+
},
266+
editInputsForm: (inputs, availableComponents) => {
267+
const { text } = availableComponents;
268+
return [
269+
text({
270+
input: inputs.name,
271+
label: "Greeting Name",
272+
helpText: `The name of the person to greet.`,
273+
}),
274+
275+
];
276+
},
277+
};
278+
279+
export default HelloWorldStepUI;
280+
```
281+
282+
### `validation.ts`
283+
284+
Step form validation is identical to target form validation:
285+
286+
```typescript
287+
import { ValidateInputs } from "@octopusdeploy/step-api";
288+
import HelloWorldStepInputs from "./inputs";
289+
290+
const validateInputs: ValidateInputs<HelloWorldStepInputs> = (inputs, validate) => {
291+
return [
292+
validate(inputs.name, (name) => {
293+
if (name === "") return "Name can not be empty";
294+
}),
295+
];
296+
};
297+
export default validateInputs;
298+
```
File renamed without changes.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import HelloWorldStepInputs from "./inputs";
22
import {ExecutionInputs, Handler, OctopusContext, TargetInputs} from "@octopusdeploy/step-api";
3-
import HelloWorldTargetInputs from "../../targets/hello-world-target/inputs";
3+
import HelloWorldTargetInputs from "../../../targets/hello-world-target/src/inputs";
44

55
const HelloWorldStepExecutor: Handler<HelloWorldStepInputs, HelloWorldTargetInputs> = async (
66
inputs: ExecutionInputs<HelloWorldStepInputs>,
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,4 @@ export const HelloWorldStepUI: StepUI<HelloWorldStepInputs> = {
2020
},
2121
};
2222

23-
2423
export default HelloWorldStepUI;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ValidateInputs } from "@octopusdeploy/step-api";
2+
import HelloWorldStepInputs from "./inputs";
3+
4+
const validateInputs: ValidateInputs<HelloWorldStepInputs> = (inputs, validate) => {
5+
return [
6+
validate(inputs.name, (name) => {
7+
if (name === "") return "Name can not be empty";
8+
}),
9+
];
10+
};
11+
export default validateInputs;

steps/hello-world/validation.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Handler } from "@octopusdeploy/step-api";
22
import HelloWorldTargetInputs from "./inputs";
33

4-
const HelloWorldDeploymentTargetHealthCheckExecutor: Handler<HelloWorldTargetInputs> = async () => {
5-
// TODO: implement this
4+
const HelloWorldDeploymentTargetHealthCheckExecutor: Handler<HelloWorldTargetInputs> = async (_, context) => {
5+
context.print("Hello world target is healthy");
66
};
77

88
export default HelloWorldDeploymentTargetHealthCheckExecutor;

0 commit comments

Comments
 (0)