@nocobase/cli 2.1.0-beta.29 → 2.1.0-beta.30
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 +14 -0
- package/README.zh-CN.md +14 -0
- package/bin/run.js +3 -0
- package/bin/session-env.js +27 -0
- package/dist/commands/app/down.js +47 -9
- package/dist/commands/app/logs.js +17 -0
- package/dist/commands/app/restart.js +23 -1
- package/dist/commands/app/start.js +17 -0
- package/dist/commands/app/stop.js +17 -0
- package/dist/commands/app/upgrade.js +22 -2
- package/dist/commands/db/check.js +6 -4
- package/dist/commands/db/ps.js +1 -1
- package/dist/commands/env/add.js +3 -2
- package/dist/commands/env/auth.js +1 -1
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +4 -3
- package/dist/commands/env/list.js +8 -14
- package/dist/commands/env/remove.js +2 -2
- package/dist/commands/env/status.js +90 -0
- package/dist/commands/env/update.js +1 -1
- package/dist/commands/env/use.js +11 -1
- package/dist/commands/install.js +10 -4
- package/dist/commands/license/activate.js +20 -24
- package/dist/commands/license/id.js +17 -2
- package/dist/commands/license/plugins/clean.js +17 -2
- package/dist/commands/license/plugins/list.js +17 -2
- package/dist/commands/license/plugins/sync.js +22 -5
- package/dist/commands/license/shared.js +15 -6
- package/dist/commands/license/status.js +17 -2
- package/dist/commands/plugin/disable.js +25 -4
- package/dist/commands/plugin/enable.js +25 -4
- package/dist/commands/plugin/list.js +25 -4
- package/dist/commands/session/id.js +24 -0
- package/dist/commands/session/remove.js +57 -0
- package/dist/commands/session/setup.js +62 -0
- package/dist/commands/source/dev.js +19 -1
- package/dist/commands/source/download.js +10 -8
- package/dist/lib/app-managed-resources.js +5 -3
- package/dist/lib/app-runtime.js +1 -1
- package/dist/lib/auth-store.js +28 -11
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/env-guard.js +61 -0
- package/dist/lib/generated-command.js +16 -0
- package/dist/lib/plugin-storage.js +1 -64
- package/dist/lib/resource-command.js +15 -0
- package/dist/lib/runtime-generator.js +1 -1
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -296,6 +296,20 @@ Show the current env:
|
|
|
296
296
|
nb env
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
+
Show only the current env name:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
nb env current
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Set up shell session integration for `NB_SESSION_ID`:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
nb session setup
|
|
309
|
+
nb session id
|
|
310
|
+
nb session remove
|
|
311
|
+
```
|
|
312
|
+
|
|
299
313
|
List configured envs with token-verified API status:
|
|
300
314
|
|
|
301
315
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -256,6 +256,20 @@ nb app down --env app1 --all --yes
|
|
|
256
256
|
nb env
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
+
只查看当前 env 名称:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
nb env current
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
配置 `NB_SESSION_ID` 的 shell session 集成:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
nb session setup
|
|
269
|
+
nb session id
|
|
270
|
+
nb session remove
|
|
271
|
+
```
|
|
272
|
+
|
|
259
273
|
查看已配置的 env 及 Token 验证后的 API 状态:
|
|
260
274
|
|
|
261
275
|
```bash
|
package/bin/run.js
CHANGED
|
@@ -5,6 +5,7 @@ import fs from 'node:fs';
|
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
import { normalizeSessionEnv } from './session-env.js';
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const requireFromCli = createRequire(import.meta.url);
|
|
@@ -16,6 +17,8 @@ if (process.env.NB_CLI_USE_DIST === '1') {
|
|
|
16
17
|
isDev = false;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
normalizeSessionEnv();
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* In the monorepo, plain `node` cannot load `.ts`. Re-exec once with `--import <tsx>`
|
|
21
24
|
* (same effect as a dedicated dev entry with `#!/usr/bin/env -S node --import tsx`).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const SESSION_ENV_SOURCES = [
|
|
2
|
+
'CODEX_THREAD_ID',
|
|
3
|
+
'OPENCODE_RUN_ID',
|
|
4
|
+
'COPILOT_AGENT_SESSION_ID',
|
|
5
|
+
'CLAUDE_CODE_SESSION_ID',
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function resolveNormalizedSessionId(env = process.env) {
|
|
9
|
+
for (const key of SESSION_ENV_SOURCES) {
|
|
10
|
+
const value = String(env[key] ?? '').trim();
|
|
11
|
+
if (value) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeSessionEnv(env = process.env) {
|
|
20
|
+
const sessionId = resolveNormalizedSessionId(env);
|
|
21
|
+
if (!sessionId) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
env.NB_SESSION_ID = sessionId;
|
|
26
|
+
return sessionId;
|
|
27
|
+
}
|
|
@@ -12,8 +12,9 @@ import fsp from 'node:fs/promises';
|
|
|
12
12
|
import os from 'node:os';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
15
|
-
import { removeEnv } from '../../lib/auth-store.js';
|
|
15
|
+
import { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
|
|
16
16
|
import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
|
|
17
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
17
18
|
import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
|
|
18
19
|
import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
|
|
19
20
|
function resolveConfiguredPath(value) {
|
|
@@ -97,16 +98,16 @@ function builtinDbContainerName(runtime) {
|
|
|
97
98
|
function managedDockerNetworkName(runtime) {
|
|
98
99
|
return runtime.dockerNetworkName?.trim() || runtime.workspaceName?.trim() || undefined;
|
|
99
100
|
}
|
|
100
|
-
async function confirmDownAll(envName,
|
|
101
|
-
if (
|
|
101
|
+
async function confirmDownAll(envName, force, options) {
|
|
102
|
+
if (force) {
|
|
102
103
|
return true;
|
|
103
104
|
}
|
|
104
105
|
const usedCurrentEnv = options?.explicitEnv === false;
|
|
105
106
|
if (!isInteractiveTerminal()) {
|
|
106
107
|
if (usedCurrentEnv) {
|
|
107
|
-
throw new Error(`\`nb app down --all\` is using the current env "${envName}". Re-run with --env ${envName} --
|
|
108
|
+
throw new Error(`\`nb app down --all\` is using the current env "${envName}". Re-run with --env ${envName} --force to delete everything for that env in non-interactive mode.`);
|
|
108
109
|
}
|
|
109
|
-
throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --
|
|
110
|
+
throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --force to delete everything for "${envName}" in non-interactive mode.`);
|
|
110
111
|
}
|
|
111
112
|
const answer = await p.confirm({
|
|
112
113
|
message: usedCurrentEnv
|
|
@@ -122,6 +123,17 @@ async function confirmDownAll(envName, yes, options) {
|
|
|
122
123
|
}
|
|
123
124
|
return answer;
|
|
124
125
|
}
|
|
126
|
+
function formatDownCrossEnvForceRequiredMessage(currentEnv, requestedEnv) {
|
|
127
|
+
return [
|
|
128
|
+
`Refusing to run against env "${requestedEnv}" because the current env is "${currentEnv}" and interactive confirmation is unavailable in the current agent session.`,
|
|
129
|
+
'',
|
|
130
|
+
'For safety, the agent will not switch envs automatically and will not add --force on your behalf.',
|
|
131
|
+
'',
|
|
132
|
+
'To continue:',
|
|
133
|
+
`- run \`nb env use ${requestedEnv}\` yourself and then re-run the command, or`,
|
|
134
|
+
`- re-run the same command with \`--env ${requestedEnv} --force\` to confirm this one-off cross-env operation.`,
|
|
135
|
+
].join('\n');
|
|
136
|
+
}
|
|
125
137
|
function formatDownFailure(envName, message) {
|
|
126
138
|
return [
|
|
127
139
|
`Couldn't bring down NocoBase for "${envName}".`,
|
|
@@ -134,7 +146,8 @@ export default class AppDown extends Command {
|
|
|
134
146
|
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.';
|
|
135
147
|
static examples = [
|
|
136
148
|
'<%= config.bin %> <%= command.id %> --env app1',
|
|
137
|
-
'<%= config.bin %> <%= command.id %> --env app1 --all --
|
|
149
|
+
'<%= config.bin %> <%= command.id %> --env app1 --all --force',
|
|
150
|
+
'<%= config.bin %> <%= command.id %> --env app1 --force',
|
|
138
151
|
];
|
|
139
152
|
static flags = {
|
|
140
153
|
env: Flags.string({
|
|
@@ -147,7 +160,12 @@ export default class AppDown extends Command {
|
|
|
147
160
|
}),
|
|
148
161
|
yes: Flags.boolean({
|
|
149
162
|
char: 'y',
|
|
150
|
-
description: 'Confirm
|
|
163
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
164
|
+
default: false,
|
|
165
|
+
}),
|
|
166
|
+
force: Flags.boolean({
|
|
167
|
+
char: 'f',
|
|
168
|
+
description: 'Force a one-off cross-env operation when --env targets a different env in non-interactive mode',
|
|
151
169
|
default: false,
|
|
152
170
|
}),
|
|
153
171
|
verbose: Flags.boolean({
|
|
@@ -158,6 +176,26 @@ export default class AppDown extends Command {
|
|
|
158
176
|
async run() {
|
|
159
177
|
const { flags } = await this.parse(AppDown);
|
|
160
178
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
179
|
+
if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
|
|
180
|
+
if (!isInteractiveTerminal()) {
|
|
181
|
+
const currentEnv = await getCurrentEnvName();
|
|
182
|
+
const normalizedCurrentEnv = String(currentEnv ?? '').trim() || undefined;
|
|
183
|
+
if (normalizedCurrentEnv && normalizedCurrentEnv !== requestedEnv && !flags.force) {
|
|
184
|
+
this.error(formatDownCrossEnvForceRequiredMessage(normalizedCurrentEnv, requestedEnv));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
189
|
+
command: this,
|
|
190
|
+
requestedEnv,
|
|
191
|
+
yes: flags.yes,
|
|
192
|
+
});
|
|
193
|
+
if (!confirmed) {
|
|
194
|
+
this.log('Canceled.');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
161
199
|
const explicitEnv = Boolean(requestedEnv);
|
|
162
200
|
const removeData = Boolean(flags.all);
|
|
163
201
|
const removeEnvConfig = Boolean(flags.all);
|
|
@@ -182,7 +220,7 @@ export default class AppDown extends Command {
|
|
|
182
220
|
if (flags.all) {
|
|
183
221
|
let confirmed = false;
|
|
184
222
|
try {
|
|
185
|
-
confirmed = await confirmDownAll(runtime.envName, flags.
|
|
223
|
+
confirmed = await confirmDownAll(runtime.envName, flags.force, { explicitEnv });
|
|
186
224
|
}
|
|
187
225
|
catch (error) {
|
|
188
226
|
this.error(error instanceof Error ? error.message : String(error));
|
|
@@ -255,7 +293,7 @@ export default class AppDown extends Command {
|
|
|
255
293
|
if (removeEnvConfig) {
|
|
256
294
|
startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
|
|
257
295
|
const result = await removeEnv(runtime.envName);
|
|
258
|
-
succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.
|
|
296
|
+
succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.lastEnv ? ` (last env: ${result.lastEnv})` : ''}.`);
|
|
259
297
|
}
|
|
260
298
|
}
|
|
261
299
|
catch (error) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
10
11
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
11
12
|
import { run } from '../../lib/run-npm.js';
|
|
12
13
|
import { printInfo } from '../../lib/ui.js';
|
|
@@ -31,6 +32,11 @@ export default class AppLogs extends Command {
|
|
|
31
32
|
char: 'e',
|
|
32
33
|
description: 'CLI env name to inspect logs for. Defaults to the current env when omitted',
|
|
33
34
|
}),
|
|
35
|
+
yes: Flags.boolean({
|
|
36
|
+
char: 'y',
|
|
37
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
38
|
+
default: false,
|
|
39
|
+
}),
|
|
34
40
|
tail: Flags.integer({
|
|
35
41
|
description: 'Number of recent log lines to show before following',
|
|
36
42
|
default: 100,
|
|
@@ -46,6 +52,17 @@ export default class AppLogs extends Command {
|
|
|
46
52
|
async run() {
|
|
47
53
|
const { flags } = await this.parse(AppLogs);
|
|
48
54
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
55
|
+
if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
|
|
56
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
57
|
+
command: this,
|
|
58
|
+
requestedEnv,
|
|
59
|
+
yes: flags.yes,
|
|
60
|
+
});
|
|
61
|
+
if (!confirmed) {
|
|
62
|
+
this.log('Canceled.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
49
66
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
50
67
|
if (!runtime) {
|
|
51
68
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
10
11
|
function argvHasToken(argv, tokens) {
|
|
11
12
|
return tokens.some((token) => argv.includes(token));
|
|
12
13
|
}
|
|
@@ -35,6 +36,11 @@ export default class AppRestart extends Command {
|
|
|
35
36
|
char: 'e',
|
|
36
37
|
description: 'CLI env name to restart. Defaults to the current env when omitted',
|
|
37
38
|
}),
|
|
39
|
+
yes: Flags.boolean({
|
|
40
|
+
char: 'y',
|
|
41
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
42
|
+
default: false,
|
|
43
|
+
}),
|
|
38
44
|
quickstart: Flags.boolean({ description: 'Quickstart the application after stopping it', required: false }),
|
|
39
45
|
port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
|
|
40
46
|
daemon: Flags.boolean({
|
|
@@ -53,9 +59,25 @@ export default class AppRestart extends Command {
|
|
|
53
59
|
};
|
|
54
60
|
async run() {
|
|
55
61
|
const { flags } = await this.parse(AppRestart);
|
|
62
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
63
|
+
const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
|
|
64
|
+
if (explicitEnvSelection) {
|
|
65
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
66
|
+
command: this,
|
|
67
|
+
requestedEnv,
|
|
68
|
+
yes: flags.yes,
|
|
69
|
+
});
|
|
70
|
+
if (!confirmed) {
|
|
71
|
+
this.log('Canceled.');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
56
75
|
const stopArgv = [];
|
|
57
76
|
const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
|
|
58
|
-
pushFlag(stopArgv, '--env',
|
|
77
|
+
pushFlag(stopArgv, '--env', requestedEnv);
|
|
78
|
+
if (flags.yes || explicitEnvSelection) {
|
|
79
|
+
stopArgv.push('--yes');
|
|
80
|
+
}
|
|
59
81
|
if (flags.verbose) {
|
|
60
82
|
stopArgv.push('--verbose');
|
|
61
83
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
10
11
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, } from '../../lib/app-runtime.js';
|
|
11
12
|
import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBaseUrl, waitForAppReady, } from '../../lib/app-health.js';
|
|
12
13
|
import { ensureBuiltinDbReady, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
|
|
@@ -68,6 +69,11 @@ export default class AppStart extends Command {
|
|
|
68
69
|
char: 'e',
|
|
69
70
|
description: 'CLI env name to start. Defaults to the current env when omitted',
|
|
70
71
|
}),
|
|
72
|
+
yes: Flags.boolean({
|
|
73
|
+
char: 'y',
|
|
74
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
75
|
+
default: false,
|
|
76
|
+
}),
|
|
71
77
|
quickstart: Flags.boolean({ description: 'Quickstart the application', required: false }),
|
|
72
78
|
port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
|
|
73
79
|
daemon: Flags.boolean({
|
|
@@ -87,6 +93,17 @@ export default class AppStart extends Command {
|
|
|
87
93
|
async run() {
|
|
88
94
|
const { flags } = await this.parse(AppStart);
|
|
89
95
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
96
|
+
if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
|
|
97
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
98
|
+
command: this,
|
|
99
|
+
requestedEnv,
|
|
100
|
+
yes: flags.yes,
|
|
101
|
+
});
|
|
102
|
+
if (!confirmed) {
|
|
103
|
+
this.log('Canceled.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
90
107
|
const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
|
|
91
108
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
92
109
|
const commandStdio = flags.verbose ? 'inherit' : 'ignore';
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
10
11
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, stopDockerContainer, } from '../../lib/app-runtime.js';
|
|
11
12
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
12
13
|
function formatStopFailure(envName, message) {
|
|
@@ -38,6 +39,11 @@ export default class AppStop extends Command {
|
|
|
38
39
|
char: 'e',
|
|
39
40
|
description: 'CLI env name to stop. Defaults to the current env when omitted',
|
|
40
41
|
}),
|
|
42
|
+
yes: Flags.boolean({
|
|
43
|
+
char: 'y',
|
|
44
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
45
|
+
default: false,
|
|
46
|
+
}),
|
|
41
47
|
verbose: Flags.boolean({
|
|
42
48
|
description: 'Show raw shutdown output from the underlying local or Docker command',
|
|
43
49
|
default: false,
|
|
@@ -46,6 +52,17 @@ export default class AppStop extends Command {
|
|
|
46
52
|
async run() {
|
|
47
53
|
const { flags } = await this.parse(AppStop);
|
|
48
54
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
55
|
+
if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
|
|
56
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
57
|
+
command: this,
|
|
58
|
+
requestedEnv,
|
|
59
|
+
yes: flags.yes,
|
|
60
|
+
});
|
|
61
|
+
if (!confirmed) {
|
|
62
|
+
this.log('Canceled.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
49
66
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
50
67
|
const commandStdio = flags.verbose ? 'inherit' : 'ignore';
|
|
51
68
|
if (!runtime) {
|
|
@@ -11,9 +11,10 @@ import { upsertEnv } from '../../lib/auth-store.js';
|
|
|
11
11
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, stopDockerContainer, } from '../../lib/app-runtime.js';
|
|
12
12
|
import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
|
|
13
13
|
import { deriveBuiltinDbConnection } from '../../lib/builtin-db.js';
|
|
14
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
15
|
+
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
14
16
|
import { commandSucceeds, run } from '../../lib/run-npm.js';
|
|
15
17
|
import { announceTargetEnv, failTask, printInfo, startTask, stopTask, succeedTask, updateTask } from '../../lib/ui.js';
|
|
16
|
-
const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
|
|
17
18
|
const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
|
|
18
19
|
const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
|
|
19
20
|
const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
|
|
@@ -211,6 +212,11 @@ export default class AppUpgrade extends Command {
|
|
|
211
212
|
char: 'e',
|
|
212
213
|
description: 'CLI env name to upgrade. Defaults to the current env when omitted',
|
|
213
214
|
}),
|
|
215
|
+
yes: Flags.boolean({
|
|
216
|
+
char: 'y',
|
|
217
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
218
|
+
default: false,
|
|
219
|
+
}),
|
|
214
220
|
'skip-code-update': Flags.boolean({
|
|
215
221
|
char: 's',
|
|
216
222
|
description: 'Restart with the saved local code or Docker image without downloading updates first',
|
|
@@ -355,7 +361,10 @@ export default class AppUpgrade extends Command {
|
|
|
355
361
|
if (missing.length > 0) {
|
|
356
362
|
throw new Error(`The saved Docker settings for "${runtime.envName}" are incomplete. Missing: ${missing.join(', ')}. Re-run \`nb init\` or \`nb env add\` to refresh this env config.`);
|
|
357
363
|
}
|
|
358
|
-
const imageRef =
|
|
364
|
+
const imageRef = resolveDockerImageRef(dockerRegistry, downloadVersion, {
|
|
365
|
+
defaultRegistry: DEFAULT_DOCKER_REGISTRY,
|
|
366
|
+
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
367
|
+
});
|
|
359
368
|
const args = [
|
|
360
369
|
'run',
|
|
361
370
|
'-d',
|
|
@@ -544,6 +553,17 @@ export default class AppUpgrade extends Command {
|
|
|
544
553
|
const { flags } = await this.parse(AppUpgrade);
|
|
545
554
|
const parsed = flags;
|
|
546
555
|
const requestedEnv = parsed.env?.trim() || undefined;
|
|
556
|
+
if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
|
|
557
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
558
|
+
command: this,
|
|
559
|
+
requestedEnv,
|
|
560
|
+
yes: parsed.yes,
|
|
561
|
+
});
|
|
562
|
+
if (!confirmed) {
|
|
563
|
+
this.log('Canceled.');
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
547
567
|
const commandStdio = parsed.verbose ? 'inherit' : 'ignore';
|
|
548
568
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
549
569
|
if (!runtime) {
|
|
@@ -10,10 +10,9 @@ import { Command, Flags } from '@oclif/core';
|
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
|
|
11
11
|
import { resolveBuiltinDbConnection } from '../../lib/builtin-db.js';
|
|
12
12
|
import { checkExternalDbConnection, formatDbCheckAddress, readExternalDbConnectionConfig, } from "../../lib/db-connection-check.js";
|
|
13
|
+
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
13
14
|
import { commandOutput } from '../../lib/run-npm.js';
|
|
14
15
|
import { validateTcpPort } from "../../lib/prompt-validators.js";
|
|
15
|
-
const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
|
|
16
|
-
const DEFAULT_DOCKER_VERSION = 'alpha';
|
|
17
16
|
function trimValue(value) {
|
|
18
17
|
const text = String(value ?? '').trim();
|
|
19
18
|
return text || undefined;
|
|
@@ -133,7 +132,10 @@ async function runExplicitDbCheck(command, dbConfig) {
|
|
|
133
132
|
async function runDockerDbCheck(command, runtime, dbConfig) {
|
|
134
133
|
const connectionConfig = buildConnectionConfigOrThrow(command, dbConfig);
|
|
135
134
|
const config = runtime.env.config ?? {};
|
|
136
|
-
const imageRef =
|
|
135
|
+
const imageRef = resolveDockerImageRef(config.dockerRegistry, config.downloadVersion, {
|
|
136
|
+
defaultRegistry: DEFAULT_DOCKER_REGISTRY,
|
|
137
|
+
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
138
|
+
});
|
|
137
139
|
const args = [
|
|
138
140
|
'run',
|
|
139
141
|
'--rm',
|
|
@@ -185,7 +187,7 @@ export default class DbCheck extends Command {
|
|
|
185
187
|
static flags = {
|
|
186
188
|
env: Flags.string({
|
|
187
189
|
char: 'e',
|
|
188
|
-
description: 'CLI env name to read saved database settings from. Defaults to the current env when omitted
|
|
190
|
+
description: 'CLI env name to read saved database settings from. Defaults to the current env when omitted',
|
|
189
191
|
}),
|
|
190
192
|
'db-dialect': Flags.string({
|
|
191
193
|
description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
|
package/dist/commands/db/ps.js
CHANGED
|
@@ -20,7 +20,7 @@ export default class DbPs extends Command {
|
|
|
20
20
|
static flags = {
|
|
21
21
|
env: Flags.string({
|
|
22
22
|
char: 'e',
|
|
23
|
-
description: 'CLI env name to inspect. Omit to show all configured envs',
|
|
23
|
+
description: 'CLI env name to inspect built-in database status for. Omit to show all configured envs',
|
|
24
24
|
}),
|
|
25
25
|
};
|
|
26
26
|
async run() {
|
package/dist/commands/env/add.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
-
import { upsertEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { setCurrentEnv, upsertEnv } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { buildStoredEnvConfig, } from '../../lib/env-config.js';
|
|
13
13
|
import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
|
|
@@ -55,7 +55,7 @@ export default class EnvAdd extends Command {
|
|
|
55
55
|
];
|
|
56
56
|
static args = {
|
|
57
57
|
name: Args.string({
|
|
58
|
-
description: '
|
|
58
|
+
description: 'Environment name to save (optional first argument; in a TTY, prompted when omitted; required when not using a TTY)',
|
|
59
59
|
required: false,
|
|
60
60
|
}),
|
|
61
61
|
};
|
|
@@ -303,6 +303,7 @@ export default class EnvAdd extends Command {
|
|
|
303
303
|
const envConfig = this.buildEnvConfig(results, parsedFlags);
|
|
304
304
|
printVerbose(`Saving env "${envName}" globally.`);
|
|
305
305
|
await upsertEnv(envName, envConfig, { scope: resolveDefaultConfigScope() });
|
|
306
|
+
await setCurrentEnv(envName, { scope: resolveDefaultConfigScope() });
|
|
306
307
|
if (results.authType === 'oauth') {
|
|
307
308
|
await this.config.runCommand('env:auth', [envName]);
|
|
308
309
|
}
|
|
@@ -19,7 +19,7 @@ export default class EnvAuth extends Command {
|
|
|
19
19
|
];
|
|
20
20
|
static args = {
|
|
21
21
|
name: Args.string({
|
|
22
|
-
description: '
|
|
22
|
+
description: 'Configured environment name to sign in to. Defaults to the current env when omitted',
|
|
23
23
|
required: false,
|
|
24
24
|
}),
|
|
25
25
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
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 } from '@oclif/core';
|
|
10
|
+
import { getCurrentEnvName } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
export default class EnvCurrent extends Command {
|
|
13
|
+
static summary = 'Show the current environment name';
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> <%= command.id %>',
|
|
16
|
+
];
|
|
17
|
+
async run() {
|
|
18
|
+
await this.parse(EnvCurrent);
|
|
19
|
+
this.log(await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -50,18 +50,19 @@ export default class EnvInfo extends Command {
|
|
|
50
50
|
'<%= config.bin %> <%= command.id %> app1',
|
|
51
51
|
'<%= config.bin %> <%= command.id %> app1 --json',
|
|
52
52
|
'<%= config.bin %> <%= command.id %> app1 --show-secrets',
|
|
53
|
-
'<%= config.bin %> <%= command.id %> --env app1',
|
|
54
53
|
];
|
|
55
54
|
static args = {
|
|
56
55
|
name: Args.string({
|
|
57
|
-
description: '
|
|
56
|
+
description: 'Configured environment name to inspect. Defaults to the current env when omitted',
|
|
58
57
|
required: false,
|
|
59
58
|
}),
|
|
60
59
|
};
|
|
61
60
|
static flags = {
|
|
62
61
|
env: Flags.string({
|
|
63
62
|
char: 'e',
|
|
64
|
-
|
|
63
|
+
hidden: true,
|
|
64
|
+
deprecated: true,
|
|
65
|
+
description: 'Environment name (same as the optional positional argument; for compatibility with -e/--env on other commands)',
|
|
65
66
|
}),
|
|
66
67
|
json: Flags.boolean({
|
|
67
68
|
description: 'Output the result as JSON',
|
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Command } from '@oclif/core';
|
|
10
|
-
import {
|
|
11
|
-
import { listEnvs } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, listEnvs } from '../../lib/auth-store.js';
|
|
12
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
13
12
|
import { renderTable } from '../../lib/ui.js';
|
|
14
|
-
import {
|
|
13
|
+
import { resolveApiBaseUrl } from './shared.js';
|
|
15
14
|
export default class EnvList extends Command {
|
|
16
|
-
static summary = 'List configured environments
|
|
15
|
+
static summary = 'List configured environments';
|
|
17
16
|
static examples = [
|
|
18
17
|
'<%= config.bin %> <%= command.id %>',
|
|
19
18
|
];
|
|
20
19
|
async run() {
|
|
21
20
|
await this.parse(EnvList);
|
|
22
21
|
const scope = resolveDefaultConfigScope();
|
|
23
|
-
const {
|
|
22
|
+
const { envs } = await listEnvs({ scope });
|
|
23
|
+
const currentEnv = await getCurrentEnvName({ scope });
|
|
24
24
|
const names = Object.keys(envs).sort();
|
|
25
25
|
if (!names.length) {
|
|
26
26
|
this.log('No envs configured.');
|
|
@@ -30,21 +30,15 @@ export default class EnvList extends Command {
|
|
|
30
30
|
const rows = [];
|
|
31
31
|
for (const name of names) {
|
|
32
32
|
const env = envs[name];
|
|
33
|
-
const runtime = await resolveManagedAppRuntime(name);
|
|
34
|
-
const statusConfig = {
|
|
35
|
-
...env,
|
|
36
|
-
...(runtime?.env.config ?? {}),
|
|
37
|
-
};
|
|
38
33
|
rows.push([
|
|
39
34
|
name === currentEnv ? '*' : '',
|
|
40
35
|
name,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
runtime ? appUrl(runtime) : resolveApiBaseUrl(env),
|
|
36
|
+
env.kind ?? '-',
|
|
37
|
+
resolveApiBaseUrl(env),
|
|
44
38
|
env.auth?.type ?? '',
|
|
45
39
|
env.runtime?.version ?? '',
|
|
46
40
|
]);
|
|
47
41
|
}
|
|
48
|
-
this.log(renderTable(['Current', 'Name', 'Kind', '
|
|
42
|
+
this.log(renderTable(['Current', 'Name', 'Kind', 'API Base URL', 'Auth', 'Runtime'], rows));
|
|
49
43
|
}
|
|
50
44
|
}
|
|
@@ -29,7 +29,7 @@ export default class EnvRemove extends Command {
|
|
|
29
29
|
};
|
|
30
30
|
static args = {
|
|
31
31
|
name: Args.string({
|
|
32
|
-
description: 'Configured environment name',
|
|
32
|
+
description: 'Configured environment name to remove',
|
|
33
33
|
required: true,
|
|
34
34
|
}),
|
|
35
35
|
};
|
|
@@ -51,7 +51,7 @@ export default class EnvRemove extends Command {
|
|
|
51
51
|
const result = await removeEnv(args.name, { scope: resolveDefaultConfigScope() });
|
|
52
52
|
this.log(`Removed env "${result.removed}".`);
|
|
53
53
|
if (result.hasEnvs) {
|
|
54
|
-
this.log(`Current env: ${
|
|
54
|
+
this.log(`Current env: ${await getCurrentEnvName({ scope: resolveDefaultConfigScope() })}`);
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
this.log('No envs configured.');
|