@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
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://nodejs.org)
|
|
5
5
|
[](https://github.com/catapultjs/deploy/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
A Capistrano-style deployment tool for Node.js — versioned releases, shared directories, composable task pipeline, automatic rollback. No agent, no server-side dependency, just SSH.
|
|
7
|
+
A Capistrano-style deployment tool for Node.js — versioned releases, shared directories, composable task pipeline, automatic rollback. No agent, no container, no server-side dependency, just SSH.
|
|
8
8
|
|
|
9
9
|
Full documentation at **https://catapultjs.com/**
|
|
10
10
|
|
|
@@ -87,6 +87,14 @@ npx cata deploy
|
|
|
87
87
|
- Node.js >= 24 on the machine running Catapult
|
|
88
88
|
- SSH key-based access to the target servers
|
|
89
89
|
|
|
90
|
+
## Agent skill
|
|
91
|
+
|
|
92
|
+
The package ships a [`catapultjs` agent skill](https://catapultjs.com/guide/agent-skills) for Claude Code and SKILL.md-compatible agents, with references covering the deploy config, recipe writing and the programmatic API.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx skills add catapultjs/deploy
|
|
96
|
+
```
|
|
97
|
+
|
|
90
98
|
## Contributing a recipe
|
|
91
99
|
|
|
92
100
|
If you've written a recipe that could be useful to others, open a pull request and add it to the [`contrib/`](https://github.com/catapultjs/deploy/tree/main/contrib) directory.
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { flags } from '@adonisjs/ace';
|
|
11
11
|
import { Context } from "../src/context.js";
|
|
12
|
-
import {
|
|
12
|
+
import { getRevisions } from "../src/deployer.js";
|
|
13
13
|
import { BaseDeployCommand } from "../src/base_command.js";
|
|
14
14
|
export default class ListRevisions extends BaseDeployCommand {
|
|
15
15
|
static commandName = 'list:revisions';
|
|
@@ -23,41 +23,28 @@ export default class ListRevisions extends BaseDeployCommand {
|
|
|
23
23
|
for (const host of hosts) {
|
|
24
24
|
if (!(await this.ensureHostSetup(ctx, host)))
|
|
25
25
|
continue;
|
|
26
|
-
const paths = getPaths(host.deployPath, ctx.release);
|
|
27
|
-
const logFile = `${paths.cataConfig}/revisions.log`;
|
|
28
26
|
if (!this.json)
|
|
29
27
|
this.logger.log(this.colors.bold(`\n# ${host.name}`));
|
|
30
|
-
const
|
|
31
|
-
const lines = stdout.trim().split('\n').filter(Boolean).reverse();
|
|
28
|
+
const revisions = await getRevisions(ctx, host);
|
|
32
29
|
if (this.json) {
|
|
33
|
-
const revisions = [];
|
|
34
|
-
for (const line of lines) {
|
|
35
|
-
try {
|
|
36
|
-
revisions.push(JSON.parse(line));
|
|
37
|
-
}
|
|
38
|
-
catch { }
|
|
39
|
-
}
|
|
40
30
|
report.push({ name: host.name, revisions });
|
|
41
31
|
continue;
|
|
42
32
|
}
|
|
43
33
|
const table = this.ui.table();
|
|
44
34
|
table.head(['Release', 'Branch', 'Commit', 'By', 'Date']);
|
|
45
|
-
if (
|
|
35
|
+
if (revisions.length === 0) {
|
|
46
36
|
table.row(['no revisions', '', '', '', '']);
|
|
47
37
|
}
|
|
48
38
|
else {
|
|
49
|
-
for (const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
]);
|
|
59
|
-
}
|
|
60
|
-
catch { }
|
|
39
|
+
for (const revision of revisions) {
|
|
40
|
+
const { release, branch, commit, user, date } = revision;
|
|
41
|
+
table.row([
|
|
42
|
+
release ?? '—',
|
|
43
|
+
branch ?? '—',
|
|
44
|
+
commit ? commit.slice(0, 7) : '—',
|
|
45
|
+
user ?? '—',
|
|
46
|
+
date ? new Date(date).toLocaleString() : '—',
|
|
47
|
+
]);
|
|
61
48
|
}
|
|
62
49
|
}
|
|
63
50
|
table.render();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list_revisions.js","sourceRoot":"","sources":["../../commands/list_revisions.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"list_revisions.js","sourceRoot":"","sources":["../../commands/list_revisions.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE1D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,iBAAiB;IAC1D,MAAM,CAAC,WAAW,GAAG,gBAAgB,CAAA;IACrC,MAAM,CAAC,WAAW,GAAG,uCAAuC,CAAA;IAK5D,KAAK,CAAC,GAAG;QACP,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;QAEzB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QACxD,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,MAAM,MAAM,GAA6D,EAAE,CAAA;QAE3E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAAE,SAAQ;YAEtD,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;YAErE,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAE/C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;gBAC3C,SAAQ;YACV,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;YAC7B,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;YAEzD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,KAAK,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;YAC7C,CAAC;iBAAM,CAAC;gBACN,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;oBACjC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAG/C,CAAA;oBACD,KAAK,CAAC,GAAG,CAAC;wBACR,OAAO,IAAI,GAAG;wBACd,MAAM,IAAI,GAAG;wBACb,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;wBACjC,IAAI,IAAI,GAAG;wBACX,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,GAAG;qBAC7C,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YAED,KAAK,CAAC,MAAM,EAAE,CAAA;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;;AAjDO;IADP,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;2CACnC"}
|
package/build/commands/status.js
CHANGED
|
@@ -8,13 +8,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { flags } from '@adonisjs/ace';
|
|
11
|
-
import { Context } from "../src/context.js";
|
|
12
|
-
import { getCurrentRelease } from "../src/deployer.js";
|
|
13
|
-
import { bin } from "../src/task.js";
|
|
14
|
-
import { getPipeline } from "../src/pipeline.js";
|
|
15
|
-
import { hooks } from "../src/pipeline/hooks.js";
|
|
16
|
-
import { q, getPaths, ssh, detectPackageManager } from "../src/utils.js";
|
|
17
11
|
import { MemoryRenderer } from '@poppinss/cliui';
|
|
12
|
+
import { Context } from "../src/context.js";
|
|
13
|
+
import { collectHostStatus, HOST_STATUS_FIELDS } from "../src/status.js";
|
|
18
14
|
import { BaseDeployCommand } from "../src/base_command.js";
|
|
19
15
|
import { CatapultLogger, logger } from "../src/logger.js";
|
|
20
16
|
export default class Status extends BaseDeployCommand {
|
|
@@ -25,91 +21,53 @@ export default class Status extends BaseDeployCommand {
|
|
|
25
21
|
const hosts = await this.selectHosts({ all: this.json });
|
|
26
22
|
if (!hosts)
|
|
27
23
|
return;
|
|
28
|
-
const pm = await detectPackageManager();
|
|
29
|
-
const report = [];
|
|
30
24
|
const hookLogger = this.json ? new CatapultLogger() : logger;
|
|
31
25
|
if (this.json)
|
|
32
26
|
hookLogger.useRenderer(new MemoryRenderer());
|
|
27
|
+
const report = [];
|
|
33
28
|
for (const host of hosts) {
|
|
34
29
|
if (!(await this.ensureHostSetup(ctx, host)))
|
|
35
30
|
continue;
|
|
36
|
-
const paths = getPaths(host.deployPath, ctx.release);
|
|
37
|
-
const status = { name: host.name };
|
|
38
|
-
report.push(status);
|
|
39
31
|
if (!this.json)
|
|
40
32
|
this.logger.log(this.colors.bold(`\n# ${host.name}`));
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (getPipeline().includes('deploy:healthcheck')) {
|
|
48
|
-
try {
|
|
49
|
-
await ssh(host, `set -e\n${bin('curl')} --fail --silent --show-error --max-time 5 ${q(host.healthcheck?.url)} >/dev/null`);
|
|
50
|
-
status.health = 'ok';
|
|
51
|
-
if (!this.json)
|
|
52
|
-
this.logger.log(`Health ${this.colors.green('OK')}`);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
status.health = 'fail';
|
|
56
|
-
if (!this.json)
|
|
57
|
-
this.logger.log(`Health ${this.colors.red('FAIL')}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const { stdout: versionsStdout } = await ssh(host, `set +e\ncd ${q(paths.current)}\necho "NODE:$(${bin('node')} --version 2>/dev/null || true)"\necho "PM:$(${bin(pm)} --version 2>/dev/null || true)"`);
|
|
61
|
-
const lines = versionsStdout.trim().split('\n');
|
|
62
|
-
const nodeVersion = lines.find((l) => l.startsWith('NODE:'))?.slice(5) || '';
|
|
63
|
-
const pmVersion = lines.find((l) => l.startsWith('PM:'))?.slice(3) || '';
|
|
64
|
-
status.node = nodeVersion || null;
|
|
65
|
-
status.packageManager = { name: pm, version: pmVersion || null };
|
|
66
|
-
if (!this.json) {
|
|
67
|
-
this.logger.log(`Node ${this.colors.dim(nodeVersion || 'unavailable')}`);
|
|
68
|
-
this.logger.log(`${pm.padEnd(8)} ${this.colors.dim(pmVersion || 'unavailable')}`);
|
|
69
|
-
}
|
|
70
|
-
for (const hook of hooks.getStatus()) {
|
|
71
|
-
const data = await hook(ctx, host, hookLogger);
|
|
72
|
-
if (!data)
|
|
73
|
-
continue;
|
|
74
|
-
Object.assign(status, data);
|
|
75
|
-
if (!this.json) {
|
|
76
|
-
for (const [key, value] of Object.entries(data)) {
|
|
77
|
-
this.logger.log(`${key.padEnd(8)} ${this.colors.dim(String(value))}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const { stdout: revStdout } = await ssh(host, `set +e\n[ -f ${q(paths.cataConfig + '/revisions.log')} ] && tail -1 ${q(paths.cataConfig + '/revisions.log')} || true`);
|
|
82
|
-
const rev = revStdout.trim();
|
|
83
|
-
if (rev) {
|
|
84
|
-
try {
|
|
85
|
-
const revision = JSON.parse(rev);
|
|
86
|
-
const { branch, commit, user, date } = revision;
|
|
87
|
-
status.revision = revision;
|
|
88
|
-
if (!this.json) {
|
|
89
|
-
this.logger.log(`Branch ${this.colors.dim(branch ?? '—')}`);
|
|
90
|
-
this.logger.log(`Commit ${this.colors.dim(commit ? commit.slice(0, 7) : '—')}`);
|
|
91
|
-
this.logger.log(`By ${this.colors.dim(user ?? '—')}`);
|
|
92
|
-
this.logger.log(`Date ${this.colors.dim(date ? new Date(date).toLocaleString() : '—')}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch { }
|
|
96
|
-
}
|
|
97
|
-
const { stdout: lockStdout } = await ssh(host, `set +e\n[ -f ${q(paths.lock)} ] && cat ${q(paths.lock)} || true`);
|
|
98
|
-
const lock = lockStdout.trim();
|
|
99
|
-
status.lock = lock || null;
|
|
100
|
-
if (!this.json) {
|
|
101
|
-
this.logger.log(`Lock ${lock ? this.colors.yellow(lock) : this.colors.dim('none')}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
status.error = error.message;
|
|
106
|
-
this.logger.error(`status error: ${error.message}`);
|
|
107
|
-
}
|
|
33
|
+
const status = await collectHostStatus(ctx, host, hookLogger);
|
|
34
|
+
report.push(status);
|
|
35
|
+
if (status.error)
|
|
36
|
+
this.logger.error(`status error: ${status.error}`);
|
|
37
|
+
if (!this.json)
|
|
38
|
+
this.printStatus(status);
|
|
108
39
|
}
|
|
109
40
|
if (this.json) {
|
|
110
41
|
this.logger.log(JSON.stringify({ hosts: report }, null, 2));
|
|
111
42
|
}
|
|
112
43
|
}
|
|
44
|
+
printStatus(status) {
|
|
45
|
+
if (status.release !== undefined) {
|
|
46
|
+
this.logger.log(`Release ${status.release ? this.colors.cyan(status.release) : this.colors.dim('none')}`);
|
|
47
|
+
}
|
|
48
|
+
if (status.health) {
|
|
49
|
+
this.logger.log(`Health ${status.health === 'ok' ? this.colors.green('OK') : this.colors.red('FAIL')}`);
|
|
50
|
+
}
|
|
51
|
+
if (status.packageManager) {
|
|
52
|
+
this.logger.log(`Node ${this.colors.dim(status.node || 'unavailable')}`);
|
|
53
|
+
this.logger.log(`${status.packageManager.name.padEnd(8)} ${this.colors.dim(status.packageManager.version || 'unavailable')}`);
|
|
54
|
+
}
|
|
55
|
+
for (const [key, value] of Object.entries(status)) {
|
|
56
|
+
if (HOST_STATUS_FIELDS.has(key))
|
|
57
|
+
continue;
|
|
58
|
+
this.logger.log(`${key.padEnd(8)} ${this.colors.dim(String(value))}`);
|
|
59
|
+
}
|
|
60
|
+
if (status.revision) {
|
|
61
|
+
const { branch, commit, user, date } = status.revision;
|
|
62
|
+
this.logger.log(`Branch ${this.colors.dim(branch ?? '—')}`);
|
|
63
|
+
this.logger.log(`Commit ${this.colors.dim(commit ? commit.slice(0, 7) : '—')}`);
|
|
64
|
+
this.logger.log(`By ${this.colors.dim(user ?? '—')}`);
|
|
65
|
+
this.logger.log(`Date ${this.colors.dim(date ? new Date(date).toLocaleString() : '—')}`);
|
|
66
|
+
}
|
|
67
|
+
if (status.lock !== undefined) {
|
|
68
|
+
this.logger.log(`Lock ${status.lock ? this.colors.yellow(status.lock) : this.colors.dim('none')}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
113
71
|
}
|
|
114
72
|
__decorate([
|
|
115
73
|
flags.boolean({ description: 'Output result as JSON' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../commands/status.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,9 +8,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { BaseCommand, flags } from '@adonisjs/ace';
|
|
11
|
-
import { readFile } from 'fs/promises';
|
|
12
|
-
import { resolve, dirname } from 'path';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
|
+
import { resolve, dirname } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
14
|
export default class Version extends BaseCommand {
|
|
15
15
|
static commandName = 'version';
|
|
16
16
|
static description = 'Show the current version';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../commands/version.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../commands/version.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,WAAW;IAC9C,MAAM,CAAC,WAAW,GAAG,SAAS,CAAA;IAC9B,MAAM,CAAC,WAAW,GAAG,0BAA0B,CAAA;IAK/C,KAAK,CAAC,GAAG;QACP,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAA;QACtF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;QAExD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;YAClE,OAAM;QACR,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;;AAZO;IADP,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;qCACnC"}
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -5,4 +5,7 @@ export * from "./src/task.js";
|
|
|
5
5
|
export * from "./src/pipeline.js";
|
|
6
6
|
export * from "./src/package_manager.js";
|
|
7
7
|
export * from "./src/define_config.js";
|
|
8
|
+
export * from "./src/deployer.js";
|
|
9
|
+
export * from "./src/status.js";
|
|
10
|
+
export { Catapult } from "./src/api.js";
|
|
8
11
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { Config, TaskName } from './types.ts';
|
|
3
|
+
import { type HostStatus } from './status.ts';
|
|
4
|
+
export interface HostSelector {
|
|
5
|
+
hosts?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface RollbackOptions extends HostSelector {
|
|
8
|
+
release?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TaskEvent {
|
|
11
|
+
host: string;
|
|
12
|
+
task: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TaskErrorEvent extends TaskEvent {
|
|
15
|
+
error: Error;
|
|
16
|
+
}
|
|
17
|
+
export interface HostDoneEvent {
|
|
18
|
+
host: string;
|
|
19
|
+
release: string;
|
|
20
|
+
}
|
|
21
|
+
export interface HostReleases {
|
|
22
|
+
name: string;
|
|
23
|
+
current: string | null;
|
|
24
|
+
releases: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface HostRevisions {
|
|
27
|
+
name: string;
|
|
28
|
+
revisions: Record<string, unknown>[];
|
|
29
|
+
}
|
|
30
|
+
export interface TaskInfo {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
}
|
|
34
|
+
export interface TaskOutput {
|
|
35
|
+
host: string;
|
|
36
|
+
output: string;
|
|
37
|
+
}
|
|
38
|
+
export interface TaskList {
|
|
39
|
+
pipeline: TaskInfo[];
|
|
40
|
+
extra: TaskInfo[];
|
|
41
|
+
}
|
|
42
|
+
export declare class Catapult extends EventEmitter {
|
|
43
|
+
#private;
|
|
44
|
+
constructor(config: Config);
|
|
45
|
+
on(event: 'task:start', listener: (event: TaskEvent) => void): this;
|
|
46
|
+
on(event: 'task:done', listener: (event: TaskEvent) => void): this;
|
|
47
|
+
on(event: 'task:error', listener: (event: TaskErrorEvent) => void): this;
|
|
48
|
+
on(event: 'host:done', listener: (event: HostDoneEvent) => void): this;
|
|
49
|
+
setup(options?: HostSelector): Promise<void>;
|
|
50
|
+
deploy(options?: HostSelector): Promise<void>;
|
|
51
|
+
task(name: TaskName, options?: HostSelector): Promise<TaskOutput[]>;
|
|
52
|
+
rollback(options?: RollbackOptions): Promise<void>;
|
|
53
|
+
status(options?: HostSelector): Promise<HostStatus[]>;
|
|
54
|
+
listReleases(options?: HostSelector): Promise<HostReleases[]>;
|
|
55
|
+
listRevisions(options?: HostSelector & {
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<HostRevisions[]>;
|
|
58
|
+
listTasks(): Promise<TaskList>;
|
|
59
|
+
pipeline(): Promise<string[]>;
|
|
60
|
+
}
|
package/build/src/api.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { MemoryRenderer } from '@poppinss/cliui';
|
|
3
|
+
import { Context } from "./context.js";
|
|
4
|
+
import { defineConfig } from "./define_config.js";
|
|
5
|
+
import { hasTask, getTasks, getTaskDescription, runTask } from "./task.js";
|
|
6
|
+
import { getPipeline } from "./pipeline.js";
|
|
7
|
+
import { deployHost, rollbackHost, initializeHost, isHostSetup, getCurrentRelease, getReleaseNames, getRevisions, } from "./deployer.js";
|
|
8
|
+
import { collectHostStatus } from "./status.js";
|
|
9
|
+
import { CatapultLogger } from "./logger.js";
|
|
10
|
+
export class Catapult extends EventEmitter {
|
|
11
|
+
#initialize;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super();
|
|
14
|
+
this.#initialize = defineConfig(config);
|
|
15
|
+
}
|
|
16
|
+
on(event, listener) {
|
|
17
|
+
return super.on(event, listener);
|
|
18
|
+
}
|
|
19
|
+
async #context() {
|
|
20
|
+
await this.#initialize();
|
|
21
|
+
return Context.get();
|
|
22
|
+
}
|
|
23
|
+
#resolveHosts(ctx, names) {
|
|
24
|
+
if (!names || names.length === 0)
|
|
25
|
+
return ctx.config.hosts;
|
|
26
|
+
return names.map((name) => {
|
|
27
|
+
const host = ctx.config.hosts.find((h) => h.name === name);
|
|
28
|
+
if (!host)
|
|
29
|
+
throw new Error(`Unknown host: ${name}`);
|
|
30
|
+
return host;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async setup(options = {}) {
|
|
34
|
+
const ctx = await this.#context();
|
|
35
|
+
for (const host of this.#resolveHosts(ctx, options.hosts)) {
|
|
36
|
+
await initializeHost(ctx, host);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async deploy(options = {}) {
|
|
40
|
+
const ctx = await this.#context();
|
|
41
|
+
const hosts = this.#resolveHosts(ctx, options.hosts);
|
|
42
|
+
const observer = {
|
|
43
|
+
taskStart: (host, task) => this.emit('task:start', { host: host.name, task }),
|
|
44
|
+
taskDone: (host, task) => this.emit('task:done', { host: host.name, task }),
|
|
45
|
+
taskError: (host, task, error) => this.emit('task:error', { host: host.name, task, error }),
|
|
46
|
+
};
|
|
47
|
+
if (ctx.hooks.beforeDeploy)
|
|
48
|
+
await ctx.hooks.beforeDeploy({ hosts });
|
|
49
|
+
try {
|
|
50
|
+
for (const host of hosts) {
|
|
51
|
+
if (!(await isHostSetup(ctx, host))) {
|
|
52
|
+
throw new Error(`[${host.name}] Catapult is not initialized on this server. Run setup() first.`);
|
|
53
|
+
}
|
|
54
|
+
await deployHost(ctx, host, observer);
|
|
55
|
+
this.emit('host:done', { host: host.name, release: ctx.release });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (ctx.hooks.afterFailure) {
|
|
60
|
+
await ctx.hooks.afterFailure({ hosts, error: error });
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
if (ctx.hooks.afterDeploy)
|
|
65
|
+
await ctx.hooks.afterDeploy({ hosts });
|
|
66
|
+
}
|
|
67
|
+
async task(name, options = {}) {
|
|
68
|
+
const ctx = await this.#context();
|
|
69
|
+
if (!hasTask(name)) {
|
|
70
|
+
throw new Error(`Unknown task: ${name}. Available: ${getTasks().join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
const results = [];
|
|
73
|
+
for (const host of this.#resolveHosts(ctx, options.hosts)) {
|
|
74
|
+
if (!(await isHostSetup(ctx, host))) {
|
|
75
|
+
throw new Error(`[${host.name}] Catapult is not initialized on this server. Run setup() first.`);
|
|
76
|
+
}
|
|
77
|
+
const currentRelease = await getCurrentRelease(ctx, host);
|
|
78
|
+
if (!currentRelease) {
|
|
79
|
+
throw new Error(`[${host.name}] no current release found, run deploy() first`);
|
|
80
|
+
}
|
|
81
|
+
const renderer = new MemoryRenderer();
|
|
82
|
+
const taskLogger = new CatapultLogger();
|
|
83
|
+
taskLogger.useRenderer(renderer);
|
|
84
|
+
this.emit('task:start', { host: host.name, task: name });
|
|
85
|
+
try {
|
|
86
|
+
await runTask(name, { ...ctx, release: currentRelease }, host, { logger: taskLogger });
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.emit('task:error', { host: host.name, task: name, error });
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
this.emit('task:done', { host: host.name, task: name });
|
|
93
|
+
results.push({
|
|
94
|
+
host: host.name,
|
|
95
|
+
output: renderer
|
|
96
|
+
.getLogs()
|
|
97
|
+
.map((log) => log.message)
|
|
98
|
+
.join('\n'),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
async rollback(options = {}) {
|
|
104
|
+
const ctx = await this.#context();
|
|
105
|
+
for (const host of this.#resolveHosts(ctx, options.hosts)) {
|
|
106
|
+
await rollbackHost(ctx, host, options.release);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async status(options = {}) {
|
|
110
|
+
const ctx = await this.#context();
|
|
111
|
+
const hosts = this.#resolveHosts(ctx, options.hosts);
|
|
112
|
+
const hookLogger = new CatapultLogger();
|
|
113
|
+
hookLogger.useRenderer(new MemoryRenderer());
|
|
114
|
+
const report = [];
|
|
115
|
+
for (const host of hosts) {
|
|
116
|
+
report.push(await collectHostStatus(ctx, host, hookLogger));
|
|
117
|
+
}
|
|
118
|
+
return report;
|
|
119
|
+
}
|
|
120
|
+
async listReleases(options = {}) {
|
|
121
|
+
const ctx = await this.#context();
|
|
122
|
+
const report = [];
|
|
123
|
+
for (const host of this.#resolveHosts(ctx, options.hosts)) {
|
|
124
|
+
report.push({
|
|
125
|
+
name: host.name,
|
|
126
|
+
current: (await getCurrentRelease(ctx, host)) ?? null,
|
|
127
|
+
releases: await getReleaseNames(ctx, host),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return report;
|
|
131
|
+
}
|
|
132
|
+
async listRevisions(options = {}) {
|
|
133
|
+
const ctx = await this.#context();
|
|
134
|
+
const report = [];
|
|
135
|
+
for (const host of this.#resolveHosts(ctx, options.hosts)) {
|
|
136
|
+
report.push({
|
|
137
|
+
name: host.name,
|
|
138
|
+
revisions: await getRevisions(ctx, host, options.limit),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return report;
|
|
142
|
+
}
|
|
143
|
+
async listTasks() {
|
|
144
|
+
await this.#context();
|
|
145
|
+
const pipeline = getPipeline();
|
|
146
|
+
const describe = (name) => ({ name, description: getTaskDescription(name) });
|
|
147
|
+
return {
|
|
148
|
+
pipeline: pipeline.map(describe),
|
|
149
|
+
extra: getTasks()
|
|
150
|
+
.filter((t) => !pipeline.includes(t))
|
|
151
|
+
.map(describe),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async pipeline() {
|
|
155
|
+
await this.#context();
|
|
156
|
+
return getPipeline();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
|
package/build/src/deployer.d.ts
CHANGED
|
@@ -4,5 +4,11 @@ export declare function initializeHost(ctx: DeployContext, host: Host): Promise<
|
|
|
4
4
|
export declare function isHostSetup(ctx: DeployContext, host: Host): Promise<boolean>;
|
|
5
5
|
export declare function getCurrentRelease(ctx: DeployContext, host: Host): Promise<string | null>;
|
|
6
6
|
export declare function getReleaseNames(ctx: DeployContext, host: Host): Promise<string[]>;
|
|
7
|
+
export declare function getRevisions(ctx: DeployContext, host: Host, limit?: number): Promise<Record<string, unknown>[]>;
|
|
7
8
|
export declare function rollbackHost(ctx: DeployContext, host: Host, target?: string): Promise<void>;
|
|
8
|
-
export
|
|
9
|
+
export interface DeployObserver {
|
|
10
|
+
taskStart?: (host: Host, task: string) => void;
|
|
11
|
+
taskDone?: (host: Host, task: string) => void;
|
|
12
|
+
taskError?: (host: Host, task: string, error: Error) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function deployHost(ctx: DeployContext, host: Host, observer?: DeployObserver): Promise<void>;
|
package/build/src/deployer.js
CHANGED
|
@@ -82,6 +82,19 @@ export async function getReleaseNames(ctx, host) {
|
|
|
82
82
|
.map((line) => line.trim().replace(/\/$/, ''))
|
|
83
83
|
.filter(Boolean);
|
|
84
84
|
}
|
|
85
|
+
export async function getRevisions(ctx, host, limit = 10) {
|
|
86
|
+
const paths = getPaths(host.deployPath, ctx.release);
|
|
87
|
+
const logFile = `${paths.cataConfig}/revisions.log`;
|
|
88
|
+
const { stdout } = await ssh(host, `set +e\n[ -f ${q(logFile)} ] && tail -${limit} ${q(logFile)} || true`);
|
|
89
|
+
const revisions = [];
|
|
90
|
+
for (const line of stdout.trim().split('\n').filter(Boolean).reverse()) {
|
|
91
|
+
try {
|
|
92
|
+
revisions.push(JSON.parse(line));
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
}
|
|
96
|
+
return revisions;
|
|
97
|
+
}
|
|
85
98
|
async function getPreviousReleaseName(ctx, host) {
|
|
86
99
|
const currentRelease = await getCurrentRelease(ctx, host);
|
|
87
100
|
const releases = await getReleaseNames(ctx, host);
|
|
@@ -106,17 +119,21 @@ export async function rollbackHost(ctx, host, target) {
|
|
|
106
119
|
await runTask('deploy:healthcheck', ctx, host);
|
|
107
120
|
}
|
|
108
121
|
}
|
|
109
|
-
export async function deployHost(ctx, host) {
|
|
122
|
+
export async function deployHost(ctx, host, observer) {
|
|
110
123
|
let locked = false;
|
|
111
124
|
let published = false;
|
|
125
|
+
let currentTask = '';
|
|
112
126
|
const deployStart = Date.now();
|
|
113
127
|
await runHook(ctx, 'beforeHostDeploy', { host });
|
|
114
128
|
try {
|
|
115
129
|
const verbose = ctx.config.verbose ?? 0;
|
|
116
130
|
for (const taskName of getPipeline()) {
|
|
131
|
+
currentTask = taskName;
|
|
117
132
|
if (verbose >= Verbose.NORMAL)
|
|
118
133
|
logger.task(elapsed(Date.now() - deployStart), host.name, taskName);
|
|
134
|
+
observer?.taskStart?.(host, taskName);
|
|
119
135
|
await runTask(taskName, ctx, host);
|
|
136
|
+
observer?.taskDone?.(host, taskName);
|
|
120
137
|
if (taskName === 'deploy:lock')
|
|
121
138
|
locked = true;
|
|
122
139
|
if (taskName === 'deploy:publish')
|
|
@@ -125,6 +142,7 @@ export async function deployHost(ctx, host) {
|
|
|
125
142
|
logger.ok(host.name, `deploy OK -> ${ctx.release}`, elapsed(Date.now() - deployStart));
|
|
126
143
|
}
|
|
127
144
|
catch (error) {
|
|
145
|
+
observer?.taskError?.(host, currentTask, error);
|
|
128
146
|
logger.fail(host.name, `deploy failed: ${error.message}`);
|
|
129
147
|
if (published) {
|
|
130
148
|
try {
|