@matthesketh/fleet 1.8.0 → 1.11.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 +186 -16
- package/dist/bin/fleet-agent.d.ts +2 -0
- package/dist/bin/fleet-agent.js +7 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +73 -31
- package/dist/commands/add.d.ts +2 -1
- package/dist/commands/add.js +66 -59
- package/dist/commands/audit.d.ts +1 -0
- package/dist/commands/audit.js +144 -0
- package/dist/commands/backup.d.ts +1 -0
- package/dist/commands/backup.js +510 -0
- package/dist/commands/boot-start.d.ts +3 -1
- package/dist/commands/boot-start.js +39 -47
- package/dist/commands/completions.d.ts +6 -0
- package/dist/commands/completions.js +83 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.js +96 -0
- package/dist/commands/deploy.js +3 -2
- package/dist/commands/deps.js +5 -1
- package/dist/commands/doctor.d.ts +32 -0
- package/dist/commands/doctor.js +186 -0
- package/dist/commands/egress.d.ts +1 -1
- package/dist/commands/egress.js +13 -10
- package/dist/commands/freeze.d.ts +8 -4
- package/dist/commands/freeze.js +77 -59
- package/dist/commands/git.js +2 -2
- package/dist/commands/health.d.ts +2 -1
- package/dist/commands/health.js +38 -56
- package/dist/commands/init.d.ts +2 -1
- package/dist/commands/init.js +83 -73
- package/dist/commands/install-mcp.d.ts +3 -1
- package/dist/commands/install-mcp.js +53 -34
- package/dist/commands/list.d.ts +2 -1
- package/dist/commands/list.js +22 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/notify.d.ts +1 -0
- package/dist/commands/notify.js +51 -0
- package/dist/commands/patch-systemd.d.ts +7 -1
- package/dist/commands/patch-systemd.js +71 -31
- package/dist/commands/remove.d.ts +3 -1
- package/dist/commands/remove.js +37 -26
- package/dist/commands/restart.d.ts +4 -1
- package/dist/commands/restart.js +17 -20
- package/dist/commands/rollback.d.ts +4 -1
- package/dist/commands/rollback.js +33 -42
- package/dist/commands/secrets.js +157 -9
- package/dist/commands/start.d.ts +4 -1
- package/dist/commands/start.js +17 -20
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.js +21 -26
- package/dist/commands/stop.d.ts +4 -1
- package/dist/commands/stop.js +17 -20
- package/dist/commands/testflight.d.ts +1 -0
- package/dist/commands/testflight.js +193 -0
- package/dist/commands/update.d.ts +16 -0
- package/dist/commands/update.js +95 -0
- package/dist/core/audit/cache.d.ts +4 -0
- package/dist/core/audit/cache.js +37 -0
- package/dist/core/audit/config.d.ts +5 -0
- package/dist/core/audit/config.js +35 -0
- package/dist/core/audit/greenlight.d.ts +11 -0
- package/dist/core/audit/greenlight.js +81 -0
- package/dist/core/audit/reporters/cli.d.ts +3 -0
- package/dist/core/audit/reporters/cli.js +68 -0
- package/dist/core/audit/suppress.d.ts +6 -0
- package/dist/core/audit/suppress.js +37 -0
- package/dist/core/audit/target.d.ts +5 -0
- package/dist/core/audit/target.js +26 -0
- package/dist/core/audit/types.d.ts +54 -0
- package/dist/core/audit/types.js +5 -0
- package/dist/core/backup/browser-api.d.ts +66 -0
- package/dist/core/backup/browser-api.js +197 -0
- package/dist/core/backup/browser-server.d.ts +11 -0
- package/dist/core/backup/browser-server.js +241 -0
- package/dist/core/backup/browser-ui.d.ts +5 -0
- package/dist/core/backup/browser-ui.js +268 -0
- package/dist/core/backup/cloudflare.d.ts +7 -0
- package/dist/core/backup/cloudflare.js +82 -0
- package/dist/core/backup/config.d.ts +9 -0
- package/dist/core/backup/config.js +80 -0
- package/dist/core/backup/detect.d.ts +11 -0
- package/dist/core/backup/detect.js +71 -0
- package/dist/core/backup/dump.d.ts +11 -0
- package/dist/core/backup/dump.js +82 -0
- package/dist/core/backup/index.d.ts +9 -0
- package/dist/core/backup/index.js +9 -0
- package/dist/core/backup/repo.d.ts +71 -0
- package/dist/core/backup/repo.js +256 -0
- package/dist/core/backup/schedule.d.ts +17 -0
- package/dist/core/backup/schedule.js +90 -0
- package/dist/core/backup/sensitive.d.ts +5 -0
- package/dist/core/backup/sensitive.js +37 -0
- package/dist/core/backup/status.d.ts +3 -0
- package/dist/core/backup/status.js +29 -0
- package/dist/core/backup/statuspage.d.ts +23 -0
- package/dist/core/backup/statuspage.js +145 -0
- package/dist/core/backup/system.d.ts +24 -0
- package/dist/core/backup/system.js +209 -0
- package/dist/core/backup/totp.d.ts +16 -0
- package/dist/core/backup/totp.js +116 -0
- package/dist/core/backup/types.d.ts +70 -0
- package/dist/core/backup/types.js +7 -0
- package/dist/core/backup/unlock.d.ts +19 -0
- package/dist/core/backup/unlock.js +69 -0
- package/dist/core/boot-refresh.d.ts +1 -1
- package/dist/core/boot-refresh.js +10 -9
- package/dist/core/deps/actors/pr-creator.d.ts +5 -3
- package/dist/core/deps/actors/pr-creator.js +71 -18
- package/dist/core/deps/collectors/fetch-with-timeout.d.ts +7 -0
- package/dist/core/deps/collectors/fetch-with-timeout.js +16 -0
- package/dist/core/deps/collectors/npm.js +3 -1
- package/dist/core/deps/collectors/vulnerability.d.ts +8 -0
- package/dist/core/deps/collectors/vulnerability.js +31 -2
- package/dist/core/deps/config.js +6 -0
- package/dist/core/deps/scanner.js +1 -1
- package/dist/core/deps/types.d.ts +8 -0
- package/dist/core/env.d.ts +3 -0
- package/dist/core/env.js +11 -0
- package/dist/core/exec.d.ts +1 -0
- package/dist/core/exec.js +4 -0
- package/dist/core/file-lock.d.ts +18 -0
- package/dist/core/file-lock.js +44 -0
- package/dist/core/git-onboard.js +10 -13
- package/dist/core/github.d.ts +3 -1
- package/dist/core/github.js +10 -7
- package/dist/core/logs-policy.d.ts +5 -0
- package/dist/core/logs-policy.js +20 -1
- package/dist/core/operator.d.ts +21 -0
- package/dist/core/operator.js +54 -0
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.js +26 -0
- package/dist/core/routines/schema.d.ts +11 -11
- package/dist/core/routines/schema.js +14 -3
- package/dist/core/routines/store.d.ts +8 -8
- package/dist/core/secrets-ops.d.ts +31 -6
- package/dist/core/secrets-ops.js +208 -102
- package/dist/core/secrets-providers.js +2 -2
- package/dist/core/secrets-rotation.d.ts +1 -1
- package/dist/core/secrets-rotation.js +58 -52
- package/dist/core/secrets-v2-cleanup.d.ts +19 -0
- package/dist/core/secrets-v2-cleanup.js +94 -0
- package/dist/core/secrets-v2-creds.d.ts +9 -0
- package/dist/core/secrets-v2-creds.js +44 -0
- package/dist/core/secrets-v2-install.d.ts +13 -0
- package/dist/core/secrets-v2-install.js +76 -0
- package/dist/core/secrets-v2-keypair.d.ts +10 -0
- package/dist/core/secrets-v2-keypair.js +31 -0
- package/dist/core/secrets-v2-migrate.d.ts +29 -0
- package/dist/core/secrets-v2-migrate.js +395 -0
- package/dist/core/secrets-v2-ops.d.ts +36 -0
- package/dist/core/secrets-v2-ops.js +184 -0
- package/dist/core/secrets-v2-protocol.d.ts +19 -0
- package/dist/core/secrets-v2-protocol.js +60 -0
- package/dist/core/secrets-v2-snapshot.d.ts +36 -0
- package/dist/core/secrets-v2-snapshot.js +115 -0
- package/dist/core/secrets-v2.d.ts +21 -0
- package/dist/core/secrets-v2.js +249 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +91 -11
- package/dist/core/self-update.d.ts +32 -11
- package/dist/core/self-update.js +52 -14
- package/dist/core/testflight/asc.d.ts +12 -0
- package/dist/core/testflight/asc.js +101 -0
- package/dist/core/testflight/credentials.d.ts +3 -0
- package/dist/core/testflight/credentials.js +35 -0
- package/dist/core/testflight/eas.d.ts +4 -0
- package/dist/core/testflight/eas.js +38 -0
- package/dist/core/testflight/resolve.d.ts +6 -0
- package/dist/core/testflight/resolve.js +44 -0
- package/dist/core/testflight/types.d.ts +13 -0
- package/dist/core/testflight/types.js +3 -0
- package/dist/core/testflight/workflow.d.ts +17 -0
- package/dist/core/testflight/workflow.js +65 -0
- package/dist/core/validate.d.ts +1 -0
- package/dist/core/validate.js +8 -0
- package/dist/mcp/audit-tools.d.ts +2 -0
- package/dist/mcp/audit-tools.js +94 -0
- package/dist/mcp/git-tools.js +1 -1
- package/dist/mcp/registry-bridge.d.ts +10 -0
- package/dist/mcp/registry-bridge.js +65 -0
- package/dist/mcp/secrets-tools.js +2 -2
- package/dist/mcp/server.js +16 -82
- package/dist/mcp/testflight-tools.d.ts +2 -0
- package/dist/mcp/testflight-tools.js +52 -0
- package/dist/registry/context.d.ts +7 -0
- package/dist/registry/context.js +37 -0
- package/dist/registry/index.d.ts +5 -0
- package/dist/registry/index.js +44 -0
- package/dist/registry/parse-args.d.ts +13 -0
- package/dist/registry/parse-args.js +74 -0
- package/dist/registry/registry.d.ts +24 -0
- package/dist/registry/registry.js +26 -0
- package/dist/registry/render.d.ts +3 -0
- package/dist/registry/render.js +29 -0
- package/dist/registry/types.d.ts +50 -0
- package/dist/registry/types.js +1 -0
- package/dist/templates/agent-unit.d.ts +5 -0
- package/dist/templates/agent-unit.js +40 -0
- package/dist/templates/app-unit-edit.d.ts +2 -0
- package/dist/templates/app-unit-edit.js +46 -0
- package/dist/templates/compose-edit.d.ts +2 -0
- package/dist/templates/compose-edit.js +156 -0
- package/dist/templates/nginx.js +11 -0
- package/dist/templates/systemd.js +6 -0
- package/dist/tui/components/ArgForm.d.ts +7 -0
- package/dist/tui/components/ArgForm.js +64 -0
- package/dist/tui/components/ArgForm.test.d.ts +1 -0
- package/dist/tui/components/ArgForm.test.js +19 -0
- package/dist/tui/components/KeyHint.js +5 -0
- package/dist/tui/hooks/use-secrets.d.ts +8 -8
- package/dist/tui/hooks/use-secrets.js +7 -7
- package/dist/tui/router.d.ts +1 -0
- package/dist/tui/router.js +26 -9
- package/dist/tui/router.test.d.ts +1 -0
- package/dist/tui/router.test.js +13 -0
- package/dist/tui/routines/components/SignalsGrid.test.js +2 -2
- package/dist/tui/routines/tabs/ScaffoldTab.js +1 -1
- package/dist/tui/tests/redaction-rerender.test.d.ts +1 -0
- package/dist/tui/tests/redaction-rerender.test.js +53 -0
- package/dist/tui/tests/scroll-flicker-proof.test.d.ts +1 -0
- package/dist/tui/tests/scroll-flicker-proof.test.js +145 -0
- package/dist/tui/types.d.ts +1 -1
- package/dist/tui/views/CommandPalette.d.ts +5 -0
- package/dist/tui/views/CommandPalette.js +90 -0
- package/dist/tui/views/CommandPalette.test.d.ts +1 -0
- package/dist/tui/views/CommandPalette.test.js +117 -0
- package/dist/tui/views/Dashboard.js +10 -7
- package/dist/tui/views/HealthView.js +14 -5
- package/dist/tui/views/SecretEdit.js +15 -16
- package/dist/tui/views/SecretEdit.test.d.ts +1 -0
- package/dist/tui/views/SecretEdit.test.js +82 -0
- package/dist/tui/views/SecretsView.js +26 -16
- package/package.json +9 -6
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { loadNotifyConfig, sendNotification } from '../core/notify.js';
|
|
2
|
+
import { success, error, warn } from '../ui/output.js';
|
|
3
|
+
const HELP = `fleet notify - send a message via configured notify adapters
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
fleet notify <message>
|
|
7
|
+
fleet notify - # read message from stdin
|
|
8
|
+
echo "msg" | fleet notify -
|
|
9
|
+
|
|
10
|
+
Reads adapter config from /etc/fleet/notify.json (telegram / bluebubbles).
|
|
11
|
+
Exits 0 if at least one adapter delivered the message, 1 otherwise.
|
|
12
|
+
`;
|
|
13
|
+
async function readStdin() {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
for await (const chunk of process.stdin) {
|
|
16
|
+
chunks.push(chunk);
|
|
17
|
+
}
|
|
18
|
+
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
19
|
+
}
|
|
20
|
+
export async function notifyCommand(args) {
|
|
21
|
+
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
22
|
+
process.stdout.write(HELP);
|
|
23
|
+
if (args.length === 0)
|
|
24
|
+
process.exit(1);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let message;
|
|
28
|
+
if (args[0] === '-') {
|
|
29
|
+
message = await readStdin();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
message = args.join(' ');
|
|
33
|
+
}
|
|
34
|
+
if (!message) {
|
|
35
|
+
error('Empty message');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const config = loadNotifyConfig();
|
|
39
|
+
if (!config) {
|
|
40
|
+
warn('No notify config at /etc/fleet/notify.json — message not sent');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const sent = await sendNotification(config, message);
|
|
44
|
+
if (sent) {
|
|
45
|
+
success('Notification sent');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
error('Failed to send notification (no adapter succeeded)');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
interface PatchSystemdData {
|
|
2
|
+
action: 'patch' | 'rollback';
|
|
3
|
+
changed: number;
|
|
4
|
+
skipped: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const patchSystemdCommand: import("../registry/types.js").CommandDef<PatchSystemdData>;
|
|
7
|
+
export {};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
import { load } from '../core/registry.js';
|
|
3
4
|
import { readServiceFile } from '../core/systemd.js';
|
|
4
5
|
import { execSafe } from '../core/exec.js';
|
|
5
|
-
import {
|
|
6
|
+
import { defineCommand } from '../registry/registry.js';
|
|
6
7
|
const SERVICE_DIR = '/etc/systemd/system';
|
|
7
|
-
|
|
8
|
-
if (args.includes('--rollback'))
|
|
9
|
-
return rollback();
|
|
8
|
+
function runPatch(ctx) {
|
|
10
9
|
const reg = load();
|
|
11
10
|
const dbServiceName = reg.infrastructure.databases.serviceName;
|
|
12
11
|
const appServiceNames = reg.apps.map(a => a.serviceName);
|
|
@@ -20,20 +19,20 @@ export function patchSystemdCommand(args) {
|
|
|
20
19
|
}
|
|
21
20
|
targetMap.set(dbServiceName, { name: dbServiceName, rewriteExecStart: false });
|
|
22
21
|
const targets = Array.from(targetMap.values());
|
|
23
|
-
info
|
|
22
|
+
ctx.log({ level: 'info', message: `patching ${targets.length} service(s)...` });
|
|
24
23
|
let patched = 0;
|
|
25
24
|
let skipped = 0;
|
|
26
25
|
for (const { name, rewriteExecStart } of targets) {
|
|
27
26
|
const path = `${SERVICE_DIR}/${name}.service`;
|
|
28
27
|
const content = readServiceFile(name);
|
|
29
28
|
if (content === null) {
|
|
30
|
-
warn
|
|
29
|
+
ctx.log({ level: 'warn', message: `${name}: no service file found, skipping` });
|
|
31
30
|
skipped++;
|
|
32
31
|
continue;
|
|
33
32
|
}
|
|
34
33
|
let updated = content;
|
|
35
34
|
let changed = false;
|
|
36
|
-
//
|
|
35
|
+
// existing behaviour: add StartLimitBurst if missing (applies to ALL services including databases)
|
|
37
36
|
if (!updated.includes('StartLimitBurst=')) {
|
|
38
37
|
updated = updated.replace(/(\[Service\])/, '$1\nStartLimitBurst=5\nStartLimitIntervalSec=300');
|
|
39
38
|
changed = true;
|
|
@@ -45,7 +44,7 @@ export function patchSystemdCommand(args) {
|
|
|
45
44
|
updated = updated.replace(/^ExecStart=.*$/m, expectedExecStart);
|
|
46
45
|
changed = true;
|
|
47
46
|
}
|
|
48
|
-
//
|
|
47
|
+
// ensure TimeoutStartSec=900
|
|
49
48
|
if (!updated.includes('TimeoutStartSec=900')) {
|
|
50
49
|
if (/^TimeoutStartSec=\d+/m.test(updated)) {
|
|
51
50
|
updated = updated.replace(/^TimeoutStartSec=\d+.*$/m, 'TimeoutStartSec=900');
|
|
@@ -57,37 +56,49 @@ export function patchSystemdCommand(args) {
|
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
if (!changed) {
|
|
60
|
-
info
|
|
59
|
+
ctx.log({ level: 'info', message: `${name}: already patched, skipping` });
|
|
61
60
|
skipped++;
|
|
62
61
|
continue;
|
|
63
62
|
}
|
|
64
|
-
//
|
|
63
|
+
// backup original before overwrite
|
|
65
64
|
try {
|
|
66
65
|
copyFileSync(path, `${path}.bak`);
|
|
67
66
|
}
|
|
68
67
|
catch (err) {
|
|
69
|
-
|
|
68
|
+
ctx.log({
|
|
69
|
+
level: 'warn',
|
|
70
|
+
message: `${name}: failed to create .bak (${err instanceof Error ? err.message : String(err)}); skipping for safety`,
|
|
71
|
+
});
|
|
70
72
|
skipped++;
|
|
71
73
|
continue;
|
|
72
74
|
}
|
|
73
75
|
writeFileSync(path, updated);
|
|
74
|
-
|
|
76
|
+
ctx.log({ level: 'info', message: `${name}: patched` });
|
|
75
77
|
patched++;
|
|
76
78
|
}
|
|
77
79
|
if (patched === 0) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
summary: 'no services needed patching',
|
|
83
|
+
data: { action: 'patch', changed: 0, skipped },
|
|
84
|
+
};
|
|
80
85
|
}
|
|
81
|
-
|
|
86
|
+
ctx.log({ level: 'info', message: 'running systemctl daemon-reload...' });
|
|
82
87
|
const result = execSafe('systemctl', ['daemon-reload']);
|
|
83
|
-
if (result.ok) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
if (!result.ok) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
summary: `patched ${patched} service(s) but daemon-reload failed: ${result.stderr}`,
|
|
92
|
+
data: { action: 'patch', changed: patched, skipped },
|
|
93
|
+
};
|
|
88
94
|
}
|
|
95
|
+
return {
|
|
96
|
+
ok: true,
|
|
97
|
+
summary: `patched ${patched} service(s), skipped ${skipped}`,
|
|
98
|
+
data: { action: 'patch', changed: patched, skipped },
|
|
99
|
+
};
|
|
89
100
|
}
|
|
90
|
-
function
|
|
101
|
+
function runRollback(ctx) {
|
|
91
102
|
const reg = load();
|
|
92
103
|
const serviceNames = [
|
|
93
104
|
...reg.apps.map(a => a.serviceName),
|
|
@@ -104,23 +115,52 @@ function rollback() {
|
|
|
104
115
|
}
|
|
105
116
|
try {
|
|
106
117
|
renameSync(bak, path);
|
|
107
|
-
|
|
118
|
+
ctx.log({ level: 'info', message: `${name}: restored from .bak` });
|
|
108
119
|
restored++;
|
|
109
120
|
}
|
|
110
121
|
catch (err) {
|
|
111
|
-
|
|
122
|
+
ctx.log({
|
|
123
|
+
level: 'error',
|
|
124
|
+
message: `${name}: failed to restore: ${err instanceof Error ? err.message : String(err)}`,
|
|
125
|
+
});
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
if (restored === 0) {
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
return {
|
|
130
|
+
ok: true,
|
|
131
|
+
summary: 'no .bak files found to restore',
|
|
132
|
+
data: { action: 'rollback', changed: 0, skipped: missing },
|
|
133
|
+
};
|
|
117
134
|
}
|
|
118
|
-
|
|
135
|
+
ctx.log({ level: 'info', message: 'running systemctl daemon-reload...' });
|
|
119
136
|
const result = execSafe('systemctl', ['daemon-reload']);
|
|
120
|
-
if (result.ok) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
summary: `restored ${restored} but daemon-reload failed: ${result.stderr}`,
|
|
141
|
+
data: { action: 'rollback', changed: restored, skipped: missing },
|
|
142
|
+
};
|
|
125
143
|
}
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
summary: `restored ${restored}, missing ${missing}`,
|
|
147
|
+
data: { action: 'rollback', changed: restored, skipped: missing },
|
|
148
|
+
};
|
|
126
149
|
}
|
|
150
|
+
export const patchSystemdCommand = defineCommand({
|
|
151
|
+
name: 'patch-systemd',
|
|
152
|
+
summary: 'Add StartLimit settings to all service files',
|
|
153
|
+
args: z.object({ rollback: z.boolean().default(false), yes: z.boolean().default(false) }),
|
|
154
|
+
destructive: true,
|
|
155
|
+
async run(args, ctx) {
|
|
156
|
+
const verb = args.rollback ? 'roll back' : 'patch';
|
|
157
|
+
if (!args.yes && !(await ctx.confirm(`${verb} all fleet systemd unit files?`))) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
summary: 'cancelled',
|
|
161
|
+
data: { action: args.rollback ? 'rollback' : 'patch', changed: 0, skipped: 0 },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return args.rollback ? runRollback(ctx) : runPatch(ctx);
|
|
165
|
+
},
|
|
166
|
+
});
|
package/dist/commands/remove.js
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { load, findApp, removeApp, withRegistry } from '../core/registry.js';
|
|
2
3
|
import { stopService, disableService } from '../core/systemd.js';
|
|
3
4
|
import { AppNotFoundError } from '../core/errors.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
5
|
+
import { defineCommand } from '../registry/registry.js';
|
|
6
|
+
export const removeCommand = defineCommand({
|
|
7
|
+
name: 'remove',
|
|
8
|
+
summary: 'Stop, disable and deregister an app',
|
|
9
|
+
args: z.object({ app: z.string(), yes: z.boolean().default(false) }),
|
|
10
|
+
destructive: true,
|
|
11
|
+
async run(args, ctx) {
|
|
12
|
+
const app = findApp(load(), args.app);
|
|
13
|
+
if (!app) {
|
|
14
|
+
return { ok: false, summary: `app not found: ${args.app}`, data: { app: args.app } };
|
|
15
|
+
}
|
|
16
|
+
if (!args.yes && !(await ctx.confirm(`Remove ${app.name}? This will stop and disable the service.`))) {
|
|
17
|
+
return { ok: false, summary: 'cancelled', data: { app: app.name } };
|
|
18
|
+
}
|
|
19
|
+
// systemctl runs outside the registry lock so we don't hold it while
|
|
20
|
+
// services stop/disable.
|
|
21
|
+
stopService(app.serviceName);
|
|
22
|
+
disableService(app.serviceName);
|
|
23
|
+
try {
|
|
24
|
+
await withRegistry(reg => {
|
|
25
|
+
const fresh = findApp(reg, app.name);
|
|
26
|
+
if (!fresh)
|
|
27
|
+
throw new AppNotFoundError(app.name);
|
|
28
|
+
return removeApp(reg, fresh.name);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
// a concurrent process removed the app between the unlocked preview and
|
|
33
|
+
// the locked mutation — surface it as a graceful expected failure.
|
|
34
|
+
return { ok: false, summary: err instanceof Error ? err.message : String(err), data: { app: app.name } };
|
|
35
|
+
}
|
|
36
|
+
ctx.log({ level: 'warn', message: 'service file not deleted — remove manually if needed' });
|
|
37
|
+
return { ok: true, summary: `removed ${app.name} from registry`, data: { app: app.name } };
|
|
38
|
+
},
|
|
39
|
+
});
|
package/dist/commands/restart.js
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { load, findApp } from '../core/registry.js';
|
|
2
3
|
import { restartService } from '../core/systemd.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
error(`Failed to restart ${app.name}`);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
4
|
+
import { defineCommand } from '../registry/registry.js';
|
|
5
|
+
export const restartCommand = defineCommand({
|
|
6
|
+
name: 'restart',
|
|
7
|
+
summary: 'Restart an app via systemctl',
|
|
8
|
+
args: z.object({ app: z.string() }),
|
|
9
|
+
async run(args) {
|
|
10
|
+
const app = findApp(load(), args.app);
|
|
11
|
+
if (!app) {
|
|
12
|
+
return { ok: false, summary: `app not found: ${args.app}`, data: { app: args.app, service: '' } };
|
|
13
|
+
}
|
|
14
|
+
if (!restartService(app.serviceName)) {
|
|
15
|
+
return { ok: false, summary: `failed to restart ${app.name}`, data: { app: app.name, service: app.serviceName } };
|
|
16
|
+
}
|
|
17
|
+
return { ok: true, summary: `restarted ${app.name}`, data: { app: app.name, service: app.serviceName } };
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { load, findApp } from '../core/registry.js';
|
|
2
3
|
import { execSafe } from '../core/exec.js';
|
|
3
4
|
import { restartService } from '../core/systemd.js';
|
|
4
|
-
|
|
5
|
-
process.stdout.write(`[rollback] ${msg}\n`);
|
|
6
|
-
}
|
|
7
|
-
function logErr(msg) {
|
|
8
|
-
process.stderr.write(`[rollback] ${msg}\n`);
|
|
9
|
-
}
|
|
5
|
+
import { defineCommand } from '../registry/registry.js';
|
|
10
6
|
function resolveImageName(composePath, composeFile) {
|
|
11
7
|
const args = ['compose', ...(composeFile ? ['-f', composeFile] : []), 'config', '--images'];
|
|
12
8
|
const r = execSafe('docker', args, { cwd: composePath, timeout: 15_000 });
|
|
@@ -20,39 +16,34 @@ function splitImageBase(image) {
|
|
|
20
16
|
return image;
|
|
21
17
|
return image.slice(0, lastColon);
|
|
22
18
|
}
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
logErr(`tag restored but service restart failed for ${app.serviceName}`);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
log(`rolled back ${app.name} to ${previous}`);
|
|
58
|
-
}
|
|
19
|
+
export const rollbackCommand = defineCommand({
|
|
20
|
+
name: 'rollback',
|
|
21
|
+
summary: 'Roll back app to previous image',
|
|
22
|
+
args: z.object({ app: z.string(), yes: z.boolean().default(false) }),
|
|
23
|
+
destructive: true,
|
|
24
|
+
async run(args, ctx) {
|
|
25
|
+
const app = findApp(load(), args.app);
|
|
26
|
+
if (!app) {
|
|
27
|
+
return { ok: false, summary: `app not found: ${args.app}`, data: { app: args.app, image: '' } };
|
|
28
|
+
}
|
|
29
|
+
const image = resolveImageName(app.composePath, app.composeFile);
|
|
30
|
+
if (!image) {
|
|
31
|
+
return { ok: false, summary: `could not resolve image name for ${app.name}`, data: { app: app.name, image: '' } };
|
|
32
|
+
}
|
|
33
|
+
const previous = `${splitImageBase(image)}:fleet-previous`;
|
|
34
|
+
if (!execSafe('docker', ['image', 'inspect', previous], { timeout: 10_000 }).ok) {
|
|
35
|
+
return { ok: false, summary: `no previous image found (${previous}) — nothing to roll back to`, data: { app: app.name, image: '' } };
|
|
36
|
+
}
|
|
37
|
+
if (!(args.yes || (await ctx.confirm(`Roll back ${app.name} to ${previous} and restart?`)))) {
|
|
38
|
+
return { ok: false, summary: 'cancelled', data: { app: app.name, image: previous } };
|
|
39
|
+
}
|
|
40
|
+
const tag = execSafe('docker', ['tag', previous, image], { timeout: 10_000 });
|
|
41
|
+
if (!tag.ok) {
|
|
42
|
+
return { ok: false, summary: `docker tag failed: ${tag.stderr || `exit ${tag.exitCode}`}`, data: { app: app.name, image: previous } };
|
|
43
|
+
}
|
|
44
|
+
if (!restartService(app.serviceName)) {
|
|
45
|
+
return { ok: false, summary: `tag restored but service restart failed for ${app.serviceName}`, data: { app: app.name, image: previous } };
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, summary: `rolled back ${app.name} to ${previous}`, data: { app: app.name, image: previous } };
|
|
48
|
+
},
|
|
49
|
+
});
|