@nocobase/cli 2.1.0-beta.20 → 2.1.0-beta.22
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 +32 -50
- package/README.zh-CN.md +29 -46
- package/bin/run.js +15 -0
- package/dist/commands/app/down.js +260 -0
- package/dist/commands/app/info.js +140 -0
- package/dist/commands/app/logs.js +98 -0
- package/dist/commands/app/ps.js +60 -0
- package/dist/commands/app/restart.js +75 -0
- package/dist/commands/app/shared.js +95 -0
- package/dist/commands/app/start.js +252 -0
- package/dist/commands/app/stop.js +98 -0
- package/dist/commands/app/upgrade.js +595 -0
- package/dist/commands/build.js +3 -48
- package/dist/commands/db/shared.js +19 -5
- package/dist/commands/dev.js +3 -140
- package/dist/commands/down.js +3 -184
- package/dist/commands/download.js +4 -856
- package/dist/commands/env/add.js +33 -48
- package/dist/commands/env/auth.js +6 -13
- package/dist/commands/env/list.js +10 -15
- package/dist/commands/env/remove.js +4 -10
- package/dist/commands/env/update.js +7 -13
- package/dist/commands/env/use.js +5 -13
- package/dist/commands/{prompts-stages.js → examples/prompts-stages.js} +3 -3
- package/dist/commands/{prompts-test.js → examples/prompts-test.js} +3 -3
- package/dist/commands/init.js +262 -63
- package/dist/commands/install.js +352 -86
- package/dist/commands/logs.js +3 -81
- package/dist/commands/plugin/disable.js +64 -0
- package/dist/commands/plugin/enable.js +64 -0
- package/dist/commands/plugin/list.js +62 -0
- package/dist/commands/pm/disable.js +3 -54
- package/dist/commands/pm/enable.js +3 -54
- package/dist/commands/pm/list.js +3 -45
- package/dist/commands/ps.js +3 -107
- package/dist/commands/restart.js +3 -65
- package/dist/commands/scaffold/migration.js +1 -1
- package/dist/commands/scaffold/plugin.js +1 -1
- package/dist/commands/self/check.js +1 -1
- package/dist/commands/self/update.js +13 -3
- package/dist/commands/skills/check.js +11 -5
- package/dist/commands/skills/index.js +1 -1
- package/dist/commands/skills/install.js +20 -7
- package/dist/commands/skills/remove.js +71 -0
- package/dist/commands/skills/update.js +27 -7
- package/dist/commands/source/build.js +58 -0
- package/dist/commands/source/dev.js +157 -0
- package/dist/commands/source/download.js +866 -0
- package/dist/commands/source/test.js +467 -0
- package/dist/commands/start.js +3 -202
- package/dist/commands/stop.js +3 -81
- package/dist/commands/test.js +3 -457
- package/dist/commands/upgrade.js +3 -574
- package/dist/help/runtime-help.js +3 -0
- package/dist/lib/api-client.js +3 -2
- package/dist/lib/app-health.js +126 -0
- package/dist/lib/app-managed-resources.js +264 -0
- package/dist/lib/app-runtime.js +16 -5
- package/dist/lib/auth-store.js +162 -43
- package/dist/lib/bootstrap.js +13 -12
- package/dist/lib/cli-home.js +38 -6
- package/dist/lib/cli-locale.js +15 -1
- package/dist/lib/env-auth.js +3 -3
- package/dist/lib/env-config.js +80 -0
- package/dist/lib/generated-command.js +10 -2
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/resource-command.js +10 -2
- package/dist/lib/runtime-generator.js +1 -1
- package/dist/lib/self-manager.js +1 -1
- package/dist/lib/skills-manager.js +173 -79
- package/dist/lib/startup-update.js +203 -0
- package/dist/locale/en-US.json +4 -1
- package/dist/locale/zh-CN.json +4 -1
- package/package.json +26 -3
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ nb init --ui
|
|
|
58
58
|
|
|
59
59
|
`nb init` can either connect to an existing NocoBase app or install a new one.
|
|
60
60
|
When creating a new app, it can also install NocoBase AI coding skills
|
|
61
|
-
(`nocobase/skills`)
|
|
61
|
+
(`nocobase/skills`) globally.
|
|
62
62
|
|
|
63
63
|
### Non-Interactive Setup
|
|
64
64
|
|
|
@@ -113,12 +113,6 @@ If `nb init` was interrupted after the env config had already been saved, you ca
|
|
|
113
113
|
nb init --env app1 --resume
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
The advanced low-level equivalent is:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
nb install --env app1 --resume
|
|
120
|
-
```
|
|
121
|
-
|
|
122
116
|
`--resume` reuses the saved workspace env config for app, source, database, and env connection settings. In interactive mode, it only asks for any missing setup-only values.
|
|
123
117
|
|
|
124
118
|
In non-interactive mode, pass these setup-only flags again because they are not saved in env config:
|
|
@@ -134,40 +128,32 @@ In non-interactive mode, pass these setup-only flags again because they are not
|
|
|
134
128
|
| Command | Description |
|
|
135
129
|
| --- | --- |
|
|
136
130
|
| `nb init` | Set up NocoBase and connect it as a CLI env for coding agents. |
|
|
137
|
-
| `nb
|
|
138
|
-
| `nb
|
|
139
|
-
| `nb start` | Start the selected local app or Docker container. |
|
|
140
|
-
| `nb stop` | Stop the selected local app or Docker container. |
|
|
141
|
-
| `nb restart` | Stop, then start the selected local app or Docker container. |
|
|
142
|
-
| `nb dev` | Run development mode for npm/Git source envs. |
|
|
143
|
-
| `nb logs` | Show app logs for npm/Git or Docker envs. |
|
|
144
|
-
| `nb ps` | Show runtime status for configured envs. |
|
|
131
|
+
| `nb app` | Manage app runtimes: start, stop, restart, logs, status, cleanup, and upgrades. |
|
|
132
|
+
| `nb source` | Manage the local source project: download, develop, build, and test. |
|
|
145
133
|
| `nb db` | Inspect or manage built-in database runtime status for local envs. |
|
|
146
|
-
| `nb upgrade` | Refresh code/image and restart the selected app. |
|
|
147
|
-
| `nb down` | Stop and remove local runtime containers for an env. |
|
|
148
134
|
| `nb env` | Manage saved CLI env connections. |
|
|
149
135
|
| `nb api` | Call NocoBase API resources from the CLI. |
|
|
150
|
-
| `nb
|
|
136
|
+
| `nb plugin` | Manage plugins for the selected NocoBase env. |
|
|
151
137
|
| `nb self` | Check or update the installed NocoBase CLI. |
|
|
152
|
-
| `nb skills` | Check, install, or update NocoBase AI coding skills
|
|
138
|
+
| `nb skills` | Check, install, or update global NocoBase AI coding skills. |
|
|
153
139
|
|
|
154
140
|
Recommended style: use `--env` explicitly for app/runtime commands. `-e` is the short form:
|
|
155
141
|
|
|
156
142
|
```bash
|
|
157
|
-
nb start --env app1
|
|
158
|
-
nb restart --env app1
|
|
159
|
-
nb logs --env app1
|
|
160
|
-
nb ps --env app1
|
|
143
|
+
nb app start --env app1
|
|
144
|
+
nb app restart --env app1
|
|
145
|
+
nb app logs --env app1
|
|
146
|
+
nb app ps --env app1
|
|
161
147
|
nb db ps --env app1
|
|
162
148
|
```
|
|
163
149
|
|
|
164
150
|
Equivalent shorthand examples:
|
|
165
151
|
|
|
166
152
|
```bash
|
|
167
|
-
nb start -e app1
|
|
168
|
-
nb restart -e app1
|
|
169
|
-
nb logs -e app1
|
|
170
|
-
nb upgrade -e app1
|
|
153
|
+
nb app start -e app1
|
|
154
|
+
nb app restart -e app1
|
|
155
|
+
nb app logs -e app1
|
|
156
|
+
nb app upgrade -e app1
|
|
171
157
|
nb db start -e app1
|
|
172
158
|
```
|
|
173
159
|
|
|
@@ -186,7 +172,7 @@ Update the CLI when it is installed globally with npm:
|
|
|
186
172
|
nb self update
|
|
187
173
|
```
|
|
188
174
|
|
|
189
|
-
Check whether the
|
|
175
|
+
Check whether the global NocoBase AI coding skills are installed:
|
|
190
176
|
|
|
191
177
|
```bash
|
|
192
178
|
nb skills check
|
|
@@ -208,18 +194,18 @@ Docker envs are managed through saved Docker containers and images:
|
|
|
208
194
|
|
|
209
195
|
```bash
|
|
210
196
|
nb init --env app1 --yes --source docker --version alpha
|
|
211
|
-
nb start --env app1
|
|
212
|
-
nb restart --env app1
|
|
213
|
-
nb logs --env app1
|
|
214
|
-
nb stop --env app1
|
|
197
|
+
nb app start --env app1
|
|
198
|
+
nb app restart --env app1
|
|
199
|
+
nb app logs --env app1
|
|
200
|
+
nb app stop --env app1
|
|
215
201
|
```
|
|
216
202
|
|
|
217
203
|
Docker downloads support platform selection:
|
|
218
204
|
|
|
219
205
|
```bash
|
|
220
|
-
nb download --source docker --version alpha --docker-platform auto
|
|
221
|
-
nb download --source docker --version alpha --docker-platform linux/amd64
|
|
222
|
-
nb download --source docker --version alpha --docker-platform linux/arm64
|
|
206
|
+
nb source download --source docker --version alpha --docker-platform auto
|
|
207
|
+
nb source download --source docker --version alpha --docker-platform linux/amd64
|
|
208
|
+
nb source download --source docker --version alpha --docker-platform linux/arm64
|
|
223
209
|
```
|
|
224
210
|
|
|
225
211
|
### npm and Git
|
|
@@ -228,11 +214,11 @@ npm and Git envs use a local source directory and can run development mode:
|
|
|
228
214
|
|
|
229
215
|
```bash
|
|
230
216
|
nb init --env app1 --yes --source git --version alpha
|
|
231
|
-
nb dev --env app1
|
|
217
|
+
nb source dev --env app1
|
|
232
218
|
```
|
|
233
219
|
|
|
234
|
-
`nb dev` only supports npm/Git source envs. Docker envs can be inspected with
|
|
235
|
-
`nb logs`, and remote envs only support API/env operations.
|
|
220
|
+
`nb source dev` only supports npm/Git source envs. Docker envs can be inspected with
|
|
221
|
+
`nb app logs`, and remote envs only support API/env operations.
|
|
236
222
|
|
|
237
223
|
### Existing NocoBase App
|
|
238
224
|
|
|
@@ -240,7 +226,7 @@ To connect an existing app, use `nb init` and choose the existing-app setup
|
|
|
240
226
|
path, or add the env directly:
|
|
241
227
|
|
|
242
228
|
```bash
|
|
243
|
-
nb env add app1 --base-url http://localhost:13000/api
|
|
229
|
+
nb env add app1 --api-base-url http://localhost:13000/api
|
|
244
230
|
```
|
|
245
231
|
|
|
246
232
|
`nb env add` will start the authentication flow automatically when needed.
|
|
@@ -250,14 +236,14 @@ nb env add app1 --base-url http://localhost:13000/api
|
|
|
250
236
|
Upgrade refreshes the saved source or image, then restarts the app:
|
|
251
237
|
|
|
252
238
|
```bash
|
|
253
|
-
nb upgrade --env app1
|
|
239
|
+
nb app upgrade --env app1
|
|
254
240
|
```
|
|
255
241
|
|
|
256
242
|
Use `--skip-code-update` or `-s` to restart with the saved local code or Docker
|
|
257
243
|
image without downloading updates first:
|
|
258
244
|
|
|
259
245
|
```bash
|
|
260
|
-
nb upgrade --env app1 -s
|
|
246
|
+
nb app upgrade --env app1 -s
|
|
261
247
|
```
|
|
262
248
|
|
|
263
249
|
## Database Commands
|
|
@@ -283,23 +269,19 @@ Notes:
|
|
|
283
269
|
Bring down a local env:
|
|
284
270
|
|
|
285
271
|
```bash
|
|
286
|
-
nb down --env app1
|
|
272
|
+
nb app down --env app1
|
|
287
273
|
```
|
|
288
274
|
|
|
289
|
-
By default, `nb down` stops the app and removes app/database containers if they
|
|
290
|
-
exist. It keeps
|
|
275
|
+
By default, `nb app down` stops the app and removes app/database containers if they
|
|
276
|
+
exist. For local envs, it also deletes the saved local app files. It keeps storage data and CLI env config.
|
|
291
277
|
|
|
292
278
|
Use explicit flags for destructive cleanup:
|
|
293
279
|
|
|
294
280
|
```bash
|
|
295
|
-
nb down --env app1 --
|
|
296
|
-
nb down --env app1 --remove-source
|
|
297
|
-
nb down --env app1 --remove-env
|
|
281
|
+
nb app down --env app1 --all --yes
|
|
298
282
|
```
|
|
299
283
|
|
|
300
|
-
- `--
|
|
301
|
-
- `--remove-source`: delete the npm/Git source directory.
|
|
302
|
-
- `--remove-env`: remove the saved CLI env config.
|
|
284
|
+
- `--all`: delete everything for the env, including storage data and the saved env config. This requires `--yes`.
|
|
303
285
|
|
|
304
286
|
## Environment Management
|
|
305
287
|
|
package/README.zh-CN.md
CHANGED
|
@@ -53,7 +53,7 @@ nb init
|
|
|
53
53
|
nb init --ui
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`nb init` 可以连接已有的 NocoBase 应用,也可以安装一个新的 NocoBase
|
|
56
|
+
`nb init` 可以连接已有的 NocoBase 应用,也可以安装一个新的 NocoBase 应用。创建新应用时,还可以全局安装 NocoBase AI coding skills (`nocobase/skills`)。
|
|
57
57
|
|
|
58
58
|
### 非交互式初始化
|
|
59
59
|
|
|
@@ -108,12 +108,6 @@ nb init --env app1 --yes --source git --version fix/cli-v2
|
|
|
108
108
|
nb init --env app1 --resume
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
对应的底层高级命令是:
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
nb install --env app1 --resume
|
|
115
|
-
```
|
|
116
|
-
|
|
117
111
|
`--resume` 会复用工作区里已保存的 env config,包括应用、source、数据库和 env 连接相关配置。在交互模式下,只会继续补齐缺失的初始化参数。
|
|
118
112
|
|
|
119
113
|
在非交互模式下,需要重新传这些只用于初始化、不会保存到 env config 的参数:
|
|
@@ -129,38 +123,30 @@ nb install --env app1 --resume
|
|
|
129
123
|
| 命令 | 说明 |
|
|
130
124
|
| --- | --- |
|
|
131
125
|
| `nb init` | 初始化 NocoBase,并连接为 coding agent 可使用的 CLI env。 |
|
|
132
|
-
| `nb
|
|
133
|
-
| `nb
|
|
134
|
-
| `nb start` | 启动选中的本地应用或 Docker 容器。 |
|
|
135
|
-
| `nb stop` | 停止选中的本地应用或 Docker 容器。 |
|
|
136
|
-
| `nb restart` | 先停止,再启动选中的本地应用或 Docker 容器。 |
|
|
137
|
-
| `nb dev` | 为 npm/Git 源码 env 启动开发模式。 |
|
|
138
|
-
| `nb logs` | 查看 npm/Git 或 Docker env 的应用日志。 |
|
|
139
|
-
| `nb ps` | 查看已配置 env 的运行状态。 |
|
|
126
|
+
| `nb app` | 管理应用运行态:启动、停止、重启、日志、状态、清理和升级。 |
|
|
127
|
+
| `nb source` | 管理本地源码工程:下载、开发、构建和测试。 |
|
|
140
128
|
| `nb db` | 查看或管理本地 env 的内置数据库运行状态。 |
|
|
141
|
-
| `nb upgrade` | 更新源码/镜像并重启选中的应用。 |
|
|
142
|
-
| `nb down` | 停止并移除某个 env 的本地运行容器。 |
|
|
143
129
|
| `nb env` | 管理已保存的 CLI env 连接。 |
|
|
144
130
|
| `nb api` | 通过 CLI 调用 NocoBase API 资源。 |
|
|
145
|
-
| `nb
|
|
131
|
+
| `nb plugin` | 管理选中 NocoBase env 的插件。 |
|
|
146
132
|
|
|
147
133
|
推荐在应用和运行时相关命令里显式使用 `--env`;`-e` 是它的简写:
|
|
148
134
|
|
|
149
135
|
```bash
|
|
150
|
-
nb start --env app1
|
|
151
|
-
nb restart --env app1
|
|
152
|
-
nb logs --env app1
|
|
153
|
-
nb ps --env app1
|
|
136
|
+
nb app start --env app1
|
|
137
|
+
nb app restart --env app1
|
|
138
|
+
nb app logs --env app1
|
|
139
|
+
nb app ps --env app1
|
|
154
140
|
nb db ps --env app1
|
|
155
141
|
```
|
|
156
142
|
|
|
157
143
|
等价的简写示例:
|
|
158
144
|
|
|
159
145
|
```bash
|
|
160
|
-
nb start -e app1
|
|
161
|
-
nb restart -e app1
|
|
162
|
-
nb logs -e app1
|
|
163
|
-
nb upgrade -e app1
|
|
146
|
+
nb app start -e app1
|
|
147
|
+
nb app restart -e app1
|
|
148
|
+
nb app logs -e app1
|
|
149
|
+
nb app upgrade -e app1
|
|
164
150
|
nb db start -e app1
|
|
165
151
|
```
|
|
166
152
|
|
|
@@ -172,18 +158,18 @@ Docker env 会通过已保存的 Docker 容器和镜像进行管理:
|
|
|
172
158
|
|
|
173
159
|
```bash
|
|
174
160
|
nb init --env app1 --yes --source docker --version alpha
|
|
175
|
-
nb start --env app1
|
|
176
|
-
nb restart --env app1
|
|
177
|
-
nb logs --env app1
|
|
178
|
-
nb stop --env app1
|
|
161
|
+
nb app start --env app1
|
|
162
|
+
nb app restart --env app1
|
|
163
|
+
nb app logs --env app1
|
|
164
|
+
nb app stop --env app1
|
|
179
165
|
```
|
|
180
166
|
|
|
181
167
|
Docker 下载支持指定平台:
|
|
182
168
|
|
|
183
169
|
```bash
|
|
184
|
-
nb download --source docker --version alpha --docker-platform auto
|
|
185
|
-
nb download --source docker --version alpha --docker-platform linux/amd64
|
|
186
|
-
nb download --source docker --version alpha --docker-platform linux/arm64
|
|
170
|
+
nb source download --source docker --version alpha --docker-platform auto
|
|
171
|
+
nb source download --source docker --version alpha --docker-platform linux/amd64
|
|
172
|
+
nb source download --source docker --version alpha --docker-platform linux/arm64
|
|
187
173
|
```
|
|
188
174
|
|
|
189
175
|
### npm 和 Git
|
|
@@ -192,17 +178,17 @@ npm 和 Git env 会使用本地源码目录,并支持开发模式:
|
|
|
192
178
|
|
|
193
179
|
```bash
|
|
194
180
|
nb init --env app1 --yes --source git --version alpha
|
|
195
|
-
nb dev --env app1
|
|
181
|
+
nb source dev --env app1
|
|
196
182
|
```
|
|
197
183
|
|
|
198
|
-
`nb dev` 只支持 npm/Git 源码 env。Docker env 可以通过 `nb logs` 查看日志,remote env 只支持 API 和 env 相关操作。
|
|
184
|
+
`nb source dev` 只支持 npm/Git 源码 env。Docker env 可以通过 `nb app logs` 查看日志,remote env 只支持 API 和 env 相关操作。
|
|
199
185
|
|
|
200
186
|
### 已有 NocoBase 应用
|
|
201
187
|
|
|
202
188
|
如果要连接已有应用,可以运行 `nb init` 并选择已有应用流程,也可以直接添加 env:
|
|
203
189
|
|
|
204
190
|
```bash
|
|
205
|
-
nb env add app1 --base-url http://localhost:13000/api
|
|
191
|
+
nb env add app1 --api-base-url http://localhost:13000/api
|
|
206
192
|
```
|
|
207
193
|
|
|
208
194
|
`nb env add` 会在需要时自动进入认证流程。
|
|
@@ -212,13 +198,13 @@ nb env add app1 --base-url http://localhost:13000/api
|
|
|
212
198
|
升级会更新已保存的源码或镜像,然后重启应用:
|
|
213
199
|
|
|
214
200
|
```bash
|
|
215
|
-
nb upgrade --env app1
|
|
201
|
+
nb app upgrade --env app1
|
|
216
202
|
```
|
|
217
203
|
|
|
218
204
|
如果只想使用当前已保存的本地源码或 Docker 镜像重启应用,可以使用 `--skip-code-update` 或 `-s`:
|
|
219
205
|
|
|
220
206
|
```bash
|
|
221
|
-
nb upgrade --env app1 -s
|
|
207
|
+
nb app upgrade --env app1 -s
|
|
222
208
|
```
|
|
223
209
|
|
|
224
210
|
## 数据库命令
|
|
@@ -244,22 +230,19 @@ nb db logs --env app1
|
|
|
244
230
|
关闭并清理某个本地 env:
|
|
245
231
|
|
|
246
232
|
```bash
|
|
247
|
-
nb down --env app1
|
|
233
|
+
nb app down --env app1
|
|
248
234
|
```
|
|
249
235
|
|
|
250
|
-
默认情况下,`nb down` 会停止应用,并在存在时移除应用容器和数据库容器。它不会删除用户数据、源码文件和 CLI env 配置。
|
|
236
|
+
默认情况下,`nb app down` 会停止应用,并在存在时移除应用容器和数据库容器。它不会删除用户数据、源码文件和 CLI env 配置。
|
|
237
|
+
对于本地 env,它还会删除已保存的本地 app 目录。默认仍会保留 storage 数据和 CLI env 配置。
|
|
251
238
|
|
|
252
239
|
如需执行破坏性清理,需要显式指定参数:
|
|
253
240
|
|
|
254
241
|
```bash
|
|
255
|
-
nb down --env app1 --
|
|
256
|
-
nb down --env app1 --remove-source
|
|
257
|
-
nb down --env app1 --remove-env
|
|
242
|
+
nb app down --env app1 --all --yes
|
|
258
243
|
```
|
|
259
244
|
|
|
260
|
-
- `--
|
|
261
|
-
- `--remove-source`:删除 npm/Git 源码目录。
|
|
262
|
-
- `--remove-env`:删除已保存的 CLI env 配置。
|
|
245
|
+
- `--all`:删除该 env 的所有内容,包括 storage 数据和已保存的 CLI env 配置。必须同时传 `--yes`。
|
|
263
246
|
|
|
264
247
|
## Env 管理
|
|
265
248
|
|
package/bin/run.js
CHANGED
|
@@ -61,6 +61,10 @@ if (isDev && !process.env._NOCO_CLI_TSX_CHILD) {
|
|
|
61
61
|
|
|
62
62
|
const bootstrapPath = isDev ? path.join(root, 'src/lib/bootstrap.ts') : path.join(root, 'dist/lib/bootstrap.js');
|
|
63
63
|
const { ensureRuntimeFromArgv } = await import(pathToFileURL(bootstrapPath).href);
|
|
64
|
+
const startupUpdatePath = isDev
|
|
65
|
+
? path.join(root, 'src/lib/startup-update.ts')
|
|
66
|
+
: path.join(root, 'dist/lib/startup-update.js');
|
|
67
|
+
const { maybeRunStartupUpdatePrompt } = await import(pathToFileURL(startupUpdatePath).href);
|
|
64
68
|
const { flush, run, settings } = await import('@oclif/core');
|
|
65
69
|
|
|
66
70
|
if (isDev) {
|
|
@@ -102,6 +106,17 @@ function formatCliEntryError(error, argv) {
|
|
|
102
106
|
|
|
103
107
|
try {
|
|
104
108
|
const argv = process.argv.slice(2);
|
|
109
|
+
const startupUpdate = await maybeRunStartupUpdatePrompt(argv);
|
|
110
|
+
if (startupUpdate.kind === 'updated') {
|
|
111
|
+
const result = spawnSync(process.execPath, process.argv.slice(1), {
|
|
112
|
+
stdio: 'inherit',
|
|
113
|
+
env: {
|
|
114
|
+
...process.env,
|
|
115
|
+
NB_SKIP_STARTUP_UPDATE: '1',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
119
|
+
}
|
|
105
120
|
if (argv[0] === 'api') {
|
|
106
121
|
await ensureRuntimeFromArgv(argv, {
|
|
107
122
|
configFile: path.join(root, 'nocobase-ctl.config.json'),
|
|
@@ -0,0 +1,260 @@
|
|
|
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 * as p from '@clack/prompts';
|
|
10
|
+
import { Command, Flags } from '@oclif/core';
|
|
11
|
+
import fsp from 'node:fs/promises';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
15
|
+
import { removeEnv } from '../../lib/auth-store.js';
|
|
16
|
+
import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
|
|
17
|
+
import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
|
|
18
|
+
import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
|
|
19
|
+
function resolveConfiguredPath(value) {
|
|
20
|
+
return resolveConfiguredEnvPath(value);
|
|
21
|
+
}
|
|
22
|
+
function assertSafeRemovalPath(target, label) {
|
|
23
|
+
const resolved = path.resolve(target);
|
|
24
|
+
const cwd = path.resolve(process.cwd());
|
|
25
|
+
const home = path.resolve(os.homedir());
|
|
26
|
+
const root = path.parse(resolved).root;
|
|
27
|
+
if (resolved === root || resolved === cwd || resolved === home) {
|
|
28
|
+
throw new Error(`Refusing to remove ${label} at "${resolved}" because it is too broad.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function removePathIfExists(target, label) {
|
|
32
|
+
const resolved = path.resolve(target);
|
|
33
|
+
assertSafeRemovalPath(resolved, label);
|
|
34
|
+
await fsp.rm(resolved, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
async function dockerContainerExists(containerName) {
|
|
37
|
+
return await commandSucceeds('docker', ['container', 'inspect', containerName]);
|
|
38
|
+
}
|
|
39
|
+
async function removeDockerContainerIfExists(containerName) {
|
|
40
|
+
if (!(await dockerContainerExists(containerName))) {
|
|
41
|
+
return 'missing';
|
|
42
|
+
}
|
|
43
|
+
await run('docker', ['rm', '-f', containerName], {
|
|
44
|
+
errorName: 'docker rm',
|
|
45
|
+
stdio: 'ignore',
|
|
46
|
+
});
|
|
47
|
+
return 'removed';
|
|
48
|
+
}
|
|
49
|
+
async function dockerNetworkExists(networkName) {
|
|
50
|
+
return await commandSucceeds('docker', ['network', 'inspect', networkName]);
|
|
51
|
+
}
|
|
52
|
+
async function dockerNetworkHasActiveEndpoints(networkName) {
|
|
53
|
+
try {
|
|
54
|
+
const output = await commandOutput('docker', [
|
|
55
|
+
'network',
|
|
56
|
+
'inspect',
|
|
57
|
+
networkName,
|
|
58
|
+
'--format',
|
|
59
|
+
'{{json .Containers}}',
|
|
60
|
+
], {
|
|
61
|
+
errorName: 'docker network inspect',
|
|
62
|
+
});
|
|
63
|
+
const containers = JSON.parse(output || '{}');
|
|
64
|
+
return Boolean(containers && typeof containers === 'object' && Object.keys(containers).length > 0);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function removeDockerNetworkIfUnused(networkName) {
|
|
71
|
+
if (!(await dockerNetworkExists(networkName))) {
|
|
72
|
+
return 'missing';
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
await run('docker', ['network', 'rm', networkName], {
|
|
76
|
+
errorName: 'docker network rm',
|
|
77
|
+
stdio: 'ignore',
|
|
78
|
+
});
|
|
79
|
+
return 'removed';
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
if (/has active endpoints|is in use|active endpoints/i.test(message)
|
|
84
|
+
|| (await dockerNetworkExists(networkName) && await dockerNetworkHasActiveEndpoints(networkName))) {
|
|
85
|
+
return 'in-use';
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function builtinDbContainerName(runtime) {
|
|
91
|
+
if (!runtime.env.config.builtinDb) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
95
|
+
const workspaceName = runtime.workspaceName;
|
|
96
|
+
return buildDockerDbContainerName(runtime.envName, dbDialect, workspaceName);
|
|
97
|
+
}
|
|
98
|
+
function managedDockerNetworkName(runtime) {
|
|
99
|
+
return runtime.workspaceName?.trim() || undefined;
|
|
100
|
+
}
|
|
101
|
+
async function confirmDownAll(envName, yes) {
|
|
102
|
+
if (yes) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (!isInteractiveTerminal()) {
|
|
106
|
+
throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --yes to delete everything for "${envName}" in non-interactive mode.`);
|
|
107
|
+
}
|
|
108
|
+
const answer = await p.confirm({
|
|
109
|
+
message: `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
|
|
110
|
+
active: 'yes',
|
|
111
|
+
inactive: 'no',
|
|
112
|
+
initialValue: false,
|
|
113
|
+
});
|
|
114
|
+
if (p.isCancel(answer)) {
|
|
115
|
+
p.cancel('Down cancelled.');
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return answer;
|
|
119
|
+
}
|
|
120
|
+
function formatDownFailure(envName, message) {
|
|
121
|
+
return [
|
|
122
|
+
`Couldn't bring down NocoBase for "${envName}".`,
|
|
123
|
+
'Some local runtime resources may still exist. Check Docker or the local app process, then try again.',
|
|
124
|
+
`Details: ${message}`,
|
|
125
|
+
].join('\n');
|
|
126
|
+
}
|
|
127
|
+
export default class AppDown extends Command {
|
|
128
|
+
static hidden = false;
|
|
129
|
+
static description = 'Bring down the selected env by removing runtime containers and the saved local app files. Storage data and env config are kept unless explicitly requested.';
|
|
130
|
+
static examples = [
|
|
131
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
132
|
+
'<%= config.bin %> <%= command.id %> --env app1 --all --yes',
|
|
133
|
+
];
|
|
134
|
+
static flags = {
|
|
135
|
+
env: Flags.string({
|
|
136
|
+
char: 'e',
|
|
137
|
+
description: 'CLI env name to bring down. Defaults to the current env when omitted',
|
|
138
|
+
}),
|
|
139
|
+
all: Flags.boolean({
|
|
140
|
+
description: 'Delete everything for this env, including storage data and the saved env config',
|
|
141
|
+
default: false,
|
|
142
|
+
}),
|
|
143
|
+
yes: Flags.boolean({
|
|
144
|
+
char: 'y',
|
|
145
|
+
description: 'Confirm destructive actions without prompting',
|
|
146
|
+
default: false,
|
|
147
|
+
}),
|
|
148
|
+
verbose: Flags.boolean({
|
|
149
|
+
description: 'Show raw output from shutdown commands',
|
|
150
|
+
default: false,
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
async run() {
|
|
154
|
+
const { flags } = await this.parse(AppDown);
|
|
155
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
156
|
+
const removeData = Boolean(flags.all);
|
|
157
|
+
const removeEnvConfig = Boolean(flags.all);
|
|
158
|
+
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
159
|
+
if (!runtime) {
|
|
160
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
161
|
+
}
|
|
162
|
+
if (runtime.kind === 'http') {
|
|
163
|
+
this.error([
|
|
164
|
+
`Can't bring down "${runtime.envName}" from this machine.`,
|
|
165
|
+
'This env only has an API connection, so there is no saved local app, Docker app, or managed database to remove here.',
|
|
166
|
+
'Use `nb env remove` if you only want to remove the CLI connection config.',
|
|
167
|
+
].join('\n'));
|
|
168
|
+
}
|
|
169
|
+
if (runtime.kind === 'ssh') {
|
|
170
|
+
this.error([
|
|
171
|
+
`Can't bring down "${runtime.envName}" yet.`,
|
|
172
|
+
'SSH env support is reserved but not implemented yet.',
|
|
173
|
+
'Use `nb env remove` if you only want to remove the saved CLI config for now.',
|
|
174
|
+
].join('\n'));
|
|
175
|
+
}
|
|
176
|
+
if (flags.all) {
|
|
177
|
+
let confirmed = false;
|
|
178
|
+
try {
|
|
179
|
+
confirmed = await confirmDownAll(runtime.envName, flags.yes);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
183
|
+
}
|
|
184
|
+
if (!confirmed) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
if (runtime.kind === 'docker') {
|
|
190
|
+
startTask(`Removing Docker app container for "${runtime.envName}"...`);
|
|
191
|
+
const state = await removeDockerContainerIfExists(runtime.containerName);
|
|
192
|
+
succeedTask(state === 'removed'
|
|
193
|
+
? `Docker app container removed for "${runtime.envName}".`
|
|
194
|
+
: `No Docker app container found for "${runtime.envName}".`);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
|
|
198
|
+
await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
|
|
199
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
200
|
+
});
|
|
201
|
+
succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
|
|
202
|
+
}
|
|
203
|
+
if (runtime.kind === 'local' || runtime.kind === 'docker') {
|
|
204
|
+
const dbContainerName = builtinDbContainerName(runtime);
|
|
205
|
+
if (dbContainerName) {
|
|
206
|
+
startTask(`Removing built-in database container for "${runtime.envName}"...`);
|
|
207
|
+
const state = await removeDockerContainerIfExists(dbContainerName);
|
|
208
|
+
succeedTask(state === 'removed'
|
|
209
|
+
? `Built-in database container removed for "${runtime.envName}".`
|
|
210
|
+
: `No built-in database container found for "${runtime.envName}".`);
|
|
211
|
+
}
|
|
212
|
+
const networkName = managedDockerNetworkName(runtime);
|
|
213
|
+
if (networkName) {
|
|
214
|
+
startTask(`Removing Docker network for "${runtime.envName}" if unused...`);
|
|
215
|
+
const state = await removeDockerNetworkIfUnused(networkName);
|
|
216
|
+
if (state === 'removed') {
|
|
217
|
+
succeedTask(`Docker network removed for "${runtime.envName}".`);
|
|
218
|
+
}
|
|
219
|
+
else if (state === 'missing') {
|
|
220
|
+
succeedTask(`No Docker network found for "${runtime.envName}".`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
succeedTask(`Docker network is still in use for "${runtime.envName}". Keeping it.`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (runtime.kind === 'local') {
|
|
228
|
+
const localAppPath = resolveConfiguredPath(runtime.env.config.appRootPath) || runtime.projectRoot;
|
|
229
|
+
if (localAppPath) {
|
|
230
|
+
startTask(`Removing local app files for "${runtime.envName}"...`);
|
|
231
|
+
await removePathIfExists(localAppPath, `app files for "${runtime.envName}"`);
|
|
232
|
+
succeedTask(`Local app files removed for "${runtime.envName}".`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
printInfo(`No saved local app path found for "${runtime.envName}".`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (removeData) {
|
|
239
|
+
const configuredStoragePath = resolveConfiguredPath(runtime.env.config.storagePath);
|
|
240
|
+
if (configuredStoragePath) {
|
|
241
|
+
startTask(`Removing storage data for "${runtime.envName}"...`);
|
|
242
|
+
await removePathIfExists(configuredStoragePath, `storage data for "${runtime.envName}"`);
|
|
243
|
+
succeedTask(`Storage data removed for "${runtime.envName}".`);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
printInfo(`No saved storage path found for "${runtime.envName}".`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (removeEnvConfig) {
|
|
250
|
+
startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
|
|
251
|
+
const result = await removeEnv(runtime.envName);
|
|
252
|
+
succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.currentEnv ? ` (current env: ${result.currentEnv})` : ''}.`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
failTask(`Failed to bring down NocoBase for "${runtime.envName}".`);
|
|
257
|
+
this.error(formatDownFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|