@catapultjs/deploy 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +87 -6
- package/build/commands/list_releases.d.ts +1 -0
- package/build/commands/list_releases.js +23 -1
- package/build/commands/list_releases.js.map +1 -1
- package/build/commands/list_revisions.d.ts +1 -0
- package/build/commands/list_revisions.js +37 -20
- package/build/commands/list_revisions.js.map +1 -1
- package/build/commands/list_tasks.d.ts +1 -0
- package/build/commands/list_tasks.js +19 -1
- package/build/commands/list_tasks.js.map +1 -1
- package/build/commands/pipeline.d.ts +1 -0
- package/build/commands/pipeline.js +18 -1
- package/build/commands/pipeline.js.map +1 -1
- package/build/commands/status.d.ts +2 -0
- package/build/commands/status.js +60 -50
- package/build/commands/status.js.map +1 -1
- package/build/commands/version.d.ts +1 -0
- package/build/commands/version.js +21 -4
- package/build/commands/version.js.map +1 -1
- package/build/index.d.ts +3 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/build/recipes/pm2.js +2 -2
- package/build/recipes/pm2.js.map +1 -1
- package/build/src/api.d.ts +60 -0
- package/build/src/api.js +159 -0
- package/build/src/api.js.map +1 -0
- package/build/src/base_command.d.ts +3 -1
- package/build/src/base_command.js +3 -1
- package/build/src/base_command.js.map +1 -1
- package/build/src/deployer.d.ts +7 -1
- package/build/src/deployer.js +19 -1
- package/build/src/deployer.js.map +1 -1
- package/build/src/pipeline/hooks.d.ts +4 -2
- package/build/src/pipeline/hooks.js.map +1 -1
- package/build/src/pipeline.d.ts +3 -3
- package/build/src/pipeline.js.map +1 -1
- package/build/src/status.d.ts +18 -0
- package/build/src/status.js +59 -0
- package/build/src/status.js.map +1 -0
- package/build/src/task/store.d.ts +4 -1
- package/build/src/task/store.js +8 -2
- package/build/src/task/store.js.map +1 -1
- package/build/src/task.d.ts +4 -1
- package/build/src/task.js +2 -2
- package/build/src/task.js.map +1 -1
- package/package.json +12 -7
- package/skills/catapultjs/SKILL.md +27 -0
- package/skills/catapultjs/references/api.md +70 -0
- package/skills/catapultjs/references/cli.md +160 -0
- package/skills/catapultjs/references/config.md +166 -0
- package/skills/catapultjs/references/github-actions.md +99 -0
- package/skills/catapultjs/references/recipe.md +115 -0
- package/skills/catapultjs/references/recipes.md +316 -0
package/build/src/task/store.js
CHANGED
|
@@ -27,12 +27,18 @@ export class TaskStore {
|
|
|
27
27
|
getDescription(name) {
|
|
28
28
|
return this.#descriptions.get(name) ?? '';
|
|
29
29
|
}
|
|
30
|
-
async run(name, deployCtx, host) {
|
|
30
|
+
async run(name, deployCtx, host, options = {}) {
|
|
31
31
|
const fn = this.#registry.get(name);
|
|
32
32
|
if (!fn)
|
|
33
33
|
throw new Error(`Task not found: "${name}"`);
|
|
34
34
|
const paths = getPaths(host.deployPath, deployCtx.release);
|
|
35
|
-
const ctx = {
|
|
35
|
+
const ctx = {
|
|
36
|
+
host,
|
|
37
|
+
paths,
|
|
38
|
+
config: deployCtx.config,
|
|
39
|
+
release: deployCtx.release,
|
|
40
|
+
logger: options.logger ?? logger,
|
|
41
|
+
};
|
|
36
42
|
this.#runner.set(ctx);
|
|
37
43
|
try {
|
|
38
44
|
const result = fn(ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/task/store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/task/store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAA;AAE1D,MAAM,OAAO,SAAS;IACpB,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAA;IACrC,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAA;IACzC,mBAAmB,GAAkB,IAAI,CAAA;IACzC,OAAO,CAAY;IAEnB,YAAY,MAAkB;QAC5B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,QAAQ,CAAC,WAAmB;QAC1B,IAAI,CAAC,mBAAmB,GAAG,WAAW,CAAA;IACxC,CAAC;IAED,QAAQ,CAAC,IAAc,EAAE,EAAU;QACjC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAC5B,IAAI,IAAI,CAAC,mBAAmB,KAAK,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAA;YACtD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAA;QACjC,CAAC;IACH,CAAC;IAED,GAAG,CAAC,IAAc;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACjC,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAA;IACnC,CAAC;IAED,cAAc,CAAC,IAAc;QAC3B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3C,CAAC;IAED,KAAK,CAAC,GAAG,CACP,IAAc,EACd,SAAwB,EACxB,IAAU,EACV,UAAuC,EAAE;QAEzC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACnC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,GAAG,CAAC,CAAA;QAErD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;QAC1D,MAAM,GAAG,GAAG;YACV,IAAI;YACJ,KAAK;YACL,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM;SACjC,CAAA;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAErB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;YACtB,IAAI,MAAM,YAAY,OAAO;gBAAE,MAAM,MAAM,CAAA;YAC3C,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC5B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QACtB,CAAC;IACH,CAAC;CACF"}
|
package/build/src/task.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Host, DeployContext, TaskName } from './types.ts';
|
|
2
|
+
import type { CatapultLogger } from './logger.ts';
|
|
2
3
|
import { type TaskFn } from './task/runner.ts';
|
|
3
4
|
import type { Verbose } from './enums.ts';
|
|
4
5
|
export type { TaskContext } from './task/runner.ts';
|
|
@@ -7,7 +8,9 @@ export declare function task(name: TaskName, fn: TaskFn): void;
|
|
|
7
8
|
export declare function hasTask(name: TaskName): boolean;
|
|
8
9
|
export declare function getTasks(): string[];
|
|
9
10
|
export declare function getTaskDescription(name: TaskName): string;
|
|
10
|
-
export declare function runTask(name: TaskName, deployCtx: DeployContext, host: Host
|
|
11
|
+
export declare function runTask(name: TaskName, deployCtx: DeployContext, host: Host, options?: {
|
|
12
|
+
logger?: CatapultLogger;
|
|
13
|
+
}): Promise<void>;
|
|
11
14
|
export declare function cd(path: string): void;
|
|
12
15
|
export declare function run(command: string): void;
|
|
13
16
|
export declare function bin(name: string): string;
|
package/build/src/task.js
CHANGED
|
@@ -18,8 +18,8 @@ export function getTasks() {
|
|
|
18
18
|
export function getTaskDescription(name) {
|
|
19
19
|
return store.getDescription(name);
|
|
20
20
|
}
|
|
21
|
-
export async function runTask(name, deployCtx, host) {
|
|
22
|
-
return store.run(name, deployCtx, host);
|
|
21
|
+
export async function runTask(name, deployCtx, host, options = {}) {
|
|
22
|
+
return store.run(name, deployCtx, host, options);
|
|
23
23
|
}
|
|
24
24
|
export function cd(path) {
|
|
25
25
|
runner.push(`cd ${q(runner.resolve(path))}`);
|
package/build/src/task.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/task.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAA;AAI9B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAA;AAC/B,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAA;AAEnC,MAAM,UAAU,IAAI,CAAC,WAAmB;IACtC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAc,EAAE,EAAU;IAC7C,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAc;IAC/C,OAAO,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAc,EACd,SAAwB,EACxB,IAAU,EACV,UAAuC,EAAE;IAEzC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AAClD,CAAC;AAMD,MAAM,UAAU,EAAE,CAAC,IAAY;IAC7B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAA;AAC9C,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe,EAAE,UAA4B,EAAE;IACnE,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,UAAkB;IAC1D,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAkB,EAAE,SAAiB;IAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;AAC/C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catapultjs/deploy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"build",
|
|
17
|
+
"skills",
|
|
17
18
|
"!build/bin/test.js",
|
|
18
19
|
"!build/bin/test.d.ts",
|
|
19
20
|
"!build/bin/test.js.map",
|
|
@@ -64,21 +65,25 @@
|
|
|
64
65
|
"types": "./build/src/utils.d.ts",
|
|
65
66
|
"import": "./build/src/utils.js"
|
|
66
67
|
},
|
|
68
|
+
"./api": {
|
|
69
|
+
"types": "./build/src/api.d.ts",
|
|
70
|
+
"import": "./build/src/api.js"
|
|
71
|
+
},
|
|
67
72
|
"./recipes/*": {
|
|
68
73
|
"types": "./build/recipes/*.d.ts",
|
|
69
74
|
"import": "./build/recipes/*.js"
|
|
70
75
|
}
|
|
71
76
|
},
|
|
72
77
|
"devDependencies": {
|
|
73
|
-
"@adonisjs/eslint-config": "^3.
|
|
74
|
-
"@adonisjs/prettier-config": "^1.
|
|
78
|
+
"@adonisjs/eslint-config": "^3.1.0",
|
|
79
|
+
"@adonisjs/prettier-config": "^1.5.0",
|
|
75
80
|
"@adonisjs/tsconfig": "^2.0.0",
|
|
76
81
|
"@japa/assert": "^4.2.0",
|
|
77
82
|
"@japa/runner": "^5.3.0",
|
|
78
|
-
"@types/node": "^25.
|
|
79
|
-
"eslint": "^10.
|
|
80
|
-
"prettier": "^3.8.
|
|
81
|
-
"typescript": "^6.0.
|
|
83
|
+
"@types/node": "^25.9.3",
|
|
84
|
+
"eslint": "^10.4.1",
|
|
85
|
+
"prettier": "^3.8.4",
|
|
86
|
+
"typescript": "^6.0.3",
|
|
82
87
|
"vitepress": "^2.0.0-alpha.17"
|
|
83
88
|
},
|
|
84
89
|
"dependencies": {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catapultjs
|
|
3
|
+
description: Work with Catapult (@catapultjs/deploy), the SSH deployment tool for Node.js. Use when creating or updating a deploy.ts config, writing recipes or deployment tasks, customizing the deploy pipeline, or scripting deployments with the programmatic API (Catapult class).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Catapult (@catapultjs/deploy)
|
|
7
|
+
|
|
8
|
+
Capistrano-style SSH deployment for Node.js: versioned releases under `releases/`, an atomic `current` symlink, shared directories, a composable task pipeline, automatic rollback. The CLI is `cata`. Docs: https://catapultjs.com
|
|
9
|
+
|
|
10
|
+
## Critical rule
|
|
11
|
+
|
|
12
|
+
`defineConfig()` does not impose a deployment mode: exactly one recipe (or a custom task) must provide the `deploy:update_code` pipeline task. Providers: `recipes/git`, `recipes/rsync`, `recipes/adonisjs_local`, `recipes/astro`, `recipes/vitepress`, or your own `task('deploy:update_code', …)`. A config without one fails at deploy time.
|
|
13
|
+
|
|
14
|
+
## References
|
|
15
|
+
|
|
16
|
+
Read the file matching the task before writing code:
|
|
17
|
+
|
|
18
|
+
| Task | Read |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| Create or update `deploy.ts`: hosts, recipe selection, healthchecks, lifecycle hooks, pipeline tuning, `setPipeline()` | [references/config.md](references/config.md) |
|
|
21
|
+
| Write or modify a recipe or deployment task: task DSL, `onSetup`/`onStatus` hooks, store options, `TaskRegistry` | [references/recipe.md](references/recipe.md) |
|
|
22
|
+
| Use an official recipe (git, rsync, astro, vitepress, adonisjs, nuxt, directus, pm2, redis): tasks added, store keys, monorepo patterns | [references/recipes.md](references/recipes.md) |
|
|
23
|
+
| Script deployments from Node.js (scripts, CI, bots, dashboards) with the `Catapult` class | [references/api.md](references/api.md) |
|
|
24
|
+
| Set up a GitHub Actions workflow with `catapultjs/deploy-action`: inputs, SSH secrets, env vars | [references/github-actions.md](references/github-actions.md) |
|
|
25
|
+
| Use the `cata` CLI: deploy, rollback, task, status, run, ssh, list commands and their options | [references/cli.md](references/cli.md) |
|
|
26
|
+
|
|
27
|
+
When a change spans several areas (e.g. a config plus a custom recipe), read every matching reference.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Using the Catapult programmatic API
|
|
2
|
+
|
|
3
|
+
The `Catapult` class exposes the same operations as the `cata` CLI, without any interactive prompt. Full docs: https://catapultjs.com/guide/programmatic
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Catapult } from '@catapultjs/deploy'
|
|
7
|
+
import '@catapultjs/deploy/recipes/git'
|
|
8
|
+
import '@catapultjs/deploy/recipes/pm2'
|
|
9
|
+
|
|
10
|
+
const catapult = new Catapult({
|
|
11
|
+
hosts: [{ name: 'production', ssh: 'deploy@example.com', deployPath: '/home/deploy/myapp' }],
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
await catapult.deploy()
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Rules that differ from a `deploy.ts`:
|
|
18
|
+
|
|
19
|
+
- Import recipes **before** constructing, exactly like in a config file.
|
|
20
|
+
- The constructor takes the same object as `defineConfig()`: `hosts`, `keepReleases`, `repository`, `packageManager`, `hooks`, `verbose`.
|
|
21
|
+
- One `Catapult` instance per process. The deploy context is global; a second instance with a different config is not supported.
|
|
22
|
+
- No prompts: methods either act or throw. Catch errors instead of expecting confirmation flows.
|
|
23
|
+
|
|
24
|
+
## Methods
|
|
25
|
+
|
|
26
|
+
All server-targeting methods accept `{ hosts?: string[] }` (default: all configured hosts; unknown names throw before anything runs).
|
|
27
|
+
|
|
28
|
+
| Method | Returns | CLI equivalent |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `setup(options?)` | `void` | `cata deploy:setup` |
|
|
31
|
+
| `deploy(options?)` | `void` | `cata deploy` |
|
|
32
|
+
| `rollback({ hosts?, release? })` | `void` | `cata rollback` |
|
|
33
|
+
| `task(name, options?)` | `TaskOutput[]` (`{ host, output }`) | `cata task <name>` |
|
|
34
|
+
| `status(options?)` | `HostStatus[]` | `cata status --json` |
|
|
35
|
+
| `listReleases(options?)` | `{ name, current, releases }[]` | `cata list:releases --json` |
|
|
36
|
+
| `listRevisions({ hosts?, limit? })` | `{ name, revisions }[]` | `cata list:revisions --json` |
|
|
37
|
+
| `listTasks()` | `{ pipeline, extra }` of `{ name, description }` | `cata list:tasks --json` |
|
|
38
|
+
| `pipeline()` | `string[]` | `cata pipeline --json` |
|
|
39
|
+
|
|
40
|
+
Behavior notes:
|
|
41
|
+
|
|
42
|
+
- `deploy()` generates a fresh release name on every call, runs the full pipeline, applies the CLI's auto-rollback on failure, then rethrows. Hosts must be initialized first (`setup()`), otherwise it throws.
|
|
43
|
+
- `task()` runs against the **currently deployed release** and returns what the task wrote through its `logger`, one entry per host. Display tasks (`pm2:logs`, `pm2:list`, …) report their result this way; output may contain ANSI codes. Tasks built from queued `run()` commands return an empty `output`.
|
|
44
|
+
- `status()` never throws on SSH failure; the affected host entry gets an `error` field instead. Data returned by recipes' `onStatus` hooks is merged into each host entry.
|
|
45
|
+
|
|
46
|
+
## Events
|
|
47
|
+
|
|
48
|
+
`Catapult` is an `EventEmitter`. `deploy()` and `task()` emit:
|
|
49
|
+
|
|
50
|
+
- `task:start`, `task:done` — `{ host, task }`
|
|
51
|
+
- `task:error` — `{ host, task, error }`
|
|
52
|
+
- `host:done` — `{ host, release }` (deploy only)
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
catapult.on('host:done', ({ host, release }) => notify(`${host} → ${release}`))
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Silent mode
|
|
59
|
+
|
|
60
|
+
By default Catapult logs progress to stdout like the CLI. For programs that rely on events and return values only:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { Verbose } from '@catapultjs/deploy/enums'
|
|
64
|
+
|
|
65
|
+
const catapult = new Catapult({ verbose: Verbose.SILENT, hosts: […] })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Lower-level functions
|
|
69
|
+
|
|
70
|
+
When the class is too coarse, the primitives are exported from `@catapultjs/deploy` and take an explicit `DeployContext` + `Host`: `deployHost()`, `rollbackHost()`, `initializeHost()`, `isHostSetup()`, `getCurrentRelease()`, `getReleaseNames()`, `getRevisions()`, `collectHostStatus()`, `runTask(name, ctx, host, { logger? })`.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Catapult CLI reference
|
|
2
|
+
|
|
3
|
+
The CLI is `cata`, invoked via `npx cata <command>`. Full docs: https://catapultjs.com/guide/cli
|
|
4
|
+
|
|
5
|
+
## Config loading
|
|
6
|
+
|
|
7
|
+
`version` and `init` work without a config file. All other commands load `deploy.ts`, `deploy.config.ts`, `deploy.js`, or `deploy.config.js` from the current directory, or use `--config <path>` / `-c <path>` to specify another file.
|
|
8
|
+
|
|
9
|
+
## Global options
|
|
10
|
+
|
|
11
|
+
| Option | Description |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `--config <path>`, `-c <path>` | Load a specific deploy config file |
|
|
14
|
+
| `--host <name>`, `-H <name>` | Target a specific host (available on all host-aware commands) |
|
|
15
|
+
| `--help` | Show help for the current command |
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
### `deploy:setup` (alias: `setup`)
|
|
20
|
+
|
|
21
|
+
Creates the remote directory structure and runs recipe setup hooks. Must be run once before the first deploy.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx cata deploy:setup
|
|
25
|
+
npx cata setup --host production
|
|
26
|
+
npx cata setup -H staging
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### `deploy` (alias: `dep`)
|
|
30
|
+
|
|
31
|
+
Runs the full deployment pipeline. Prompts to run `deploy:setup` if the host is not initialized.
|
|
32
|
+
|
|
33
|
+
| Option | Description |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `--branch <name>`, `-b <name>` | Override the branch for this deployment |
|
|
36
|
+
| `-v`, `-vv`, `-vvv` | Increase verbosity |
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx cata deploy
|
|
40
|
+
npx cata deploy --host production
|
|
41
|
+
npx cata deploy --branch feature/my-feature
|
|
42
|
+
npx cata dep -H staging -b develop
|
|
43
|
+
npx cata deploy --config deploy.staging.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `rollback`
|
|
47
|
+
|
|
48
|
+
Restores the previous release. With `--interactive`, prompts to choose the target release. Automatic rollback also triggers after a failure past `deploy:publish`.
|
|
49
|
+
|
|
50
|
+
| Option | Description |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `--interactive`, `-i` | Choose the target release interactively |
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx cata rollback
|
|
56
|
+
npx cata rollback --interactive
|
|
57
|
+
npx cata rollback -i -H production
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `status`
|
|
61
|
+
|
|
62
|
+
Shows current release, healthcheck, runtime versions, revision metadata, and lock state. With `--json`, targets all hosts by default and writes aggregated JSON to stdout (errors to stderr).
|
|
63
|
+
|
|
64
|
+
| Option | Description |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `--json` | Output result as JSON |
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx cata status
|
|
70
|
+
npx cata status --host production
|
|
71
|
+
npx cata status --json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `task <task-name>`
|
|
75
|
+
|
|
76
|
+
Runs a registered task against the current release. Requires an existing release on each target host. Use `list:tasks` first to see available task names.
|
|
77
|
+
|
|
78
|
+
| Option | Description |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `-v`, `-vv`, `-vvv` | Increase verbosity |
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx cata list:tasks
|
|
84
|
+
npx cata task deploy:build
|
|
85
|
+
npx cata task deploy:unlock
|
|
86
|
+
npx cata task deploy:restart -H production
|
|
87
|
+
npx cata task deploy:healthcheck -vv
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `run "<command>"`
|
|
91
|
+
|
|
92
|
+
Executes a shell command on the selected hosts over SSH. Quote the command string.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx cata run "pm2 list"
|
|
96
|
+
npx cata run "pm2 reload all" --host production
|
|
97
|
+
npx cata run "ls -lah current" -H staging
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `ssh`
|
|
101
|
+
|
|
102
|
+
Opens an interactive SSH session in `deployPath` on the selected host. Prompts to choose when several hosts are configured and `--host` is omitted.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx cata ssh
|
|
106
|
+
npx cata ssh --host production
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `list:releases`
|
|
110
|
+
|
|
111
|
+
Lists all releases on the target hosts, marking the current one.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx cata list:releases
|
|
115
|
+
npx cata list:releases --host production
|
|
116
|
+
npx cata list:releases --json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `list:revisions`
|
|
120
|
+
|
|
121
|
+
Shows the last 10 logged revisions from `.catapult/revisions.log`.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx cata list:revisions
|
|
125
|
+
npx cata list:revisions --host production
|
|
126
|
+
npx cata list:revisions --json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `list:tasks`
|
|
130
|
+
|
|
131
|
+
Lists all pipeline tasks and extra registered tasks from the loaded config.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx cata list:tasks
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `pipeline`
|
|
138
|
+
|
|
139
|
+
Shows the current deployment pipeline task order.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npx cata pipeline
|
|
143
|
+
npx cata pipeline --json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `init`
|
|
147
|
+
|
|
148
|
+
Generates a starter `deploy.ts` in the current directory. No config required.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npx cata init
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `version`
|
|
155
|
+
|
|
156
|
+
Prints the installed `@catapultjs/deploy` version. No config required.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx cata version
|
|
160
|
+
```
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Writing a Catapult deploy config
|
|
2
|
+
|
|
3
|
+
The config file is auto-detected as `deploy.ts`, `deploy.js`, `deploy.config.ts` or `deploy.config.js` at the project root (override with `cata --config <path>`). It must default-export `defineConfig()`. Full reference: https://catapultjs.com/guide/api
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { defineConfig } from '@catapultjs/deploy'
|
|
7
|
+
import '@catapultjs/deploy/recipes/git'
|
|
8
|
+
import '@catapultjs/deploy/recipes/pm2'
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
hosts: [
|
|
12
|
+
{
|
|
13
|
+
name: 'production',
|
|
14
|
+
ssh: 'deploy@example.com',
|
|
15
|
+
deployPath: '/home/deploy/myapp',
|
|
16
|
+
branch: 'main',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Critical rule: code delivery
|
|
23
|
+
|
|
24
|
+
`defineConfig()` does not impose a deployment mode. The pipeline task `deploy:update_code` MUST be provided by exactly one recipe (or a custom task), otherwise the deploy fails. Pick one:
|
|
25
|
+
|
|
26
|
+
| Recipe | Delivery | Use for |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `recipes/git` | Clones the repo on the server (bare mirror in `.catapult/repo`) | Server can reach the repo, build on server |
|
|
29
|
+
| `recipes/rsync` | Pushes a local directory into the release | Local builds, no repo access from server |
|
|
30
|
+
| `recipes/adonisjs_local` | Builds AdonisJS locally, uploads the artifact | AdonisJS without building on the server |
|
|
31
|
+
| `recipes/astro` / `recipes/vitepress` | Builds locally, uploads the static output | Static sites |
|
|
32
|
+
| custom `task('deploy:update_code', …)` | Whatever you implement | Anything else |
|
|
33
|
+
|
|
34
|
+
## Picking recipes by stack
|
|
35
|
+
|
|
36
|
+
Inspect the project (`package.json`, lock file, config files) before choosing:
|
|
37
|
+
|
|
38
|
+
- **AdonisJS**: `adonisjs` (installs and builds on the server, exposes `ace:*` migration tasks) or `adonisjs_local` (build locally, upload). Both need a delivery recipe only in the `adonisjs` case (pair it with `git`).
|
|
39
|
+
- **Nuxt**: `nuxt` (exposes `deploy:build` and `nuxt:generate`), pair with `git`.
|
|
40
|
+
- **Astro / VitePress**: `astro` / `vitepress` alone (they handle delivery).
|
|
41
|
+
- **Process manager**: add `pm2` if the app runs under PM2 (`ecosystem.config.cjs` expected in the project). It wires restart after publish and adds `pm2:*` tasks plus a status report.
|
|
42
|
+
- **Extras**: `directus` (DB migrations and schema snapshots), `redis` (`redis:db:flush*` tasks, configure with `set('redis_db', n)`).
|
|
43
|
+
|
|
44
|
+
Recipes register their tasks at import time: imports must appear before or inside the config file, never lazily.
|
|
45
|
+
|
|
46
|
+
## Config options
|
|
47
|
+
|
|
48
|
+
| Option | Type | Notes |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `hosts` | `Host[]` | Required |
|
|
51
|
+
| `keepReleases?` | `number` | Default `5` |
|
|
52
|
+
| `repository?` | `string` | Auto-detected from git origin |
|
|
53
|
+
| `packageManager?` | `PackageManager` | Auto-detected from lock file |
|
|
54
|
+
| `hooks?` | `Hooks` | See below |
|
|
55
|
+
| `verbose?` | `Verbose` | From `@catapultjs/deploy/enums`: `SILENT`, `NORMAL`, `TRACE` (default), `DEBUG` |
|
|
56
|
+
|
|
57
|
+
## Host options
|
|
58
|
+
|
|
59
|
+
| Option | Type | Notes |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `name` | `string` | Identifier used by `--host` / `hosts:` selectors |
|
|
62
|
+
| `ssh` | `string \| SshConfig` | `'user@host'` or `{ user, host, port? }` |
|
|
63
|
+
| `deployPath` | `string` | Absolute path on the server |
|
|
64
|
+
| `branch?` | `string \| { name, ask }` | `ask: true` prompts at deploy time (CLI only) |
|
|
65
|
+
| `healthcheck?` | `{ url?, retries?, delayMs? }` | Curl check after publish; the `deploy:healthcheck` task is removed automatically when no host defines a `url` |
|
|
66
|
+
| `bin?` | `Record<string, string>` | Per-host binary paths, e.g. `{ node: '/home/deploy/.nvm/versions/node/v24.0.0/bin/node' }`. Needed when binaries are not on the non-interactive SSH PATH (nvm setups) |
|
|
67
|
+
|
|
68
|
+
Multiple environments are just multiple hosts (`production`, `staging`); target one with `cata deploy -H staging`.
|
|
69
|
+
|
|
70
|
+
## Lifecycle hooks
|
|
71
|
+
|
|
72
|
+
All optional, all `(ctx: { host?, hosts?, error? }) => Promise<void>`:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
export default defineConfig({
|
|
76
|
+
hosts: […],
|
|
77
|
+
hooks: {
|
|
78
|
+
beforeDeploy: async ({ hosts }) => {}, // once, before all hosts
|
|
79
|
+
beforeHostDeploy: async ({ host }) => {}, // per host
|
|
80
|
+
afterHostDeploy: async ({ host }) => {}, // per host, even on failure
|
|
81
|
+
afterFailure: async ({ hosts, error }) => {}, // on error, before rethrow
|
|
82
|
+
afterDeploy: async ({ hosts }) => {}, // once, after success
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Pipeline customisation
|
|
88
|
+
|
|
89
|
+
The config file is a regular module: pipeline and store calls work alongside `defineConfig()`.
|
|
90
|
+
|
|
91
|
+
Default pipeline order: `deploy:lock` → `deploy:release` → `deploy:update_code` → `deploy:shared` → `deploy:publish` → `deploy:log_revision` → `deploy:healthcheck` → `deploy:unlock` → `deploy:cleanup`. Inspect with `cata pipeline`. Reference: https://catapultjs.com/guide/pipeline
|
|
92
|
+
|
|
93
|
+
Three built-in tasks are registered but NOT inserted by default. Insert them instead of writing custom install/build/test tasks:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { after } from '@catapultjs/deploy'
|
|
97
|
+
|
|
98
|
+
after('deploy:update_code', 'deploy:install') // package manager install
|
|
99
|
+
after('deploy:install', 'deploy:build') // pm run build
|
|
100
|
+
after('deploy:build', 'deploy:test') // pm run test
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Pipeline functions: `after(existing, task)`, `before(existing, task)`, `remove(name)`, `inPipeline(name)` to place a task relative to optional steps, `setPipeline([…])` to replace the whole sequence. Insertion deduplicates: re-adding a task moves it, the last position wins.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { set, after, inPipeline, remove, task, run, cd } from '@catapultjs/deploy'
|
|
107
|
+
|
|
108
|
+
set('writable_dirs', ['logs', 'tmp/uploads']) // created under shared/ at setup
|
|
109
|
+
set('shared_files', ['.env']) // touched under shared/ at setup
|
|
110
|
+
set('rsync_source_path', './dist') // rsync recipe option
|
|
111
|
+
|
|
112
|
+
task('app:warmup', () => {
|
|
113
|
+
cd('{{current_path}}')
|
|
114
|
+
run('curl -s localhost:3333/health')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// healthcheck is removed automatically when no host defines one
|
|
118
|
+
if (inPipeline('deploy:healthcheck')) {
|
|
119
|
+
after('deploy:healthcheck', 'app:warmup')
|
|
120
|
+
} else {
|
|
121
|
+
after('deploy:publish', 'app:warmup')
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Custom tasks can also be async functions receiving the `TaskContext` (e.g. `fetch` a Slack webhook with the `release` name). Run any registered task manually with `cata task <name>`.
|
|
126
|
+
|
|
127
|
+
### Rewriting the whole pipeline
|
|
128
|
+
|
|
129
|
+
`setPipeline()` replaces the sequence entirely, for full control over the order:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { defineConfig, setPipeline } from '@catapultjs/deploy'
|
|
133
|
+
import '@catapultjs/deploy/recipes/git'
|
|
134
|
+
import '@catapultjs/deploy/recipes/pm2'
|
|
135
|
+
|
|
136
|
+
setPipeline([
|
|
137
|
+
'deploy:lock',
|
|
138
|
+
'deploy:release',
|
|
139
|
+
'deploy:update_code',
|
|
140
|
+
'deploy:install',
|
|
141
|
+
'deploy:build',
|
|
142
|
+
'deploy:shared',
|
|
143
|
+
'deploy:publish',
|
|
144
|
+
'pm2:startOrReload',
|
|
145
|
+
'deploy:unlock',
|
|
146
|
+
'deploy:cleanup',
|
|
147
|
+
])
|
|
148
|
+
|
|
149
|
+
export default defineConfig({ hosts: […] })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Rules:
|
|
153
|
+
|
|
154
|
+
- Call it AFTER the recipe imports: recipes run their own `after()`/`before()` insertions at import time, and `setPipeline()` must have the last word.
|
|
155
|
+
- Every name must be a registered task (built-in or from an imported recipe), otherwise the deploy fails at that step with "Task not found".
|
|
156
|
+
- Keep `deploy:lock`/`deploy:unlock` unless concurrent deploys are acceptable, and `deploy:log_revision` if `list:revisions` and revision metadata matter.
|
|
157
|
+
- Omitting `deploy:cleanup` disables release pruning (`keepReleases` has no effect then).
|
|
158
|
+
|
|
159
|
+
## Checklist
|
|
160
|
+
|
|
161
|
+
1. `defineConfig()` is the default export.
|
|
162
|
+
2. Exactly one provider for `deploy:update_code` is imported (or defined).
|
|
163
|
+
3. Recipe store options (`set(…)`) match the imported recipes.
|
|
164
|
+
4. Healthcheck URLs point to an endpoint reachable from the server itself.
|
|
165
|
+
5. Pin the package version in `package.json` (the API is still in beta).
|
|
166
|
+
6. Verify with `cata pipeline` (task order) and `cata list:tasks` (registered tasks), then `cata deploy:setup` before the first deploy.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# GitHub Actions with Catapult
|
|
2
|
+
|
|
3
|
+
Use the official `catapultjs/deploy-action` to run Catapult inside GitHub Actions. The action handles SSH setup, detects the package manager from the lockfile, installs dependencies, and runs the requested Catapult command.
|
|
4
|
+
|
|
5
|
+
Full docs: https://catapultjs.com/guide/ci-cd
|
|
6
|
+
|
|
7
|
+
## Minimal workflow
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
name: Deploy
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: catapultjs/deploy-action@v0.5.0
|
|
22
|
+
with:
|
|
23
|
+
command: deploy
|
|
24
|
+
config: deploy.ts
|
|
25
|
+
private-key: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
|
|
26
|
+
known-hosts: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
|
27
|
+
version: latest
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Workflow with a custom runtime
|
|
31
|
+
|
|
32
|
+
Add `actions/setup-node` (or pnpm/bun setup) before the action when the project needs a specific Node version.
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- uses: actions/setup-node@v4
|
|
36
|
+
with:
|
|
37
|
+
node-version: 24
|
|
38
|
+
cache: npm
|
|
39
|
+
|
|
40
|
+
- uses: catapultjs/deploy-action@v0.5.0
|
|
41
|
+
with:
|
|
42
|
+
command: deploy
|
|
43
|
+
config: deploy.ts
|
|
44
|
+
private-key: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
|
|
45
|
+
known-hosts: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
|
46
|
+
version: latest
|
|
47
|
+
working-directory: .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Action inputs
|
|
51
|
+
|
|
52
|
+
| Name | Default | Description |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `command` | `deploy` | Catapult command to run (`deploy`, `rollback`, `task <name>`, etc.) |
|
|
55
|
+
| `config` | — | Path to the deploy config file |
|
|
56
|
+
| `args` | — | Extra CLI args, one per line |
|
|
57
|
+
| `package-manager` | auto-detected | `npm`, `pnpm`, `yarn`, or `bun` |
|
|
58
|
+
| `version` | `latest` | Version of `@catapultjs/deploy` to install |
|
|
59
|
+
| `working-directory` | `.` | Working directory relative to the repository root |
|
|
60
|
+
|
|
61
|
+
## SSH inputs
|
|
62
|
+
|
|
63
|
+
| Input | Required | Description |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `private-key` | Yes | SSH private key added to `ssh-agent` |
|
|
66
|
+
| `known-hosts` | Recommended | Content written to `~/.ssh/known_hosts` |
|
|
67
|
+
| `ssh-config` | No | Content written to `~/.ssh/config` |
|
|
68
|
+
| `insecure-ignore-host-key` | No | Disables strict host key checking |
|
|
69
|
+
|
|
70
|
+
## Reading sensitive config from environment variables
|
|
71
|
+
|
|
72
|
+
Keep SSH host and path out of `deploy.ts` by reading them from env vars, then passing them from the workflow:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// deploy.ts
|
|
76
|
+
const ssh = process.env.DEPLOY_SSH
|
|
77
|
+
const deployPath = process.env.DEPLOY_PATH
|
|
78
|
+
if (!ssh) throw new Error('Missing DEPLOY_SSH')
|
|
79
|
+
if (!deployPath) throw new Error('Missing DEPLOY_PATH')
|
|
80
|
+
|
|
81
|
+
export default defineConfig({
|
|
82
|
+
hosts: [{ name: 'production', ssh, deployPath, branch: process.env.DEPLOY_BRANCH || 'main' }],
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
- uses: catapultjs/deploy-action@v0.5.0
|
|
88
|
+
with:
|
|
89
|
+
command: deploy
|
|
90
|
+
private-key: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
|
|
91
|
+
known-hosts: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
|
92
|
+
env:
|
|
93
|
+
DEPLOY_SSH: ${{ secrets.DEPLOY_SSH }}
|
|
94
|
+
DEPLOY_PATH: ${{ vars.DEPLOY_PATH }}
|
|
95
|
+
DEPLOY_BRANCH: main
|
|
96
|
+
DEPLOY_USER: ${{ github.actor }}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use GitHub **Secrets** for sensitive values (SSH key, host string) and **Variables** for non-sensitive ones (deploy path). `DEPLOY_USER` is picked up by `deploy:log_revision` when `git config user.name` is unavailable on the runner.
|