@nocobase/cli 2.1.0-beta.32 → 2.1.0-beta.34
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/dist/commands/app/down.js +10 -13
- package/dist/commands/app/logs.js +0 -1
- package/dist/commands/app/restart.js +63 -2
- package/dist/commands/app/start.js +41 -17
- package/dist/commands/app/stop.js +0 -1
- package/dist/commands/app/upgrade.js +9 -4
- package/dist/commands/env/add.js +3 -4
- package/dist/commands/env/auth.js +3 -2
- package/dist/commands/env/remove.js +38 -13
- package/dist/commands/env/update.js +9 -2
- package/dist/commands/examples/prompts-stages.js +4 -4
- package/dist/commands/examples/prompts-test.js +4 -4
- package/dist/commands/init.js +38 -31
- package/dist/commands/install.js +100 -63
- package/dist/commands/license/activate.js +66 -64
- package/dist/commands/license/id.js +0 -1
- package/dist/commands/license/plugins/clean.js +0 -1
- package/dist/commands/license/plugins/list.js +0 -1
- package/dist/commands/license/plugins/sync.js +0 -1
- package/dist/commands/license/shared.js +3 -3
- package/dist/commands/license/status.js +0 -1
- package/dist/commands/plugin/disable.js +0 -1
- package/dist/commands/plugin/enable.js +0 -1
- package/dist/commands/plugin/list.js +0 -1
- package/dist/commands/self/update.js +12 -3
- package/dist/commands/skills/install.js +12 -3
- package/dist/commands/skills/remove.js +12 -3
- package/dist/commands/skills/update.js +12 -3
- package/dist/commands/source/dev.js +0 -1
- package/dist/commands/source/download.js +29 -17
- package/dist/lib/app-managed-resources.js +8 -2
- package/dist/lib/bootstrap.js +11 -2
- package/dist/lib/db-connection-check.js +3 -23
- package/dist/lib/docker-env-file.js +52 -0
- package/dist/lib/env-auth.js +4 -3
- package/dist/lib/env-config.js +1 -0
- package/dist/lib/env-guard.js +8 -7
- package/dist/lib/generated-command.js +0 -1
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +244 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/dist/lib/prompt-catalog.js +2 -573
- package/dist/lib/prompt-validators.js +56 -1
- package/dist/lib/resource-command.js +0 -1
- package/dist/lib/skills-manager.js +75 -11
- package/dist/lib/startup-update.js +12 -8
- package/dist/lib/ui.js +28 -51
- package/dist/locale/en-US.json +8 -3
- package/dist/locale/zh-CN.json +8 -3
- package/dist/post-processors/data-modeling.js +25 -7
- package/dist/post-processors/data-source-manager.js +24 -0
- package/nocobase-ctl.config.json +20 -1
- package/package.json +7 -5
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import * as p from '@clack/prompts';
|
|
10
9
|
import { Command, Flags } from '@oclif/core';
|
|
11
10
|
import fsp from 'node:fs/promises';
|
|
12
11
|
import os from 'node:os';
|
|
@@ -15,6 +14,7 @@ import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveM
|
|
|
15
14
|
import { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
|
|
16
15
|
import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
|
|
17
16
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
17
|
+
import { confirm } from "../../lib/inquirer.js";
|
|
18
18
|
import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
|
|
19
19
|
import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
|
|
20
20
|
function resolveConfiguredPath(value) {
|
|
@@ -109,19 +109,17 @@ async function confirmDownAll(envName, force, options) {
|
|
|
109
109
|
}
|
|
110
110
|
throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --force to delete everything for "${envName}" in non-interactive mode.`);
|
|
111
111
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
p.cancel('Down cancelled.');
|
|
112
|
+
try {
|
|
113
|
+
return await confirm({
|
|
114
|
+
message: usedCurrentEnv
|
|
115
|
+
? `Delete everything for current env "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`
|
|
116
|
+
: `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
|
|
117
|
+
default: false,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
122
121
|
return false;
|
|
123
122
|
}
|
|
124
|
-
return answer;
|
|
125
123
|
}
|
|
126
124
|
function formatDownCrossEnvForceRequiredMessage(currentEnv, requestedEnv) {
|
|
127
125
|
return [
|
|
@@ -191,7 +189,6 @@ export default class AppDown extends Command {
|
|
|
191
189
|
yes: flags.yes,
|
|
192
190
|
});
|
|
193
191
|
if (!confirmed) {
|
|
194
|
-
this.log('Canceled.');
|
|
195
192
|
return;
|
|
196
193
|
}
|
|
197
194
|
}
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
11
|
+
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, stopDockerContainer, } from '../../lib/app-runtime.js';
|
|
12
|
+
import { formatAppUrl, resolveManagedAppApiBaseUrl, waitForAppReady } from '../../lib/app-health.js';
|
|
13
|
+
import { recreateSavedDockerApp } from '../../lib/app-managed-resources.js';
|
|
14
|
+
import { run } from '../../lib/run-npm.js';
|
|
15
|
+
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
11
16
|
function argvHasToken(argv, tokens) {
|
|
12
17
|
return tokens.some((token) => argv.includes(token));
|
|
13
18
|
}
|
|
@@ -16,9 +21,17 @@ function pushFlag(argv, flag, value) {
|
|
|
16
21
|
argv.push(flag, String(value));
|
|
17
22
|
}
|
|
18
23
|
}
|
|
24
|
+
function formatDockerRestartFailure(envName, message) {
|
|
25
|
+
return [
|
|
26
|
+
`Couldn't restart NocoBase for "${envName}".`,
|
|
27
|
+
'The CLI was not able to recreate the saved Docker app container successfully.',
|
|
28
|
+
'Check the saved Docker image, envFile, container settings, and database connection, then try again.',
|
|
29
|
+
`Details: ${message}`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
}
|
|
19
32
|
export default class AppRestart extends Command {
|
|
20
33
|
static hidden = false;
|
|
21
|
-
static description = 'Restart NocoBase for the selected env
|
|
34
|
+
static description = 'Restart NocoBase for the selected env. Local npm/git installs stop and start the app again, and Docker installs recreate the saved app container so saved env changes can take effect.';
|
|
22
35
|
static examples = [
|
|
23
36
|
'<%= config.bin %> <%= command.id %>',
|
|
24
37
|
'<%= config.bin %> <%= command.id %> --env local',
|
|
@@ -68,10 +81,58 @@ export default class AppRestart extends Command {
|
|
|
68
81
|
yes: flags.yes,
|
|
69
82
|
});
|
|
70
83
|
if (!confirmed) {
|
|
71
|
-
this.log('Canceled.');
|
|
72
84
|
return;
|
|
73
85
|
}
|
|
74
86
|
}
|
|
87
|
+
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
88
|
+
const commandStdio = flags.verbose ? 'inherit' : 'ignore';
|
|
89
|
+
if (!runtime) {
|
|
90
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
91
|
+
}
|
|
92
|
+
if (runtime.kind === 'docker') {
|
|
93
|
+
announceTargetEnv(runtime.envName);
|
|
94
|
+
startTask(`Stopping NocoBase for "${runtime.envName}" before restart...`);
|
|
95
|
+
try {
|
|
96
|
+
const state = await stopDockerContainer(runtime.containerName, {
|
|
97
|
+
stdio: commandStdio,
|
|
98
|
+
});
|
|
99
|
+
succeedTask(state === 'already-stopped'
|
|
100
|
+
? `NocoBase was already stopped for "${runtime.envName}".`
|
|
101
|
+
: `Stopped NocoBase for "${runtime.envName}".`);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
failTask(`Failed to stop NocoBase for "${runtime.envName}".`);
|
|
106
|
+
this.error(formatDockerRestartFailure(runtime.envName, message));
|
|
107
|
+
}
|
|
108
|
+
startTask(`Recreating the Docker app container for "${runtime.envName}"...`);
|
|
109
|
+
try {
|
|
110
|
+
await run('docker', ['rm', '-f', runtime.containerName], {
|
|
111
|
+
errorName: 'docker rm',
|
|
112
|
+
stdio: commandStdio,
|
|
113
|
+
}).catch(() => undefined);
|
|
114
|
+
await recreateSavedDockerApp(runtime, {
|
|
115
|
+
verbose: flags.verbose,
|
|
116
|
+
});
|
|
117
|
+
succeedTask(`Docker app container is ready for "${runtime.envName}".`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
failTask(`Failed to recreate NocoBase for "${runtime.envName}".`);
|
|
122
|
+
this.error(formatDockerRestartFailure(runtime.envName, message));
|
|
123
|
+
}
|
|
124
|
+
const appUrl = formatAppUrl(runtime.env.appPort === undefined || runtime.env.appPort === null
|
|
125
|
+
? undefined
|
|
126
|
+
: String(runtime.env.appPort));
|
|
127
|
+
await waitForAppReady({
|
|
128
|
+
envName: runtime.envName,
|
|
129
|
+
apiBaseUrl: resolveManagedAppApiBaseUrl(runtime),
|
|
130
|
+
containerName: runtime.containerName,
|
|
131
|
+
logHint: `You can inspect startup logs with \`nb app logs --env ${runtime.envName}\`.`,
|
|
132
|
+
});
|
|
133
|
+
succeedTask(`NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
75
136
|
const stopArgv = [];
|
|
76
137
|
const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
|
|
77
138
|
pushFlag(stopArgv, '--env', requestedEnv);
|
|
@@ -11,6 +11,7 @@ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-
|
|
|
11
11
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, } from '../../lib/app-runtime.js';
|
|
12
12
|
import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBaseUrl, waitForAppReady, } from '../../lib/app-health.js';
|
|
13
13
|
import { ensureBuiltinDbReady, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
|
|
14
|
+
import { run } from '../../lib/run-npm.js';
|
|
14
15
|
import { announceTargetEnv, failTask, printInfo, startTask, succeedTask } from '../../lib/ui.js';
|
|
15
16
|
function argvHasToken(argv, tokens) {
|
|
16
17
|
return tokens.some((token) => argv.includes(token));
|
|
@@ -89,6 +90,10 @@ export default class AppStart extends Command {
|
|
|
89
90
|
description: 'Show raw startup output from the underlying local or Docker command',
|
|
90
91
|
default: false,
|
|
91
92
|
}),
|
|
93
|
+
recreate: Flags.boolean({
|
|
94
|
+
description: 'Recreate the saved Docker app container before starting it',
|
|
95
|
+
default: false,
|
|
96
|
+
}),
|
|
92
97
|
};
|
|
93
98
|
async run() {
|
|
94
99
|
const { flags } = await this.parse(AppStart);
|
|
@@ -100,7 +105,6 @@ export default class AppStart extends Command {
|
|
|
100
105
|
yes: flags.yes,
|
|
101
106
|
});
|
|
102
107
|
if (!confirmed) {
|
|
103
|
-
this.log('Canceled.');
|
|
104
108
|
return;
|
|
105
109
|
}
|
|
106
110
|
}
|
|
@@ -150,29 +154,49 @@ export default class AppStart extends Command {
|
|
|
150
154
|
? undefined
|
|
151
155
|
: String(runtime.env.appPort));
|
|
152
156
|
const apiBaseUrl = resolveManagedAppApiBaseUrl(runtime);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
165
|
-
if (/does not exist/i.test(message)) {
|
|
166
|
-
printInfo(`The saved Docker app container for "${runtime.envName}" is missing. Recreating it from the saved Docker env settings...`);
|
|
157
|
+
if (flags.recreate) {
|
|
158
|
+
startTask(`Recreating the Docker app container for "${runtime.envName}"...`);
|
|
159
|
+
try {
|
|
160
|
+
await run('docker', ['rm', '-f', runtime.containerName], {
|
|
161
|
+
errorName: 'docker rm',
|
|
162
|
+
stdio: commandStdio,
|
|
163
|
+
}).catch(() => undefined);
|
|
167
164
|
await recreateSavedDockerApp(runtime, {
|
|
168
165
|
verbose: flags.verbose,
|
|
169
166
|
});
|
|
167
|
+
succeedTask(`Docker app container is ready for "${runtime.envName}".`);
|
|
170
168
|
}
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
catch (error) {
|
|
170
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
171
|
+
failTask(`Failed to recreate NocoBase for "${runtime.envName}".`);
|
|
173
172
|
this.error(formatDockerStartFailure(runtime.envName, message));
|
|
174
173
|
}
|
|
175
174
|
}
|
|
175
|
+
else {
|
|
176
|
+
startTask(`Starting NocoBase for "${runtime.envName}"...`);
|
|
177
|
+
try {
|
|
178
|
+
const state = await startDockerContainer(runtime.containerName, {
|
|
179
|
+
stdio: commandStdio,
|
|
180
|
+
});
|
|
181
|
+
if (state === 'already-running' && await isAppReady(apiBaseUrl)) {
|
|
182
|
+
succeedTask(`NocoBase is already running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
188
|
+
if (/does not exist/i.test(message)) {
|
|
189
|
+
printInfo(`The saved Docker app container for "${runtime.envName}" is missing. Recreating it from the saved Docker env settings...`);
|
|
190
|
+
await recreateSavedDockerApp(runtime, {
|
|
191
|
+
verbose: flags.verbose,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
failTask(`Failed to start NocoBase for "${runtime.envName}".`);
|
|
196
|
+
this.error(formatDockerStartFailure(runtime.envName, message));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
176
200
|
await waitForAppReady({
|
|
177
201
|
envName: runtime.envName,
|
|
178
202
|
apiBaseUrl,
|
|
@@ -11,6 +11,7 @@ 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 { resolveDockerEnvFileArg } from "../../lib/docker-env-file.js";
|
|
14
15
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
15
16
|
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
16
17
|
import { commandSucceeds, run } from '../../lib/run-npm.js';
|
|
@@ -311,12 +312,13 @@ export default class AppUpgrade extends Command {
|
|
|
311
312
|
argv.push('--daemon');
|
|
312
313
|
return argv;
|
|
313
314
|
}
|
|
314
|
-
static buildDockerUpgradePlan(runtime, downloadVersion) {
|
|
315
|
+
static async buildDockerUpgradePlan(runtime, downloadVersion) {
|
|
315
316
|
const dockerRegistry = readEnvValue(runtime.env, 'dockerRegistry') || DEFAULT_DOCKER_REGISTRY;
|
|
316
317
|
const appPort = runtime.env.appPort === undefined || runtime.env.appPort === null
|
|
317
318
|
? ''
|
|
318
319
|
: trimValue(runtime.env.appPort);
|
|
319
320
|
const storagePath = readEnvValue(runtime.env, 'storagePath');
|
|
321
|
+
const envFile = await resolveDockerEnvFileArg(runtime.envName, runtime.env.config);
|
|
320
322
|
const appKey = readEnvValue(runtime.env, 'appKey');
|
|
321
323
|
const timeZone = readEnvValue(runtime.env, 'timezone');
|
|
322
324
|
const builtinDbConnection = runtime.env.config.builtinDb ? deriveBuiltinDbConnection(runtime) : undefined;
|
|
@@ -378,6 +380,9 @@ export default class AppUpgrade extends Command {
|
|
|
378
380
|
if (appPort) {
|
|
379
381
|
args.push('-p', `${appPort}:80`);
|
|
380
382
|
}
|
|
383
|
+
if (envFile) {
|
|
384
|
+
args.push('--env-file', envFile);
|
|
385
|
+
}
|
|
381
386
|
args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:${DOCKER_APP_STORAGE_DESTINATION}`, imageRef);
|
|
382
387
|
return {
|
|
383
388
|
containerName: runtime.containerName,
|
|
@@ -387,6 +392,7 @@ export default class AppUpgrade extends Command {
|
|
|
387
392
|
imageRef,
|
|
388
393
|
appPort: appPort || undefined,
|
|
389
394
|
storagePath,
|
|
395
|
+
envFile,
|
|
390
396
|
appKey,
|
|
391
397
|
timeZone,
|
|
392
398
|
dbDialect,
|
|
@@ -452,7 +458,7 @@ export default class AppUpgrade extends Command {
|
|
|
452
458
|
const apiBaseUrl = resolveApiBaseUrl(runtime);
|
|
453
459
|
const containerExists = await dockerContainerExists(runtime.containerName);
|
|
454
460
|
if (!flags['skip-code-update']) {
|
|
455
|
-
const plan = AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
461
|
+
const plan = await AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
456
462
|
startTask(`Refreshing the Docker image for "${runtime.envName}"...`);
|
|
457
463
|
try {
|
|
458
464
|
await runCommand('source:download', AppUpgrade.buildDockerDownloadArgv(runtime, plan));
|
|
@@ -500,7 +506,7 @@ export default class AppUpgrade extends Command {
|
|
|
500
506
|
}
|
|
501
507
|
}
|
|
502
508
|
else {
|
|
503
|
-
const plan = AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
509
|
+
const plan = await AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
504
510
|
const displayUrl = formatDisplayUrl(apiBaseUrl, plan.appPort);
|
|
505
511
|
startTask(`Recreating the Docker app container for "${runtime.envName}"...`);
|
|
506
512
|
try {
|
|
@@ -560,7 +566,6 @@ export default class AppUpgrade extends Command {
|
|
|
560
566
|
yes: parsed.yes,
|
|
561
567
|
});
|
|
562
568
|
if (!confirmed) {
|
|
563
|
-
this.log('Canceled.');
|
|
564
569
|
return;
|
|
565
570
|
}
|
|
566
571
|
}
|
package/dist/commands/env/add.js
CHANGED
|
@@ -13,8 +13,7 @@ import { buildStoredEnvConfig, } from '../../lib/env-config.js';
|
|
|
13
13
|
import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
|
|
14
14
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, } from '../../lib/cli-locale.js';
|
|
15
15
|
import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
|
|
16
|
-
import { printVerbose, setVerboseMode } from '../../lib/ui.js';
|
|
17
|
-
import * as p from '@clack/prompts';
|
|
16
|
+
import { printStage, printSuccess, printVerbose, setVerboseMode } from '../../lib/ui.js';
|
|
18
17
|
const ENV_RUNTIME_FLAG_MAP = {
|
|
19
18
|
source: 'source',
|
|
20
19
|
'download-version': 'downloadVersion',
|
|
@@ -292,7 +291,7 @@ export default class EnvAdd extends Command {
|
|
|
292
291
|
applyCliLocale(parsedFlags.locale);
|
|
293
292
|
setVerboseMode(parsedFlags.verbose);
|
|
294
293
|
if (!parsedFlags['no-intro']) {
|
|
295
|
-
|
|
294
|
+
printStage('Connect to NocoBase');
|
|
296
295
|
}
|
|
297
296
|
const results = await runPromptCatalog(EnvAdd.prompts, {
|
|
298
297
|
values: this.buildPromptValues(args.name, parsedFlags),
|
|
@@ -308,6 +307,6 @@ export default class EnvAdd extends Command {
|
|
|
308
307
|
await this.config.runCommand('env:auth', [envName]);
|
|
309
308
|
}
|
|
310
309
|
await this.config.runCommand('env:update', [envName]);
|
|
311
|
-
|
|
310
|
+
printSuccess(`✔ Env "${envName}" is ready.`);
|
|
312
311
|
}
|
|
313
312
|
}
|
|
@@ -10,7 +10,7 @@ import { Args, Command, Flags } from '@oclif/core';
|
|
|
10
10
|
import { getCurrentEnvName } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { authenticateEnvWithOauth } from '../../lib/env-auth.js';
|
|
13
|
-
import { failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
13
|
+
import { failTask, printStage, startTask, succeedTask } from '../../lib/ui.js';
|
|
14
14
|
export default class EnvAuth extends Command {
|
|
15
15
|
static summary = 'Sign in to a saved NocoBase environment with OAuth';
|
|
16
16
|
static examples = [
|
|
@@ -39,13 +39,14 @@ export default class EnvAuth extends Command {
|
|
|
39
39
|
this.error(`Environment name was provided both as the argument ("${nameArg}") and as --env ("${nameFlag}"). Please use only one.`);
|
|
40
40
|
}
|
|
41
41
|
const envName = nameArg || nameFlag || (await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
|
|
42
|
+
printStage('Signing in');
|
|
42
43
|
startTask(`Starting browser sign-in for "${envName}"...`);
|
|
43
44
|
try {
|
|
44
45
|
await authenticateEnvWithOauth({
|
|
45
46
|
envName,
|
|
46
47
|
scope: resolveDefaultConfigScope(),
|
|
47
48
|
});
|
|
48
|
-
succeedTask(
|
|
49
|
+
succeedTask(`✔ Signed in to "${envName}".`);
|
|
49
50
|
}
|
|
50
51
|
catch (error) {
|
|
51
52
|
failTask(`Sign-in failed for "${envName}".`);
|
|
@@ -7,19 +7,26 @@
|
|
|
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 { getCurrentEnvName, removeEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, loadAuthConfig, removeEnv } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
-
import {
|
|
12
|
+
import { confirm } from "../../lib/inquirer.js";
|
|
13
|
+
import { isInteractiveTerminal, printVerbose, setVerboseMode } from '../../lib/ui.js';
|
|
13
14
|
export default class EnvRemove extends Command {
|
|
14
15
|
static summary = 'Remove a configured environment';
|
|
16
|
+
static description = 'Remove the saved CLI env config for an environment. This command does not clean local app files, containers, or storage data.';
|
|
15
17
|
static examples = [
|
|
16
18
|
'<%= config.bin %> <%= command.id %> staging',
|
|
17
|
-
'<%= config.bin %> <%= command.id %> staging
|
|
19
|
+
'<%= config.bin %> <%= command.id %> staging --yes',
|
|
18
20
|
];
|
|
19
21
|
static flags = {
|
|
22
|
+
yes: Flags.boolean({
|
|
23
|
+
char: 'y',
|
|
24
|
+
description: 'Skip confirmation and remove the saved CLI env config',
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
20
27
|
force: Flags.boolean({
|
|
21
28
|
char: 'f',
|
|
22
|
-
|
|
29
|
+
hidden: true,
|
|
23
30
|
default: false,
|
|
24
31
|
}),
|
|
25
32
|
verbose: Flags.boolean({
|
|
@@ -36,24 +43,42 @@ export default class EnvRemove extends Command {
|
|
|
36
43
|
async run() {
|
|
37
44
|
const { args, flags } = await this.parse(EnvRemove);
|
|
38
45
|
setVerboseMode(flags.verbose);
|
|
39
|
-
const
|
|
40
|
-
|
|
46
|
+
const scope = resolveDefaultConfigScope();
|
|
47
|
+
const config = await loadAuthConfig({ scope });
|
|
48
|
+
if (!config.envs[args.name]) {
|
|
49
|
+
this.error(`Env "${args.name}" is not configured`);
|
|
50
|
+
}
|
|
51
|
+
const currentEnv = await getCurrentEnvName({ scope });
|
|
52
|
+
const skipConfirmation = flags.yes || flags.force;
|
|
53
|
+
if (!skipConfirmation) {
|
|
41
54
|
if (!isInteractiveTerminal()) {
|
|
42
|
-
this.error(
|
|
55
|
+
this.error(`Refusing to remove env "${args.name}" without confirmation in non-interactive mode. Re-run with \`--yes\` to remove only the saved CLI env config.`);
|
|
56
|
+
}
|
|
57
|
+
const subject = args.name === currentEnv ? `current env "${args.name}"` : `env "${args.name}"`;
|
|
58
|
+
let confirmed = false;
|
|
59
|
+
try {
|
|
60
|
+
confirmed = await confirm({
|
|
61
|
+
message: `Remove ${subject}? Only the saved CLI env config will be removed.`,
|
|
62
|
+
default: false,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return;
|
|
43
67
|
}
|
|
44
|
-
const confirmed = await confirmAction(`Remove current env "${args.name}"?`, { defaultValue: false });
|
|
45
68
|
if (!confirmed) {
|
|
46
|
-
this.log('Canceled.');
|
|
47
69
|
return;
|
|
48
70
|
}
|
|
49
71
|
}
|
|
50
72
|
printVerbose(`Removing env "${args.name}"`);
|
|
51
|
-
const result = await removeEnv(args.name, { scope
|
|
52
|
-
this.log(`Removed env "${result.removed}".`);
|
|
73
|
+
const result = await removeEnv(args.name, { scope });
|
|
53
74
|
if (result.hasEnvs) {
|
|
54
|
-
|
|
75
|
+
if (args.name === currentEnv) {
|
|
76
|
+
this.log(`Removed env "${result.removed}". Switched current env to "${await getCurrentEnvName({ scope })}".`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.log(`Removed env "${result.removed}".`);
|
|
55
80
|
return;
|
|
56
81
|
}
|
|
57
|
-
this.log(
|
|
82
|
+
this.log(`Removed env "${result.removed}". No envs configured.`);
|
|
58
83
|
}
|
|
59
84
|
}
|
|
@@ -12,7 +12,7 @@ import { Args, Command, Flags } from '@oclif/core';
|
|
|
12
12
|
import { getCurrentEnvName } from '../../lib/auth-store.js';
|
|
13
13
|
import { updateEnvRuntime } from '../../lib/bootstrap.js';
|
|
14
14
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
15
|
-
import { failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
15
|
+
import { failTask, printVerbose, setVerboseMode, startTask, stopTask, succeedTask } from '../../lib/ui.js';
|
|
16
16
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
export default class EnvUpdate extends Command {
|
|
18
18
|
static summary = 'Refresh an environment runtime from swagger:get and persist connection overrides';
|
|
@@ -44,6 +44,7 @@ export default class EnvUpdate extends Command {
|
|
|
44
44
|
};
|
|
45
45
|
async run() {
|
|
46
46
|
const { args, flags } = await this.parse(EnvUpdate);
|
|
47
|
+
setVerboseMode(Boolean(flags.verbose));
|
|
47
48
|
const envName = args.name;
|
|
48
49
|
const envLabel = envName ?? (await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
|
|
49
50
|
startTask(`Updating env runtime: ${envLabel}`);
|
|
@@ -57,7 +58,13 @@ export default class EnvUpdate extends Command {
|
|
|
57
58
|
configFile: path.join(path.dirname(path.dirname(path.dirname(__dirname))), 'nocobase-ctl.config.json'),
|
|
58
59
|
verbose: flags.verbose,
|
|
59
60
|
});
|
|
60
|
-
|
|
61
|
+
if (flags.verbose) {
|
|
62
|
+
succeedTask(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
stopTask();
|
|
66
|
+
printVerbose(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
catch (error) {
|
|
63
70
|
failTask(`Failed to update env "${envLabel}".`);
|
|
@@ -44,8 +44,8 @@ function buildWebUiStagesFromTestPrompts(c) {
|
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* With `--ui`: `runPromptCatalogWebUI` using `stages` + `sectionTitle`, then
|
|
47
|
-
* `runPromptCatalog` on the same catalog as `prompts-test`. Without `--ui`, same terminal
|
|
48
|
-
* as `prompts-test` (handy for comparing).
|
|
47
|
+
* `runPromptCatalog` on the same catalog as `prompts-test`. Without `--ui`, the same terminal prompt
|
|
48
|
+
* behavior as `prompts-test` (handy for comparing).
|
|
49
49
|
*/
|
|
50
50
|
export default class PromptsStages extends Command {
|
|
51
51
|
static hidden = true;
|
|
@@ -60,12 +60,12 @@ export default class PromptsStages extends Command {
|
|
|
60
60
|
];
|
|
61
61
|
static flags = {
|
|
62
62
|
ui: Flags.boolean({
|
|
63
|
-
description: 'Open the localhost form: `runPromptCatalogWebUI` with `stages` and `sectionTitle` (vs `prompts-test --ui`, which uses one `catalog`). Without --ui, behavior matches `prompts-test` (
|
|
63
|
+
description: 'Open the localhost form: `runPromptCatalogWebUI` with `stages` and `sectionTitle` (vs `prompts-test --ui`, which uses one `catalog`). Without --ui, behavior matches `prompts-test` (terminal prompt layer in TTY / presets).',
|
|
64
64
|
default: false,
|
|
65
65
|
}),
|
|
66
66
|
yes: Flags.boolean({
|
|
67
67
|
char: 'y',
|
|
68
|
-
description: 'Accept defaults only in the terminal (no
|
|
68
|
+
description: 'Accept defaults only in the terminal (no extra interactive prompts after submit); same semantics as `prompts-test` when used with the submitted or default preset.',
|
|
69
69
|
default: false,
|
|
70
70
|
}),
|
|
71
71
|
locale: Flags.string({
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import * as p from '@clack/prompts';
|
|
10
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
11
10
|
import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
|
|
12
11
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../../lib/cli-locale.js";
|
|
12
|
+
import { printInfo } from "../../lib/ui.js";
|
|
13
13
|
import { runPromptCatalogWebUI, } from "../../lib/prompt-web-ui.js";
|
|
14
14
|
export default class PromptsTest extends Command {
|
|
15
15
|
static hidden = true;
|
|
@@ -84,7 +84,7 @@ export default class PromptsTest extends Command {
|
|
|
84
84
|
initialValue: 'world',
|
|
85
85
|
placeholder: 'Your name',
|
|
86
86
|
/**
|
|
87
|
-
* Example: async validation (
|
|
87
|
+
* Example: async validation (the terminal prompt layer re-prompts; Web UI returns 400 on submit with this message).
|
|
88
88
|
*/
|
|
89
89
|
validate: async (v) => {
|
|
90
90
|
if (String(v).trim().length < 2) {
|
|
@@ -109,7 +109,7 @@ export default class PromptsTest extends Command {
|
|
|
109
109
|
type: 'run',
|
|
110
110
|
when: (values) => values.select === 'option1',
|
|
111
111
|
run: (values) => {
|
|
112
|
-
|
|
112
|
+
printInfo(`run block: select is option1 (current keys: ${Object.keys(values).join(', ')})`);
|
|
113
113
|
},
|
|
114
114
|
},
|
|
115
115
|
password: {
|
|
@@ -154,7 +154,7 @@ export default class PromptsTest extends Command {
|
|
|
154
154
|
});
|
|
155
155
|
this.log(JSON.stringify(results, null, 2));
|
|
156
156
|
}
|
|
157
|
-
/** Map oclif parse result into `values` presets: each key here skips that prompt (no
|
|
157
|
+
/** Map oclif parse result into `values` presets: each key here skips that prompt (no terminal prompt UI). */
|
|
158
158
|
buildPresetValuesFromParsed(args, flags) {
|
|
159
159
|
const preset = {};
|
|
160
160
|
const file = args.file ?? flags.file;
|