@nocobase/cli 2.1.0-alpha.40 → 2.1.0-alpha.46
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 +20 -8
- package/README.zh-CN.md +20 -8
- package/bin/run.js +9 -37
- package/dist/commands/app/destroy.js +225 -0
- package/dist/commands/app/down.js +24 -254
- package/dist/commands/app/restart.js +12 -5
- package/dist/commands/app/shared.js +122 -0
- package/dist/commands/app/start.js +41 -59
- package/dist/commands/app/stop.js +72 -26
- package/dist/commands/app/upgrade.js +312 -429
- package/dist/commands/config/delete.js +8 -1
- package/dist/commands/config/get.js +5 -0
- package/dist/commands/config/set.js +10 -1
- package/dist/commands/db/ps.js +5 -18
- package/dist/commands/db/start.js +23 -8
- package/dist/commands/env/add.js +66 -6
- package/dist/commands/env/auth.js +86 -27
- package/dist/commands/env/info.js +52 -8
- package/dist/commands/env/list.js +6 -7
- package/dist/commands/env/shared.js +41 -3
- package/dist/commands/env/status.js +10 -7
- package/dist/commands/init.js +214 -136
- package/dist/commands/install.js +364 -294
- package/dist/commands/license/plugins/shared.js +9 -3
- package/dist/commands/license/plugins/sync.js +54 -25
- package/dist/commands/self/update.js +58 -1
- package/dist/commands/source/dev.js +5 -5
- package/dist/commands/source/download.js +29 -25
- package/dist/generated/command-registry.js +3 -2
- package/dist/lib/api-client.js +6 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +31 -18
- package/dist/lib/app-managed-resources.js +1 -1
- package/dist/lib/app-runtime.js +2 -2
- package/dist/lib/auth-store.js +56 -19
- package/dist/lib/backup.js +3 -3
- package/dist/lib/bootstrap.js +14 -8
- package/dist/lib/cli-config.js +137 -4
- package/dist/lib/cli-entry-error.js +44 -0
- package/dist/lib/cli-locale.js +19 -7
- package/dist/lib/db-connection-check.js +61 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +98 -11
- package/dist/lib/env-config.js +3 -2
- package/dist/lib/env-guard.js +1 -1
- package/dist/lib/generated-command.js +17 -0
- package/dist/lib/prompt-validators.js +23 -5
- package/dist/lib/prompt-web-ui.js +143 -19
- package/dist/lib/run-npm.js +133 -23
- package/dist/lib/skills-manager.js +80 -4
- package/dist/lib/source-publish.js +4 -1
- package/dist/lib/startup-update.js +74 -50
- package/dist/lib/ui.js +4 -1
- package/dist/locale/en-US.json +92 -5
- package/dist/locale/zh-CN.json +92 -5
- package/package.json +34 -2
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ When creating a new app, it can also install NocoBase AI coding skills
|
|
|
61
61
|
(`nocobase/skills`) globally.
|
|
62
62
|
|
|
63
63
|
Use `--skip-skills` if the skills are managed separately, or when running in CI
|
|
64
|
-
or offline environments where `nb init` should not install
|
|
64
|
+
or offline environments where `nb init` should not install them.
|
|
65
65
|
|
|
66
66
|
### Non-Interactive Setup
|
|
67
67
|
|
|
@@ -265,28 +265,40 @@ nb db logs --env app1
|
|
|
265
265
|
|
|
266
266
|
Notes:
|
|
267
267
|
|
|
268
|
+
- `nb db start` can also recreate the saved built-in database container when it has been removed.
|
|
268
269
|
- `nb db start` and `nb db stop` only work for envs created with the built-in database option enabled.
|
|
269
270
|
- `nb db logs` only works for envs created with the built-in database option enabled.
|
|
270
271
|
- `nb db ps` can also show `external` or `remote` status for envs that do not have a CLI-managed database container.
|
|
271
272
|
|
|
272
273
|
## Cleanup
|
|
273
274
|
|
|
274
|
-
|
|
275
|
+
Stop only the app runtime:
|
|
275
276
|
|
|
276
277
|
```bash
|
|
277
|
-
nb app
|
|
278
|
+
nb app stop --env app1
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Stop the app runtime and also remove the CLI-managed built-in database runtime when present:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
nb app stop --env app1 --with-db
|
|
278
285
|
```
|
|
279
286
|
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
- `nb app stop` keeps storage data and the saved CLI env config.
|
|
288
|
+
- Docker envs remove the saved app container when stopped.
|
|
289
|
+
- `--with-db` only affects CLI-managed built-in databases. External databases are not touched.
|
|
282
290
|
|
|
283
|
-
|
|
291
|
+
Destroy the env's managed local resources:
|
|
284
292
|
|
|
285
293
|
```bash
|
|
286
|
-
nb app
|
|
294
|
+
nb app destroy --env app1
|
|
295
|
+
nb app destroy --env app1 --force
|
|
287
296
|
```
|
|
288
297
|
|
|
289
|
-
-
|
|
298
|
+
- `nb app destroy` removes managed runtime resources, storage data, and the saved CLI env config.
|
|
299
|
+
- For downloaded npm/Git envs, `nb app destroy` also removes the saved local app files. Custom local app directories are kept.
|
|
300
|
+
- In interactive terminals, `nb app destroy` requires a strong confirmation prompt. In non-interactive mode, re-run with `--env <name> --force`.
|
|
301
|
+
- `nb app down` is deprecated. Use `nb app stop --with-db` for runtime cleanup, or `nb app destroy` for destructive cleanup.
|
|
290
302
|
|
|
291
303
|
## Environment Management
|
|
292
304
|
|
package/README.zh-CN.md
CHANGED
|
@@ -55,7 +55,7 @@ nb init --ui
|
|
|
55
55
|
|
|
56
56
|
`nb init` 可以连接已有的 NocoBase 应用,也可以安装一个新的 NocoBase 应用。创建新应用时,还可以全局安装 NocoBase AI coding skills (`nocobase/skills`)。
|
|
57
57
|
|
|
58
|
-
如果已经自行管理 skills,或在 CI、离线环境中运行,不希望 `nb init`
|
|
58
|
+
如果已经自行管理 skills,或在 CI、离线环境中运行,不希望 `nb init` 安装 skills,可以传入 `--skip-skills`。
|
|
59
59
|
|
|
60
60
|
### 非交互式初始化
|
|
61
61
|
|
|
@@ -225,28 +225,40 @@ nb db logs --env app1
|
|
|
225
225
|
|
|
226
226
|
说明:
|
|
227
227
|
|
|
228
|
+
- `nb db start` 在内置数据库容器已被删除时,也可以根据已保存的 env 配置自动恢复它。
|
|
228
229
|
- `nb db start` 和 `nb db stop` 只适用于启用了内置数据库的 env。
|
|
229
230
|
- `nb db logs` 只适用于启用了内置数据库的 env。
|
|
230
231
|
- 对于没有 CLI 托管数据库容器的 env,`nb db ps` 也会显示 `external` 或 `remote` 状态。
|
|
231
232
|
|
|
232
233
|
## 清理
|
|
233
234
|
|
|
234
|
-
|
|
235
|
+
只停止应用运行态:
|
|
235
236
|
|
|
236
237
|
```bash
|
|
237
|
-
nb app
|
|
238
|
+
nb app stop --env app1
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
如果还要一并移除 CLI 托管的内置数据库运行态,可以使用:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
nb app stop --env app1 --with-db
|
|
238
245
|
```
|
|
239
246
|
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
- `nb app stop` 会保留 storage 数据和已保存的 CLI env 配置。
|
|
248
|
+
- Docker env 停止时会移除已保存的 app container。
|
|
249
|
+
- `--with-db` 只会影响 CLI 托管的内置数据库,external db 不会被处理。
|
|
242
250
|
|
|
243
|
-
|
|
251
|
+
销毁该 env 的本地托管资源:
|
|
244
252
|
|
|
245
253
|
```bash
|
|
246
|
-
nb app
|
|
254
|
+
nb app destroy --env app1
|
|
255
|
+
nb app destroy --env app1 --force
|
|
247
256
|
```
|
|
248
257
|
|
|
249
|
-
-
|
|
258
|
+
- `nb app destroy` 会删除托管的运行时资源、storage 数据以及已保存的 CLI env 配置。
|
|
259
|
+
- 对于通过 npm/Git 下载的 env,`nb app destroy` 还会删除已保存的本地 app 文件。自定义的本地源码目录会被保留。
|
|
260
|
+
- 在交互终端中,`nb app destroy` 需要强确认;在非交互模式下,需要显式传入 `--env <name> --force`。
|
|
261
|
+
- `nb app down` 已废弃。运行态清理请使用 `nb app stop --with-db`,彻底销毁请使用 `nb app destroy`。
|
|
250
262
|
|
|
251
263
|
## Env 管理
|
|
252
264
|
|
package/bin/run.js
CHANGED
|
@@ -4,6 +4,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import pc from 'picocolors';
|
|
7
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
9
|
import { normalizeNodeOptions, normalizeSessionEnv } from './session-env.js';
|
|
9
10
|
|
|
@@ -68,49 +69,20 @@ const { ensureRuntimeFromArgv } = await import(pathToFileURL(bootstrapPath).href
|
|
|
68
69
|
const startupUpdatePath = isDev
|
|
69
70
|
? path.join(root, 'src/lib/startup-update.ts')
|
|
70
71
|
: path.join(root, 'dist/lib/startup-update.js');
|
|
71
|
-
const {
|
|
72
|
+
const { maybeRunStartupUpdate } = await import(pathToFileURL(startupUpdatePath).href);
|
|
73
|
+
const cliEntryErrorPath = isDev
|
|
74
|
+
? path.join(root, 'src/lib/cli-entry-error.ts')
|
|
75
|
+
: path.join(root, 'dist/lib/cli-entry-error.js');
|
|
76
|
+
const { formatCliEntryError } = await import(pathToFileURL(cliEntryErrorPath).href);
|
|
72
77
|
const { flush, run, settings } = await import('@oclif/core');
|
|
73
78
|
|
|
74
79
|
if (isDev) {
|
|
75
80
|
settings.debug = true;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
function getCommandToken(argv) {
|
|
79
|
-
const tokens = [];
|
|
80
|
-
|
|
81
|
-
for (const token of argv) {
|
|
82
|
-
if (!token || token.startsWith('-')) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
tokens.push(token);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (tokens[0] === 'api') {
|
|
90
|
-
return tokens[1] ?? tokens[0];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return tokens[0];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function formatCliEntryError(error, argv) {
|
|
97
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
98
|
-
const missingCommandMatch = message.match(/^Command (.+) not found\.$/);
|
|
99
|
-
if (missingCommandMatch) {
|
|
100
|
-
const commandToken = getCommandToken(argv) ?? missingCommandMatch[1];
|
|
101
|
-
return [
|
|
102
|
-
`Unknown command: \`${commandToken}\`.`,
|
|
103
|
-
'If this is a built-in command or a typo, run `nb --help` to inspect available commands.',
|
|
104
|
-
`If \`${commandToken}\` should be a runtime command from your NocoBase app, run \`nb env update\` and try again.`,
|
|
105
|
-
].join('\n');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return message;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
83
|
try {
|
|
112
84
|
const argv = process.argv.slice(2);
|
|
113
|
-
const startupUpdate = await
|
|
85
|
+
const startupUpdate = await maybeRunStartupUpdate(argv);
|
|
114
86
|
if (startupUpdate.kind === 'updated') {
|
|
115
87
|
const result = spawnSync(process.execPath, process.argv.slice(1), {
|
|
116
88
|
stdio: 'inherit',
|
|
@@ -130,6 +102,6 @@ try {
|
|
|
130
102
|
flush();
|
|
131
103
|
} catch (error) {
|
|
132
104
|
const message = formatCliEntryError(error, process.argv.slice(2));
|
|
133
|
-
console.error(message);
|
|
134
|
-
process.
|
|
105
|
+
console.error(pc.red(message));
|
|
106
|
+
process.exit(1);
|
|
135
107
|
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { removeEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
12
|
+
import { hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
13
|
+
import { input } from "../../lib/inquirer.js";
|
|
14
|
+
import { announceTargetEnv, failTask, isInteractiveTerminal, printInfo, startTask, succeedTask } from '../../lib/ui.js';
|
|
15
|
+
import { builtinDbContainerName, managedDockerNetworkName, removeDockerContainerIfExists, removeDockerNetworkIfUnused, removePathIfExists, resolveConfiguredPath, resolveManagedLocalAppPath, shouldRemoveManagedLocalAppFiles, } from './shared.js';
|
|
16
|
+
function formatDestroyFailure(envName, message) {
|
|
17
|
+
return [
|
|
18
|
+
`Couldn't destroy env "${envName}".`,
|
|
19
|
+
'Some managed local resources may still exist. Check Docker, local app files, and storage data, then try again.',
|
|
20
|
+
`Details: ${message}`,
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
function formatDestroyForceRequiredMessage(envName, explicitEnv) {
|
|
24
|
+
if (!explicitEnv) {
|
|
25
|
+
return [
|
|
26
|
+
`Refusing to destroy current env "${envName}" without explicit selection in non-interactive mode.`,
|
|
27
|
+
`Re-run with \`--env ${envName} --force\` to destroy this env.`,
|
|
28
|
+
].join('\n');
|
|
29
|
+
}
|
|
30
|
+
return [
|
|
31
|
+
`Refusing to destroy env "${envName}" without confirmation in non-interactive mode.`,
|
|
32
|
+
`Re-run with \`--env ${envName} --force\` to destroy this env.`,
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
function buildDestroyPrompt(runtime, options) {
|
|
36
|
+
const subject = options.explicitEnv ? `env "${runtime.envName}"` : `current env "${runtime.envName}"`;
|
|
37
|
+
const lines = [`Destroy ${subject}?`];
|
|
38
|
+
if (runtime.kind === 'http' || runtime.kind === 'ssh') {
|
|
39
|
+
lines.push('This env has no CLI-managed local app or database runtime on this machine.');
|
|
40
|
+
lines.push('Only the saved CLI env config will be removed. External services are not touched.');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
lines.push('This removes managed app runtime resources.');
|
|
44
|
+
if (runtime.env.config.builtinDb) {
|
|
45
|
+
lines.push('CLI-managed built-in database runtime will also be removed.');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
lines.push('External database resources are not managed by the CLI and will be left untouched.');
|
|
49
|
+
}
|
|
50
|
+
if (options.removesManagedLocalAppFiles) {
|
|
51
|
+
lines.push('CLI-managed local app files will also be removed.');
|
|
52
|
+
}
|
|
53
|
+
else if (runtime.kind === 'local') {
|
|
54
|
+
lines.push('Custom local app source files will be kept.');
|
|
55
|
+
}
|
|
56
|
+
if (options.removesStorageData) {
|
|
57
|
+
lines.push('Storage data will be removed.');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push('No saved storage path was found for this env.');
|
|
61
|
+
}
|
|
62
|
+
lines.push('The saved CLI env config will be removed.');
|
|
63
|
+
}
|
|
64
|
+
lines.push(`Type "${runtime.envName}" to confirm:`);
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
async function confirmDestroy(runtime, options) {
|
|
68
|
+
if (!isInteractiveTerminal()) {
|
|
69
|
+
if (!options.explicitEnv) {
|
|
70
|
+
throw new Error(formatDestroyForceRequiredMessage(runtime.envName, false));
|
|
71
|
+
}
|
|
72
|
+
if (options.force) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
throw new Error(formatDestroyForceRequiredMessage(runtime.envName, options.explicitEnv));
|
|
76
|
+
}
|
|
77
|
+
if (options.force) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
await input({
|
|
82
|
+
message: buildDestroyPrompt(runtime, options),
|
|
83
|
+
required: true,
|
|
84
|
+
validate: (value) => (value.trim() === runtime.envName ? true : `Type "${runtime.envName}" to confirm.`),
|
|
85
|
+
placeholder: runtime.envName,
|
|
86
|
+
});
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export default class AppDestroy extends Command {
|
|
94
|
+
static hidden = false;
|
|
95
|
+
static description = 'Destroy the selected env by removing managed runtime resources, storage data, and the saved CLI env config.';
|
|
96
|
+
static examples = [
|
|
97
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
98
|
+
'<%= config.bin %> <%= command.id %> --env app1 --force',
|
|
99
|
+
];
|
|
100
|
+
static flags = {
|
|
101
|
+
env: Flags.string({
|
|
102
|
+
char: 'e',
|
|
103
|
+
description: 'CLI env name to destroy. Defaults to the current env when omitted in interactive mode',
|
|
104
|
+
}),
|
|
105
|
+
force: Flags.boolean({
|
|
106
|
+
char: 'f',
|
|
107
|
+
description: 'Skip confirmation and destroy the selected env immediately',
|
|
108
|
+
default: false,
|
|
109
|
+
}),
|
|
110
|
+
verbose: Flags.boolean({
|
|
111
|
+
description: 'Show raw output from destruction commands',
|
|
112
|
+
default: false,
|
|
113
|
+
}),
|
|
114
|
+
};
|
|
115
|
+
async run() {
|
|
116
|
+
const { flags } = await this.parse(AppDestroy);
|
|
117
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
118
|
+
const explicitEnv = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
|
|
119
|
+
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
120
|
+
if (!runtime) {
|
|
121
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
122
|
+
}
|
|
123
|
+
const removesManagedLocalAppFiles = runtime.kind === 'local' &&
|
|
124
|
+
Boolean(resolveManagedLocalAppPath(runtime)) &&
|
|
125
|
+
shouldRemoveManagedLocalAppFiles(runtime);
|
|
126
|
+
const removesStorageData = (runtime.kind === 'local' || runtime.kind === 'docker') &&
|
|
127
|
+
Boolean(resolveConfiguredPath(runtime.env.config.storagePath));
|
|
128
|
+
let confirmed = false;
|
|
129
|
+
try {
|
|
130
|
+
confirmed = await confirmDestroy(runtime, {
|
|
131
|
+
explicitEnv,
|
|
132
|
+
force: flags.force,
|
|
133
|
+
removesManagedLocalAppFiles,
|
|
134
|
+
removesStorageData,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
139
|
+
}
|
|
140
|
+
if (!confirmed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
announceTargetEnv(runtime.envName);
|
|
144
|
+
try {
|
|
145
|
+
if (runtime.kind === 'docker') {
|
|
146
|
+
startTask(`Removing Docker app container for "${runtime.envName}"...`);
|
|
147
|
+
const state = await removeDockerContainerIfExists(runtime.containerName, {
|
|
148
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
149
|
+
});
|
|
150
|
+
succeedTask(state === 'removed'
|
|
151
|
+
? `Docker app container removed for "${runtime.envName}".`
|
|
152
|
+
: `No Docker app container found for "${runtime.envName}".`);
|
|
153
|
+
}
|
|
154
|
+
else if (runtime.kind === 'local') {
|
|
155
|
+
startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
|
|
156
|
+
await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
|
|
157
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
158
|
+
});
|
|
159
|
+
succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
|
|
160
|
+
}
|
|
161
|
+
if (runtime.kind === 'local' || runtime.kind === 'docker') {
|
|
162
|
+
const dbContainer = builtinDbContainerName(runtime);
|
|
163
|
+
if (dbContainer) {
|
|
164
|
+
startTask(`Removing built-in database container for "${runtime.envName}"...`);
|
|
165
|
+
const state = await removeDockerContainerIfExists(dbContainer, {
|
|
166
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
167
|
+
});
|
|
168
|
+
succeedTask(state === 'removed'
|
|
169
|
+
? `Built-in database container removed for "${runtime.envName}".`
|
|
170
|
+
: `No built-in database container found for "${runtime.envName}".`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
printInfo(`External database resources for "${runtime.envName}" were left untouched.`);
|
|
174
|
+
}
|
|
175
|
+
const networkName = managedDockerNetworkName(runtime);
|
|
176
|
+
if (networkName) {
|
|
177
|
+
startTask(`Removing Docker network for "${runtime.envName}" if unused...`);
|
|
178
|
+
const state = await removeDockerNetworkIfUnused(networkName);
|
|
179
|
+
if (state === 'removed') {
|
|
180
|
+
succeedTask(`Docker network removed for "${runtime.envName}".`);
|
|
181
|
+
}
|
|
182
|
+
else if (state === 'missing') {
|
|
183
|
+
succeedTask(`No Docker network found for "${runtime.envName}".`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
succeedTask(`Docker network is still in use for "${runtime.envName}". Keeping it.`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (runtime.kind === 'local') {
|
|
190
|
+
const localAppPath = resolveManagedLocalAppPath(runtime);
|
|
191
|
+
if (localAppPath && removesManagedLocalAppFiles) {
|
|
192
|
+
startTask(`Removing managed local app files for "${runtime.envName}"...`);
|
|
193
|
+
await removePathIfExists(localAppPath, `managed app files for "${runtime.envName}"`);
|
|
194
|
+
succeedTask(`Managed local app files removed for "${runtime.envName}".`);
|
|
195
|
+
}
|
|
196
|
+
else if (localAppPath) {
|
|
197
|
+
printInfo(`Keeping custom local app files for "${runtime.envName}" at "${localAppPath}".`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
printInfo(`No saved local app path found for "${runtime.envName}".`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const configuredStoragePath = resolveConfiguredPath(runtime.env.config.storagePath);
|
|
204
|
+
if (configuredStoragePath) {
|
|
205
|
+
startTask(`Removing storage data for "${runtime.envName}"...`);
|
|
206
|
+
await removePathIfExists(configuredStoragePath, `storage data for "${runtime.envName}"`);
|
|
207
|
+
succeedTask(`Storage data removed for "${runtime.envName}".`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
printInfo(`No saved storage path found for "${runtime.envName}".`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
printInfo(`No CLI-managed local app or database runtime exists for "${runtime.envName}" on this machine.`);
|
|
215
|
+
}
|
|
216
|
+
startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
|
|
217
|
+
const result = await removeEnv(runtime.envName);
|
|
218
|
+
succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.lastEnv ? ` (last env: ${result.lastEnv})` : ''}.`);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
failTask(`Failed to destroy env "${runtime.envName}".`);
|
|
222
|
+
this.error(formatDestroyFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|