@catapultjs/deploy 0.9.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/README.md +9 -1
- package/build/commands/list_revisions.js +12 -25
- package/build/commands/list_revisions.js.map +1 -1
- package/build/commands/status.d.ts +1 -0
- package/build/commands/status.js +36 -78
- package/build/commands/status.js.map +1 -1
- package/build/commands/version.js +3 -3
- 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/src/api.d.ts +60 -0
- package/build/src/api.js +159 -0
- package/build/src/api.js.map +1 -0
- 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/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
|
@@ -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.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Writing a Catapult recipe
|
|
2
|
+
|
|
3
|
+
A recipe is a JavaScript/TypeScript module that registers tasks and inserts them into the deployment pipeline as a side effect of being imported. Users activate it with a bare import in their `deploy.ts`:
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import './recipes/my-recipe.ts'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Full docs: https://catapultjs.com/guide/creating-recipes
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
|
|
13
|
+
- Namespace every task with a recipe prefix: `my-recipe:build`, not `build`.
|
|
14
|
+
- Call `desc('…')` immediately before each `task()` so the task shows a description in `cata list:tasks`.
|
|
15
|
+
- Tasks that queue remote commands use the DSL (`cd()`, `run()`); tasks that need command output or conditionals use `ssh()` directly with `set -e` and `q()` for quoting.
|
|
16
|
+
- Resolve binaries with `bin('node')` instead of hardcoding, so users can override paths per host via `host.bin`.
|
|
17
|
+
- Expose recipe options through the store (`get()`/`set()`), never through module-level mutable state.
|
|
18
|
+
|
|
19
|
+
## Basic structure
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { type TaskContext, task, desc, cd, run, after, bin } from '@catapultjs/deploy'
|
|
23
|
+
import { ssh, q } from '@catapultjs/deploy/utils'
|
|
24
|
+
|
|
25
|
+
// TypeScript: register task names for autocompletion
|
|
26
|
+
declare module '@catapultjs/deploy' {
|
|
27
|
+
interface TaskRegistry {
|
|
28
|
+
'my-recipe:build': true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
desc('Builds the application in the release directory')
|
|
33
|
+
task('my-recipe:build', () => {
|
|
34
|
+
cd('{{release_path}}')
|
|
35
|
+
run(`${bin('node')} --run build`)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Insert into the pipeline (deduplicated: re-adding moves the task)
|
|
39
|
+
after('deploy:update_code', 'my-recipe:build')
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Pipeline functions: `after(existing, newTask)`, `before(existing, newTask)`, `remove(name)`, `inPipeline(name)`, `getPipeline()`, `setPipeline(tasks)`.
|
|
43
|
+
|
|
44
|
+
Default pipeline: `deploy:lock`, `deploy:release`, `deploy:update_code`, `deploy:shared`, `deploy:publish`, `deploy:log_revision`, `deploy:healthcheck`, `deploy:unlock`, `deploy:cleanup`. Recipes may override a built-in task by registering the same name (e.g. `git`/`rsync` recipes provide `deploy:update_code`).
|
|
45
|
+
|
|
46
|
+
Use `inPipeline()` to attach relative to optional steps (`deploy:healthcheck` is removed when no host defines one). Insertion deduplicates: re-adding a task moves it, the last position wins.
|
|
47
|
+
|
|
48
|
+
Three tasks are registered but not inserted: `deploy:install` (package manager install), `deploy:build` (`pm run build`), `deploy:test` (`pm run test`). Prefer inserting or overriding them over inventing new install/build tasks, like the `adonisjs` and `nuxt` recipes do.
|
|
49
|
+
|
|
50
|
+
## Task context and raw SSH
|
|
51
|
+
|
|
52
|
+
The task function receives a `TaskContext`: `{ host, paths, config, release, logger }`. Use it for output capture or host-specific logic:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
desc('Shows service logs')
|
|
56
|
+
task('my-recipe:logs', async ({ host, paths, logger }: TaskContext) => {
|
|
57
|
+
const { stdout } = await ssh(host, `set -e\ncd ${q(paths.current)}\nmy-service logs --tail 50`)
|
|
58
|
+
logger.log(stdout.trim())
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Always write display output through `ctx.logger` (not `console.log`): the programmatic API captures it and returns it from `catapult.task()`.
|
|
63
|
+
|
|
64
|
+
`paths` fields: `base`, `releases`, `release`, `current`, `shared`, `cataConfig`, `repo`, `builder`, `lock`.
|
|
65
|
+
|
|
66
|
+
Template variables in `cd()`/`run()` strings: `{{release_path}}`, `{{current_path}}`, `{{shared_path}}`, `{{releases_path}}`, `{{base_path}}`, `{{release}}`.
|
|
67
|
+
|
|
68
|
+
Other DSL helpers: `local(command, { cwd? })` runs on the local machine, `upload(localPath, remotePath)` / `download(remotePath, localPath)` transfer over SCP, `isVerbose(level)` checks verbosity.
|
|
69
|
+
|
|
70
|
+
## Hooks
|
|
71
|
+
|
|
72
|
+
`onSetup()` runs during `cata deploy:setup`, after base directories exist:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { onSetup } from '@catapultjs/deploy'
|
|
76
|
+
import { ssh, q, getPaths } from '@catapultjs/deploy/utils'
|
|
77
|
+
|
|
78
|
+
onSetup(async (ctx, host, logger) => {
|
|
79
|
+
const paths = getPaths(host.deployPath, ctx.release)
|
|
80
|
+
await ssh(host, `set -e\nmkdir -p ${q(paths.shared + '/storage')}`)
|
|
81
|
+
logger.step(host.name, 'storage ready')
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`onStatus()` runs during `cata status`. Return an object: each key becomes an aligned line in text mode and is merged into the host entry of `status --json` and `catapult.status()`. Output written via the `logger` argument only appears in text mode.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { onStatus } from '@catapultjs/deploy'
|
|
89
|
+
|
|
90
|
+
onStatus(async (_ctx, host) => {
|
|
91
|
+
const { stdout } = await ssh(host, `set +e\nmy-service --version || true`)
|
|
92
|
+
return { 'my-service': stdout.trim() || 'unavailable' }
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configurable options
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { task, run, get } from '@catapultjs/deploy'
|
|
100
|
+
|
|
101
|
+
task('my-recipe:sync', () => {
|
|
102
|
+
const excludes = get<string[]>('my_recipe_excludes', [])
|
|
103
|
+
run(`rsync ${excludes.map((e) => `--exclude=${e}`).join(' ')} …`)
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Users set values in `deploy.ts` with `set('my_recipe_excludes', […])` before or after the import. Two store keys have built-in meaning during setup: `writable_dirs` (string[], created under `shared/`) and `shared_files` (string[], touched under `shared/`).
|
|
108
|
+
|
|
109
|
+
## Checklist before shipping
|
|
110
|
+
|
|
111
|
+
1. Tasks namespaced and described with `desc()`.
|
|
112
|
+
2. `TaskRegistry` augmented for TypeScript users.
|
|
113
|
+
3. Remote commands quoted with `q()` and prefixed with `set -e` (or `set +e` when failure is expected).
|
|
114
|
+
4. Display tasks write to `ctx.logger`, status hooks return data objects.
|
|
115
|
+
5. Reusable recipes can be contributed to the `contrib/` directory of https://github.com/catapultjs/deploy via pull request.
|