@catapultjs/deploy 0.12.0 → 0.13.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 +34 -13
- package/build/bin/run.js +18 -42
- package/build/bin/run.js.map +1 -1
- package/build/commands/config_validate.d.ts +7 -0
- package/build/commands/config_validate.js +45 -0
- package/build/commands/config_validate.js.map +1 -0
- package/build/commands/init.js +18 -2
- package/build/commands/init.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/src/config/json/loader.d.ts +4 -0
- package/build/src/config/json/loader.js +206 -0
- package/build/src/config/json/loader.js.map +1 -0
- package/build/src/config/json/recipe_registry.d.ts +3 -0
- package/build/src/config/json/recipe_registry.js +44 -0
- package/build/src/config/json/recipe_registry.js.map +1 -0
- package/build/src/config/json/schema.d.ts +45 -0
- package/build/src/config/json/schema.js +198 -0
- package/build/src/config/json/schema.js.map +1 -0
- package/build/src/config/loader.d.ts +2 -0
- package/build/src/config/loader.js +21 -0
- package/build/src/config/loader.js.map +1 -0
- package/build/src/config/resolve.d.ts +2 -0
- package/build/src/config/resolve.js +30 -0
- package/build/src/config/resolve.js.map +1 -0
- package/build/src/utils.js +8 -1
- package/build/src/utils.js.map +1 -1
- package/package.json +11 -6
- package/schema/deploy.schema.json +287 -0
- package/skills/catapultjs/SKILL.md +14 -11
- package/skills/catapultjs/references/cli.md +11 -2
- package/skills/catapultjs/references/config.md +133 -34
- package/skills/catapultjs/references/recipe.md +1 -1
- package/skills/catapultjs/references/recipes.md +4 -2
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://catapultjs.com/schema/deploy.schema.json",
|
|
4
|
+
"title": "Catapult deploy JSON configuration",
|
|
5
|
+
"description": "Declarative deployment configuration for @catapultjs/deploy.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "config"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": { "type": "string" },
|
|
11
|
+
"version": { "const": 1 },
|
|
12
|
+
"recipes": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"enum": [
|
|
16
|
+
"adonisjs",
|
|
17
|
+
"adonisjs_local",
|
|
18
|
+
"astro",
|
|
19
|
+
"astro_static",
|
|
20
|
+
"caddy",
|
|
21
|
+
"directus",
|
|
22
|
+
"git",
|
|
23
|
+
"nestjs",
|
|
24
|
+
"nextjs",
|
|
25
|
+
"nextjs_static",
|
|
26
|
+
"nuxt",
|
|
27
|
+
"nuxt_static",
|
|
28
|
+
"pm2",
|
|
29
|
+
"redis",
|
|
30
|
+
"rsync",
|
|
31
|
+
"systemd",
|
|
32
|
+
"tanstack",
|
|
33
|
+
"vitepress"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"uniqueItems": true
|
|
37
|
+
},
|
|
38
|
+
"config": { "$ref": "#/definitions/config" },
|
|
39
|
+
"store": { "$ref": "#/definitions/store" },
|
|
40
|
+
"tasks": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"propertyNames": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
43
|
+
"additionalProperties": { "$ref": "#/definitions/taskDefinition" }
|
|
44
|
+
},
|
|
45
|
+
"pipeline": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"uniqueItems": true
|
|
50
|
+
},
|
|
51
|
+
"before": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"propertyNames": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
54
|
+
"additionalProperties": {
|
|
55
|
+
"oneOf": [
|
|
56
|
+
{ "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
57
|
+
{
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
60
|
+
"minItems": 1,
|
|
61
|
+
"uniqueItems": true
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"after": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"propertyNames": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
69
|
+
"additionalProperties": {
|
|
70
|
+
"oneOf": [
|
|
71
|
+
{ "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
72
|
+
{
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
75
|
+
"minItems": 1,
|
|
76
|
+
"uniqueItems": true
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"remove": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": { "type": "string", "minLength": 1, "pattern": "^\\S+$" },
|
|
84
|
+
"uniqueItems": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"definitions": {
|
|
88
|
+
"store": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": {
|
|
91
|
+
"shared_dirs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
92
|
+
"shared_files": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
93
|
+
"writable_dirs": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
94
|
+
"source_path": { "type": "string" },
|
|
95
|
+
"adonisjs_path": { "type": "string" },
|
|
96
|
+
"astro_mode": {
|
|
97
|
+
"oneOf": [
|
|
98
|
+
{ "type": "string", "minLength": 1 },
|
|
99
|
+
{
|
|
100
|
+
"type": "object",
|
|
101
|
+
"minProperties": 1,
|
|
102
|
+
"propertyNames": { "type": "string", "minLength": 1 },
|
|
103
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"astro_path": { "type": "string" },
|
|
108
|
+
"caddy_config_path": { "type": "string", "minLength": 1 },
|
|
109
|
+
"caddy_local_config_path": { "type": "string", "minLength": 1 },
|
|
110
|
+
"caddy_use_sudo": { "type": "boolean" },
|
|
111
|
+
"caddy_validate_before_reload": { "type": "boolean" },
|
|
112
|
+
"caddy_reload_after_publish": { "type": "boolean" },
|
|
113
|
+
"directus_path": { "type": "string" },
|
|
114
|
+
"directus_snapshot_path": { "type": "string", "minLength": 1 },
|
|
115
|
+
"nextjs_path": { "type": "string" },
|
|
116
|
+
"nextjs_out_path": { "type": "string", "minLength": 1 },
|
|
117
|
+
"nuxt_path": { "type": "string" },
|
|
118
|
+
"redis_db": {
|
|
119
|
+
"oneOf": [
|
|
120
|
+
{ "type": "integer", "minimum": 0 },
|
|
121
|
+
{
|
|
122
|
+
"type": "array",
|
|
123
|
+
"items": { "type": "integer", "minimum": 0 },
|
|
124
|
+
"minItems": 1,
|
|
125
|
+
"uniqueItems": true
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
"rsync_source_path": { "type": "string", "minLength": 1 },
|
|
130
|
+
"rsync_excludes": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
131
|
+
"systemd_service": { "type": "string", "minLength": 1 },
|
|
132
|
+
"systemd_use_sudo": { "type": "boolean" },
|
|
133
|
+
"systemd_logs_lines": { "type": "number", "exclusiveMinimum": 0 },
|
|
134
|
+
"vitepress_path": { "type": "string" }
|
|
135
|
+
},
|
|
136
|
+
"required": [],
|
|
137
|
+
"additionalProperties": false
|
|
138
|
+
},
|
|
139
|
+
"config": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"properties": {
|
|
142
|
+
"keepReleases": { "type": "integer", "minimum": 1 },
|
|
143
|
+
"repository": { "type": "string", "minLength": 1 },
|
|
144
|
+
"packageManager": { "enum": ["npm", "pnpm", "yarn", "bun"] },
|
|
145
|
+
"hosts": { "type": "array", "items": { "$ref": "#/definitions/host" }, "minItems": 1 },
|
|
146
|
+
"verbose": { "enum": [0, 1, 2, 3] }
|
|
147
|
+
},
|
|
148
|
+
"required": ["hosts"],
|
|
149
|
+
"additionalProperties": false
|
|
150
|
+
},
|
|
151
|
+
"host": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"name": { "type": "string", "minLength": 1 },
|
|
155
|
+
"ssh": {
|
|
156
|
+
"oneOf": [
|
|
157
|
+
{ "type": "string", "minLength": 1 },
|
|
158
|
+
{
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"user": { "type": "string", "minLength": 1 },
|
|
162
|
+
"host": { "type": "string", "minLength": 1 },
|
|
163
|
+
"port": { "type": "integer", "minimum": 1, "maximum": 65535 }
|
|
164
|
+
},
|
|
165
|
+
"required": ["user", "host"],
|
|
166
|
+
"additionalProperties": false
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
"deployPath": { "type": "string", "minLength": 1, "pattern": "^/" },
|
|
171
|
+
"branch": {
|
|
172
|
+
"oneOf": [
|
|
173
|
+
{ "type": "string", "minLength": 1 },
|
|
174
|
+
{
|
|
175
|
+
"type": "object",
|
|
176
|
+
"properties": {
|
|
177
|
+
"name": { "type": "string", "minLength": 1 },
|
|
178
|
+
"ask": { "type": "boolean" }
|
|
179
|
+
},
|
|
180
|
+
"required": ["name", "ask"],
|
|
181
|
+
"additionalProperties": false
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
"healthcheck": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"url": { "type": "string", "minLength": 1 },
|
|
189
|
+
"retries": { "type": "integer", "minimum": 1 },
|
|
190
|
+
"delayMs": { "type": "integer", "minimum": 0 }
|
|
191
|
+
},
|
|
192
|
+
"required": [],
|
|
193
|
+
"additionalProperties": false
|
|
194
|
+
},
|
|
195
|
+
"bin": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"propertyNames": { "type": "string", "minLength": 1 },
|
|
198
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"required": ["name", "ssh", "deployPath"],
|
|
202
|
+
"additionalProperties": false
|
|
203
|
+
},
|
|
204
|
+
"taskStep": {
|
|
205
|
+
"oneOf": [
|
|
206
|
+
{
|
|
207
|
+
"type": "object",
|
|
208
|
+
"properties": { "cd": { "type": "string", "minLength": 1 } },
|
|
209
|
+
"required": ["cd"],
|
|
210
|
+
"additionalProperties": false
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"type": "object",
|
|
214
|
+
"properties": { "run": { "type": "string", "minLength": 1 } },
|
|
215
|
+
"required": ["run"],
|
|
216
|
+
"additionalProperties": false
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"type": "object",
|
|
220
|
+
"properties": {
|
|
221
|
+
"local": {
|
|
222
|
+
"type": "object",
|
|
223
|
+
"properties": {
|
|
224
|
+
"command": { "type": "string", "minLength": 1 },
|
|
225
|
+
"cwd": { "type": "string", "minLength": 1 }
|
|
226
|
+
},
|
|
227
|
+
"required": ["command"],
|
|
228
|
+
"additionalProperties": false
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"required": ["local"],
|
|
232
|
+
"additionalProperties": false
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"type": "object",
|
|
236
|
+
"properties": {
|
|
237
|
+
"upload": {
|
|
238
|
+
"type": "object",
|
|
239
|
+
"properties": {
|
|
240
|
+
"local": { "type": "string", "minLength": 1 },
|
|
241
|
+
"remote": { "type": "string", "minLength": 1 }
|
|
242
|
+
},
|
|
243
|
+
"required": ["local", "remote"],
|
|
244
|
+
"additionalProperties": false
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"required": ["upload"],
|
|
248
|
+
"additionalProperties": false
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"type": "object",
|
|
252
|
+
"properties": {
|
|
253
|
+
"download": {
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"remote": { "type": "string", "minLength": 1 },
|
|
257
|
+
"local": { "type": "string", "minLength": 1 }
|
|
258
|
+
},
|
|
259
|
+
"required": ["remote", "local"],
|
|
260
|
+
"additionalProperties": false
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"required": ["download"],
|
|
264
|
+
"additionalProperties": false
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
},
|
|
268
|
+
"taskDefinition": {
|
|
269
|
+
"oneOf": [
|
|
270
|
+
{ "type": "array", "items": { "$ref": "#/definitions/taskStep" }, "minItems": 1 },
|
|
271
|
+
{
|
|
272
|
+
"type": "object",
|
|
273
|
+
"properties": {
|
|
274
|
+
"description": { "type": "string", "minLength": 1 },
|
|
275
|
+
"steps": {
|
|
276
|
+
"type": "array",
|
|
277
|
+
"items": { "$ref": "#/definitions/taskStep" },
|
|
278
|
+
"minItems": 1
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"required": ["steps"],
|
|
282
|
+
"additionalProperties": false
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: catapultjs
|
|
3
|
-
description: Work with Catapult (@catapultjs/deploy), the SSH deployment tool for Node.js. Use when creating or updating
|
|
3
|
+
description: Work with Catapult (@catapultjs/deploy), the SSH deployment tool for Node.js. Use when creating or updating TypeScript, JavaScript or JSON deploy configs, writing recipes or deployment tasks, customizing the deploy pipeline, or scripting deployments with the programmatic API (Catapult class).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Catapult (@catapultjs/deploy)
|
|
@@ -9,33 +9,36 @@ Capistrano-style SSH deployment for Node.js: versioned releases under `releases/
|
|
|
9
9
|
|
|
10
10
|
## Critical rule
|
|
11
11
|
|
|
12
|
-
`deploy:update_code` uploads `source_path` by SCP by default. Import `recipes/git` or `recipes/rsync` only when you want to replace that delivery behavior. Do not combine delivery providers such as `git` and `rsync` unless the user explicitly defines the pipeline.
|
|
12
|
+
`deploy:update_code` uploads `source_path` by SCP by default. Import `recipes/git` or `recipes/rsync`, or list `git`/`rsync` under JSON `recipes`, only when you want to replace that delivery behavior. Do not combine delivery providers such as `git` and `rsync` unless the user explicitly defines the pipeline.
|
|
13
13
|
|
|
14
14
|
## References
|
|
15
15
|
|
|
16
16
|
Read the file matching the task before writing code:
|
|
17
17
|
|
|
18
|
-
| Task
|
|
19
|
-
|
|
|
20
|
-
| Create or update
|
|
21
|
-
| Write or modify a recipe or deployment task: task DSL, `onSetup`/`onStatus` hooks, store options, `TaskRegistry`
|
|
22
|
-
| Use an official recipe (git, rsync, nextjs, nuxt, astro, tanstack, nestjs, adonisjs, vitepress, directus, pm2, redis, caddy, systemd): 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
|
|
24
|
-
| Set up a GitHub Actions workflow with `catapultjs/deploy-action`: inputs, SSH secrets, env vars
|
|
25
|
-
| Use the `cata` CLI: deploy, rollback, task, status, run, ssh, list commands and their options
|
|
18
|
+
| Task | Read |
|
|
19
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
20
|
+
| Create or update a deploy config: choose TS/JS or JSON, configure hosts and recipes, add tasks, store values, healthchecks or pipeline controls | [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, nextjs, nuxt, astro, tanstack, nestjs, adonisjs, vitepress, directus, pm2, redis, caddy, systemd): 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
26
|
|
|
27
27
|
When a change spans several areas (e.g. a config plus a custom recipe), read every matching reference.
|
|
28
28
|
|
|
29
29
|
## Interactive config workflow
|
|
30
30
|
|
|
31
|
-
When the user asks to create, initialize, or configure a Catapult deployment and the required facts are not already present in the repo or prompt, ask focused questions before writing
|
|
31
|
+
When the user asks to create, initialize, or configure a Catapult deployment and the required facts are not already present in the repo or prompt, ask focused questions before writing the config.
|
|
32
32
|
|
|
33
33
|
Do not invent production values. At minimum, determine:
|
|
34
34
|
|
|
35
35
|
- app stack/recipe (`nextjs`, `nextjs_static`, `nuxt`, `astro`, `nestjs`, `tanstack`, `adonisjs`, etc.)
|
|
36
|
+
- config format: strict declarative JSON, or TypeScript/JavaScript when executable behavior is required
|
|
36
37
|
- code delivery mode (default SCP via `source_path`, `git`, `rsync`, local-build/static recipe, or custom)
|
|
37
38
|
- host name, SSH target, deploy path, and branch if using `git`
|
|
38
39
|
- process manager (`pm2` or none) and whether an `ecosystem.config.cjs` should be created
|
|
39
40
|
- `.env`/shared paths and optional healthcheck URL
|
|
40
41
|
|
|
41
42
|
Prefer one compact question group over a long interview. If the project files already answer some items, state the inferred values and ask only for the missing or ambiguous ones. After the user answers, generate the config and include the setup/deploy commands.
|
|
43
|
+
|
|
44
|
+
Use `deploy.config.json` for data-only deployments with built-in recipes, serializable store values, declarative task steps and direct `pipeline`, `remove`, `before`, and `after` controls. Include `"$schema": "https://catapultjs.com/schema/deploy.schema.json"` and `"version": 1`. Static `hosts[].bin` overrides are supported. Use `cata config:validate` to validate a config explicitly in CI or before deployment; add `--json` for machine-readable output. Use TypeScript or JavaScript for callbacks/hooks, external recipes, dynamic `bin()` calls inside custom tasks, conditional pipeline changes or arbitrary task code.
|
|
@@ -4,7 +4,7 @@ The CLI is `cata`, invoked via `npx cata <command>`. Full docs: https://catapult
|
|
|
4
4
|
|
|
5
5
|
## Config loading
|
|
6
6
|
|
|
7
|
-
`version` and `init` work without a config file. All other commands
|
|
7
|
+
`version` and `init` work without a config file. All other commands auto-detect `deploy.ts`, `deploy.config.ts`, `deploy.js`, `deploy.config.js`, `deploy.config.json` or `deploy.json` from the current directory, or use `--config <path>` / `-c <path>` to specify another file. JSON configs are schema-validated before command execution.
|
|
8
8
|
|
|
9
9
|
## Global options
|
|
10
10
|
|
|
@@ -145,12 +145,21 @@ npx cata pipeline --json
|
|
|
145
145
|
|
|
146
146
|
### `init`
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
Prompts for TypeScript, JavaScript or JSON and generates `deploy.config.ts`, `deploy.config.js` or `deploy.config.json` in the current directory. The JSON template includes `version: 1` and `"$schema": "https://catapultjs.com/schema/deploy.schema.json"`. No config required.
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
151
|
npx cata init
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
### `config:validate`
|
|
155
|
+
|
|
156
|
+
Loads the deploy config and exits without running deployment tasks or connecting to hosts. Supports `--config <path>` / `-c <path>` and `--json`. JSON output is `{ "valid": true, "file": "..." }` on success and `{ "valid": false, "file": "...", "error": "..." }` on validation failure.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx cata config:validate
|
|
160
|
+
npx cata config:validate --json
|
|
161
|
+
```
|
|
162
|
+
|
|
154
163
|
### `version`
|
|
155
164
|
|
|
156
165
|
Prints the installed `@catapultjs/deploy` version. No config required.
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Writing a Catapult deploy config
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Config files are auto-detected at the project root (override with `cata <command> --config <path>`):
|
|
4
|
+
|
|
5
|
+
- TypeScript/JavaScript: `deploy.ts`, `deploy.config.ts`, `deploy.js`, `deploy.config.js`
|
|
6
|
+
- JSON: `deploy.config.json`, `deploy.json`
|
|
7
|
+
|
|
8
|
+
TypeScript and JavaScript must default-export `defineConfig()`. JSON is validated as a strict declarative document. Full references: https://catapultjs.com/guide/api and https://catapultjs.com/guide/json-configuration
|
|
9
|
+
|
|
10
|
+
## Choosing a format
|
|
11
|
+
|
|
12
|
+
Use JSON for a data-only deployment made from built-in recipes, serializable store values, declarative task steps and direct pipeline controls. Prefer `deploy.config.json`, include the public schema URL, and set `version` to `1`.
|
|
13
|
+
|
|
14
|
+
Use TypeScript or JavaScript when the deployment needs callbacks/hooks, external or project-local recipes, dynamic `bin()` calls inside custom tasks, conditions such as `inPipeline()`, or custom task code with branching, loops, API calls or arbitrary async behavior. Static per-host `bin` overrides are supported in JSON. Do not try to encode executable behavior as JSON strings.
|
|
4
15
|
|
|
5
16
|
## Before creating a config
|
|
6
17
|
|
|
@@ -21,7 +32,7 @@ Ask a compact set of questions for missing values:
|
|
|
21
32
|
4. Should PM2 be configured? If yes, confirm app name, start entry, port, and whether to create `ecosystem.config.cjs`.
|
|
22
33
|
5. Is there a healthcheck URL and any extra shared files or directories?
|
|
23
34
|
|
|
24
|
-
When the user answers, generate
|
|
35
|
+
When the user answers, generate the requested format. For TypeScript/JavaScript, put `set(...)` calls before the recipe imports they configure. For JSON, put those values under `store` and recipe names under `recipes`.
|
|
25
36
|
|
|
26
37
|
```typescript
|
|
27
38
|
import { defineConfig } from '@catapultjs/deploy'
|
|
@@ -40,18 +51,106 @@ export default defineConfig({
|
|
|
40
51
|
})
|
|
41
52
|
```
|
|
42
53
|
|
|
54
|
+
## JSON configuration
|
|
55
|
+
|
|
56
|
+
Use strict JSON: double-quoted keys and strings, with no comments or trailing commas. The schema rejects unknown properties and validates the whole document before Catapult connects to a host.
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"$schema": "https://catapultjs.com/schema/deploy.schema.json",
|
|
61
|
+
"version": 1,
|
|
62
|
+
"recipes": ["git"],
|
|
63
|
+
"config": {
|
|
64
|
+
"keepReleases": 2,
|
|
65
|
+
"hosts": [
|
|
66
|
+
{
|
|
67
|
+
"name": "production",
|
|
68
|
+
"ssh": "deploy@example.com",
|
|
69
|
+
"deployPath": "/home/deploy/myapp",
|
|
70
|
+
"branch": "main"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"store": {
|
|
75
|
+
"shared_files": [".env"],
|
|
76
|
+
"shared_dirs": ["storage", "logs"]
|
|
77
|
+
},
|
|
78
|
+
"tasks": {
|
|
79
|
+
"app:build": {
|
|
80
|
+
"description": "Install dependencies and build the application",
|
|
81
|
+
"steps": [{ "cd": "{{release_path}}" }, { "run": "npm ci" }, { "run": "npm run build" }]
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"after": {
|
|
85
|
+
"deploy:shared": "app:build"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Top-level contract:
|
|
91
|
+
|
|
92
|
+
| Field | Rule |
|
|
93
|
+
| ---------- | --------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `$schema` | Recommended: `https://catapultjs.com/schema/deploy.schema.json` |
|
|
95
|
+
| `version` | Required and exactly `1` |
|
|
96
|
+
| `recipes` | Optional closed list of built-in recipe names |
|
|
97
|
+
| `config` | Required serializable `Config` values, excluding function-valued `hooks` |
|
|
98
|
+
| `store` | Optional typed values for documented core and built-in recipe keys, equivalent to `set(key, value)` |
|
|
99
|
+
| `tasks` | Optional object of declarative task definitions |
|
|
100
|
+
| `pipeline` | Optional complete task array, equivalent to `setPipeline()` |
|
|
101
|
+
| `remove` | Optional task array, equivalent to repeated `remove()` calls |
|
|
102
|
+
| `before` | Optional target-to-task mapping, equivalent to `before()` |
|
|
103
|
+
| `after` | Optional target-to-task mapping, equivalent to `after()` |
|
|
104
|
+
|
|
105
|
+
Built-in recipe names accepted by JSON: `adonisjs`, `adonisjs_local`, `astro`, `astro_static`, `caddy`, `directus`, `git`, `nestjs`, `nextjs`, `nextjs_static`, `nuxt`, `nuxt_static`, `pm2`, `redis`, `rsync`, `systemd`, `tanstack`, `vitepress`. Do not use module paths such as `recipes/git`.
|
|
106
|
+
|
|
107
|
+
The schema validates known `store` key types and rejects unknown keys. Use TypeScript or JavaScript when a custom recipe needs its own store contract.
|
|
108
|
+
|
|
109
|
+
`config` accepts `hosts`, `keepReleases`, `repository`, `packageManager` and `verbose`. Host data accepts `name`, `ssh`, `deployPath`, `branch`, `healthcheck` and static `bin` path overrides. Host names must be unique. Built-in recipes can resolve `hosts[].bin`; declarative custom task steps cannot invoke the dynamic `bin()` helper.
|
|
110
|
+
|
|
111
|
+
A task is either an array of steps or `{ "description": "...", "steps": [...] }`. Supported steps:
|
|
112
|
+
|
|
113
|
+
- `{ "cd": "..." }` and `{ "run": "..." }` execute remotely.
|
|
114
|
+
- `{ "local": { "command": "...", "cwd": "..." } }` executes locally; `cwd` is optional.
|
|
115
|
+
- `{ "upload": { "local": "...", "remote": "..." } }` uploads with SCP.
|
|
116
|
+
- `{ "download": { "remote": "...", "local": "..." } }` downloads with SCP.
|
|
117
|
+
|
|
118
|
+
Local, upload and download steps flush the current remote command batch. Repeat `{ "cd": "..." }` before later remote `{ "run": "..." }` steps when a task mixes remote commands with local or transfer steps.
|
|
119
|
+
|
|
120
|
+
Remote commands and paths support Catapult placeholders such as `{{release_path}}`, `{{current_path}}` and `{{shared_path}}`. Declaring a task registers it but does not insert it into the deploy pipeline.
|
|
121
|
+
|
|
122
|
+
Pipeline controls are applied after recipes and tasks are registered, in a fixed order independent of top-level JSON property order:
|
|
123
|
+
|
|
124
|
+
1. `pipeline` replaces the complete task sequence when present.
|
|
125
|
+
2. `remove` removes each listed task.
|
|
126
|
+
3. `before` inserts a task or ordered task array before each target.
|
|
127
|
+
4. `after` inserts a task or ordered task array after each target.
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"pipeline": ["deploy:lock", "deploy:release", "deploy:publish", "deploy:unlock"],
|
|
132
|
+
"remove": ["deploy:release"],
|
|
133
|
+
"before": { "deploy:publish": "app:verify" },
|
|
134
|
+
"after": { "deploy:lock": ["app:install", "app:build"] }
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Every pipeline and placement task must be registered. Each `before`/`after` target must exist when placement is applied, and the final pipeline must contain at least one task. A task can be placed only once across `before` and `after`; it cannot also be a placement target or appear under `remove`. Use an array under one target for an ordered task sequence. When `pipeline` is present it replaces recipe insertions too; recipes still register their tasks, but the array must describe the complete intended sequence.
|
|
139
|
+
|
|
140
|
+
Catapult already unlocks failed deployments automatically; do not invent a `deploy:failed` hook mapping. `ace:migration:run` only exists when `adonisjs` is listed in `recipes`, so import that recipe before trying to remove the migration task.
|
|
141
|
+
|
|
43
142
|
## Code delivery
|
|
44
143
|
|
|
45
144
|
Catapult registers a default `deploy:update_code` task that uploads `source_path` via SCP. Recipes can override that task when a different delivery mechanism is needed. Pick one delivery mode:
|
|
46
145
|
|
|
47
|
-
| Recipe
|
|
48
|
-
|
|
|
49
|
-
| default `deploy:update_code`
|
|
50
|
-
| `recipes/git`
|
|
51
|
-
| `recipes/rsync`
|
|
52
|
-
| `recipes/adonisjs_local`
|
|
53
|
-
| `recipes/vitepress`
|
|
54
|
-
| custom `task('deploy:update_code', …)` | Whatever you implement
|
|
146
|
+
| Recipe | Delivery | Use for |
|
|
147
|
+
| -------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
148
|
+
| default `deploy:update_code` | Uploads `source_path` via SCP | Simple local upload, static recipes that set `source_path` |
|
|
149
|
+
| `recipes/git` | Clones the repo on the server (bare mirror in `.catapult/repo`) | Server can reach the repo, build on server |
|
|
150
|
+
| `recipes/rsync` | Pushes a local directory into the release | Local builds, no repo access from server |
|
|
151
|
+
| `recipes/adonisjs_local` | Builds AdonisJS locally, uploads the artifact | AdonisJS without building on the server |
|
|
152
|
+
| `recipes/vitepress` | Builds locally, uploads the static output | VitePress static sites |
|
|
153
|
+
| custom `task('deploy:update_code', …)` | Whatever you implement | Anything else |
|
|
55
154
|
|
|
56
155
|
Avoid combining providers that override `deploy:update_code` (`git`, `rsync`, `adonisjs_local`, `vitepress`, custom) unless intentionally replacing task behavior.
|
|
57
156
|
|
|
@@ -69,35 +168,35 @@ Inspect the project (`package.json`, lock file, config files) before choosing:
|
|
|
69
168
|
- **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.
|
|
70
169
|
- **Extras**: `directus` (DB migrations and schema snapshots), `redis` (`redis:db:flush*` tasks, configure with `set('redis_db', n)`).
|
|
71
170
|
|
|
72
|
-
Recipes register their tasks at import time
|
|
171
|
+
Recipes register their tasks at import time. In TypeScript/JavaScript, imports must appear before or inside the config file, never lazily. In JSON, list built-in names under `recipes`; external recipes require TypeScript or JavaScript.
|
|
73
172
|
|
|
74
173
|
## Config options
|
|
75
174
|
|
|
76
|
-
| Option
|
|
77
|
-
|
|
|
78
|
-
| `hosts`
|
|
79
|
-
| `keepReleases?`
|
|
80
|
-
| `repository?`
|
|
81
|
-
| `packageManager?` | `PackageManager` | Auto-detected from lock file
|
|
82
|
-
| `hooks?`
|
|
83
|
-
| `verbose?`
|
|
175
|
+
| Option | Type | Notes |
|
|
176
|
+
| ----------------- | ---------------- | ------------------------------------------------------------------------------- |
|
|
177
|
+
| `hosts` | `Host[]` | Required |
|
|
178
|
+
| `keepReleases?` | `number` | Default `5` |
|
|
179
|
+
| `repository?` | `string` | Auto-detected from git origin |
|
|
180
|
+
| `packageManager?` | `PackageManager` | Auto-detected from lock file |
|
|
181
|
+
| `hooks?` | `Hooks` | TypeScript/JavaScript only; see below |
|
|
182
|
+
| `verbose?` | `Verbose` | From `@catapultjs/deploy/enums`: `SILENT`, `NORMAL`, `TRACE` (default), `DEBUG` |
|
|
84
183
|
|
|
85
184
|
## Host options
|
|
86
185
|
|
|
87
|
-
| Option
|
|
88
|
-
|
|
|
89
|
-
| `name`
|
|
90
|
-
| `ssh`
|
|
91
|
-
| `deployPath`
|
|
92
|
-
| `branch?`
|
|
93
|
-
| `healthcheck?` | `{ url?, retries?, delayMs? }` | Curl check after publish; the `deploy:healthcheck` task is removed automatically when no host defines a `url`
|
|
94
|
-
| `bin?`
|
|
186
|
+
| Option | Type | Notes |
|
|
187
|
+
| -------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
188
|
+
| `name` | `string` | Identifier used by `--host` / `hosts:` selectors |
|
|
189
|
+
| `ssh` | `string \| SshConfig` | `'user@host'` or `{ user, host, port? }` |
|
|
190
|
+
| `deployPath` | `string` | Absolute path on the server |
|
|
191
|
+
| `branch?` | `string \| { name, ask }` | `ask: true` prompts at deploy time (CLI only) |
|
|
192
|
+
| `healthcheck?` | `{ url?, retries?, delayMs? }` | Curl check after publish; the `deploy:healthcheck` task is removed automatically when no host defines a `url` |
|
|
193
|
+
| `bin?` | `Record<string, string>` | Supported in all formats. Per-host binary paths, e.g. `{ node: '/home/deploy/.nvm/versions/node/v24.0.0/bin/node' }`. Built-in recipes resolve these paths; calling `bin()` inside a custom task requires TypeScript/JavaScript. |
|
|
95
194
|
|
|
96
195
|
Multiple environments are just multiple hosts (`production`, `staging`); target one with `cata deploy -H staging`.
|
|
97
196
|
|
|
98
197
|
## Lifecycle hooks
|
|
99
198
|
|
|
100
|
-
All optional, all `(ctx: { host?, hosts?, error? }) => Promise<void>`:
|
|
199
|
+
TypeScript/JavaScript only. All optional, all `(ctx: { host?, hosts?, error? }) => Promise<void>`:
|
|
101
200
|
|
|
102
201
|
```typescript
|
|
103
202
|
export default defineConfig({
|
|
@@ -124,8 +223,8 @@ Three built-in tasks are registered but NOT inserted by default. Insert them ins
|
|
|
124
223
|
import { after } from '@catapultjs/deploy'
|
|
125
224
|
|
|
126
225
|
after('deploy:update_code', 'deploy:install') // package manager install
|
|
127
|
-
after('deploy:install', 'deploy:build')
|
|
128
|
-
after('deploy:build', 'deploy:test')
|
|
226
|
+
after('deploy:install', 'deploy:build') // pm run build
|
|
227
|
+
after('deploy:build', 'deploy:test') // pm run test
|
|
129
228
|
```
|
|
130
229
|
|
|
131
230
|
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.
|
|
@@ -133,9 +232,9 @@ Pipeline functions: `after(existing, task)`, `before(existing, task)`, `remove(n
|
|
|
133
232
|
```typescript
|
|
134
233
|
import { set, after, inPipeline, remove, task, run, cd } from '@catapultjs/deploy'
|
|
135
234
|
|
|
136
|
-
set('writable_dirs', ['logs', 'tmp/uploads'])
|
|
137
|
-
set('shared_files', ['.env'])
|
|
138
|
-
set('rsync_source_path', './dist')
|
|
235
|
+
set('writable_dirs', ['logs', 'tmp/uploads']) // created under shared/ at setup
|
|
236
|
+
set('shared_files', ['.env']) // touched under shared/ at setup
|
|
237
|
+
set('rsync_source_path', './dist') // rsync recipe option
|
|
139
238
|
|
|
140
239
|
task('app:warmup', () => {
|
|
141
240
|
cd('{{current_path}}')
|
|
@@ -186,7 +285,7 @@ Rules:
|
|
|
186
285
|
|
|
187
286
|
## Checklist
|
|
188
287
|
|
|
189
|
-
1. `defineConfig()
|
|
288
|
+
1. TypeScript/JavaScript default-exports `defineConfig()`; JSON uses strict syntax, the public `$schema` URL and `version: 1`.
|
|
190
289
|
2. Delivery mode is intentional: default SCP, or one overriding provider such as `git`, `rsync`, `adonisjs_local`, `vitepress`, or a custom `deploy:update_code`.
|
|
191
290
|
3. Recipe store options (`set(…)`) match the imported recipes.
|
|
192
291
|
4. Healthcheck URLs point to an endpoint reachable from the server itself.
|
|
@@ -104,7 +104,7 @@ task('my-recipe:sync', () => {
|
|
|
104
104
|
})
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
Users set values in `deploy.ts` with `set('my_recipe_excludes', […])
|
|
107
|
+
Users set values in `deploy.ts` with `set('my_recipe_excludes', […])`. Values read inside task functions can be set before or after a static import. Values read while the recipe module is loaded, especially options that change pipeline wiring, require `set(...)` followed by `await import(...)` because static ESM imports run before the module body. Two store keys have built-in meaning during setup: `writable_dirs` (string[], created under `shared/`) and `shared_files` (string[], touched under `shared/`).
|
|
108
108
|
|
|
109
109
|
## Checklist before shipping
|
|
110
110
|
|