@nocobase/cli 2.1.0-beta.37 → 2.1.0-beta.38
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 +19 -7
- package/README.zh-CN.md +19 -7
- package/dist/commands/app/destroy.js +225 -0
- package/dist/commands/app/down.js +24 -254
- package/dist/commands/app/shared.js +122 -0
- package/dist/commands/app/start.js +40 -59
- package/dist/commands/app/stop.js +72 -26
- package/dist/commands/app/upgrade.js +310 -427
- package/dist/commands/db/start.js +23 -8
- package/dist/commands/license/plugins/shared.js +9 -3
- package/dist/commands/license/plugins/sync.js +54 -25
- package/dist/commands/source/download.js +29 -25
- package/dist/generated/command-registry.js +3 -2
- package/dist/lib/api-client.js +6 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +27 -21
- package/dist/lib/env-guard.js +1 -1
- package/dist/lib/generated-command.js +17 -0
- package/dist/lib/skills-manager.js +6 -0
- package/dist/lib/ui.js +4 -1
- package/dist/locale/en-US.json +51 -1
- package/dist/locale/zh-CN.json +51 -1
- package/package.json +34 -2
|
@@ -7,34 +7,18 @@
|
|
|
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 { upsertEnv } from '../../lib/auth-store.js';
|
|
11
|
-
import {
|
|
12
|
-
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, stopDockerContainer, } from '../../lib/app-runtime.js';
|
|
13
|
-
import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
|
|
14
|
-
import { deriveBuiltinDbConnection } from '../../lib/builtin-db.js';
|
|
15
|
-
import { resolveDockerEnvFileArg } from "../../lib/docker-env-file.js";
|
|
10
|
+
import { getCurrentEnvName, upsertEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../lib/app-runtime.js';
|
|
16
12
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
17
|
-
import { DEFAULT_DOCKER_REGISTRY
|
|
18
|
-
import {
|
|
19
|
-
import { announceTargetEnv,
|
|
20
|
-
const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
|
|
21
|
-
const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
|
|
22
|
-
const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
|
|
23
|
-
const APP_HEALTH_CHECK_REQUEST_TIMEOUT_MS = 5_000;
|
|
13
|
+
import { DEFAULT_DOCKER_REGISTRY } from "../../lib/docker-image.js";
|
|
14
|
+
import { confirm } from "../../lib/inquirer.js";
|
|
15
|
+
import { announceTargetEnv, isInteractiveTerminal, printInfo, printWarning, succeedTask } from '../../lib/ui.js';
|
|
24
16
|
function trimValue(value) {
|
|
25
17
|
return String(value ?? '').trim();
|
|
26
18
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
args.push('-e', `${key}=${value}`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (typeof value === 'boolean') {
|
|
36
|
-
args.push('-e', `${key}=${String(value)}`);
|
|
37
|
-
}
|
|
19
|
+
function normalizeEnvName(value) {
|
|
20
|
+
const text = trimValue(value);
|
|
21
|
+
return text || undefined;
|
|
38
22
|
}
|
|
39
23
|
function formatAppUrl(port) {
|
|
40
24
|
const value = trimValue(port);
|
|
@@ -51,27 +35,21 @@ function formatDisplayUrl(apiBaseUrl, appPort) {
|
|
|
51
35
|
}
|
|
52
36
|
return value.replace(/\/api\/?$/, '');
|
|
53
37
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
if (baseUrl) {
|
|
57
|
-
return baseUrl.replace(/\/+$/, '');
|
|
58
|
-
}
|
|
59
|
-
const appPort = runtime.env.appPort === undefined || runtime.env.appPort === null
|
|
60
|
-
? ''
|
|
61
|
-
: trimValue(runtime.env.appPort);
|
|
62
|
-
return appPort ? `http://127.0.0.1:${appPort}/api` : undefined;
|
|
63
|
-
}
|
|
64
|
-
function buildHealthCheckUrl(apiBaseUrl) {
|
|
65
|
-
return `${apiBaseUrl.replace(/\/+$/, '')}/__health_check`;
|
|
38
|
+
function readEnvValue(env, key) {
|
|
39
|
+
return trimValue(env.config[key]);
|
|
66
40
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
41
|
+
function normalizeDockerPlatform(value) {
|
|
42
|
+
const text = trimValue(value);
|
|
43
|
+
if (!text || text === 'auto') {
|
|
44
|
+
return undefined;
|
|
70
45
|
}
|
|
71
|
-
if (
|
|
72
|
-
return
|
|
46
|
+
if (text === 'linux/amd64' || text === 'linux/arm64') {
|
|
47
|
+
return text;
|
|
73
48
|
}
|
|
74
|
-
return
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
function isDownloadableLocalRuntime(runtime) {
|
|
52
|
+
return runtime.kind === 'local' && (runtime.source === 'npm' || runtime.source === 'git');
|
|
75
53
|
}
|
|
76
54
|
function formatLocalDownloadFailure(envName, source, message) {
|
|
77
55
|
const sourceLabel = source === 'git' ? 'the saved Git checkout' : 'the saved npm app';
|
|
@@ -82,16 +60,6 @@ function formatLocalDownloadFailure(envName, source, message) {
|
|
|
82
60
|
`Details: ${message}`,
|
|
83
61
|
].join('\n');
|
|
84
62
|
}
|
|
85
|
-
function formatLocalStartFailure(envName, source, port, message) {
|
|
86
|
-
const sourceLabel = dockerRefLabel(source);
|
|
87
|
-
const portHint = trimValue(port) ? ` Expected app port: ${trimValue(port)}.` : '';
|
|
88
|
-
const details = trimValue(message) ? ` Details: ${trimValue(message)}` : '';
|
|
89
|
-
return [
|
|
90
|
-
`Couldn't finish the upgrade for "${envName}".`,
|
|
91
|
-
`The CLI updated ${sourceLabel}, but it could not start the upgraded app successfully.`,
|
|
92
|
-
`Check the local dependencies, database connection, and saved env settings, then try again.${portHint}${details}`,
|
|
93
|
-
].join('\n');
|
|
94
|
-
}
|
|
95
63
|
function formatDockerDownloadFailure(envName, message) {
|
|
96
64
|
return [
|
|
97
65
|
`Couldn't refresh the Docker image for "${envName}".`,
|
|
@@ -100,122 +68,187 @@ function formatDockerDownloadFailure(envName, message) {
|
|
|
100
68
|
`Details: ${message}`,
|
|
101
69
|
].join('\n');
|
|
102
70
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
71
|
+
function buildManagedActionArgv(envName, flags, options) {
|
|
72
|
+
const argv = ['--env', envName, '--yes'];
|
|
73
|
+
if (flags.verbose) {
|
|
74
|
+
argv.push('--verbose');
|
|
75
|
+
}
|
|
76
|
+
if (options?.quickstart) {
|
|
77
|
+
argv.push('--quickstart');
|
|
78
|
+
}
|
|
79
|
+
return argv;
|
|
110
80
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
81
|
+
function shouldSkipDownload(flags) {
|
|
82
|
+
return Boolean(flags['skip-download'] || flags['skip-code-update']);
|
|
83
|
+
}
|
|
84
|
+
function buildUpgradeCliArgv(envName, flags, options) {
|
|
85
|
+
const argv = ['--env', envName];
|
|
86
|
+
if (shouldSkipDownload(flags)) {
|
|
87
|
+
argv.push('--skip-download');
|
|
115
88
|
}
|
|
116
|
-
|
|
117
|
-
|
|
89
|
+
const version = normalizeEnvName(flags.version);
|
|
90
|
+
if (version) {
|
|
91
|
+
argv.push('--version', version);
|
|
118
92
|
}
|
|
119
|
-
|
|
93
|
+
if (flags.verbose) {
|
|
94
|
+
argv.push('--verbose');
|
|
95
|
+
}
|
|
96
|
+
if (options?.yes ?? flags.yes) {
|
|
97
|
+
argv.push('--yes');
|
|
98
|
+
}
|
|
99
|
+
if (options?.force ?? flags.force) {
|
|
100
|
+
argv.push('--force');
|
|
101
|
+
}
|
|
102
|
+
return argv;
|
|
120
103
|
}
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
104
|
+
function buildUpgradeCliCommand(envName, flags, options) {
|
|
105
|
+
return ['nb', 'app', 'upgrade', ...buildUpgradeCliArgv(envName, flags, options)].join(' ');
|
|
106
|
+
}
|
|
107
|
+
function formatUpgradeOperationSummary(runtime, flags) {
|
|
108
|
+
const mayRunUpgradeMigrations = 'It may also run upgrade migrations.';
|
|
109
|
+
if (shouldSkipDownload(flags)) {
|
|
110
|
+
const sourceLabel = runtime.kind === 'docker'
|
|
111
|
+
? 'saved Docker image'
|
|
112
|
+
: runtime.source === 'local'
|
|
113
|
+
? 'saved local app path'
|
|
114
|
+
: runtime.source === 'git'
|
|
115
|
+
? 'saved Git checkout'
|
|
116
|
+
: 'saved npm app';
|
|
117
|
+
return [
|
|
118
|
+
'This operation will stop the app, skip source download and commercial plugin sync,',
|
|
119
|
+
`and start it again with the ${sourceLabel}.`,
|
|
120
|
+
mayRunUpgradeMigrations,
|
|
121
|
+
].join(' ');
|
|
124
122
|
}
|
|
125
|
-
|
|
123
|
+
if (runtime.kind === 'docker') {
|
|
124
|
+
return [
|
|
125
|
+
'This operation will stop the app, replace the saved Docker image,',
|
|
126
|
+
'sync commercial plugins when applicable, and start the app again.',
|
|
127
|
+
mayRunUpgradeMigrations,
|
|
128
|
+
].join(' ');
|
|
129
|
+
}
|
|
130
|
+
if (runtime.source === 'local') {
|
|
131
|
+
return [
|
|
132
|
+
'This operation will stop the app, reuse the saved local app path,',
|
|
133
|
+
'sync commercial plugins when applicable, and start the app again.',
|
|
134
|
+
mayRunUpgradeMigrations,
|
|
135
|
+
].join(' ');
|
|
136
|
+
}
|
|
137
|
+
return [
|
|
138
|
+
'This operation will stop the app, replace the saved source,',
|
|
139
|
+
'sync commercial plugins when applicable, and start the app again.',
|
|
140
|
+
mayRunUpgradeMigrations,
|
|
141
|
+
].join(' ');
|
|
126
142
|
}
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
function formatUpgradePromptSummary(flags) {
|
|
144
|
+
if (shouldSkipDownload(flags)) {
|
|
145
|
+
return 'This will stop and restart the app, and may run upgrade migrations.';
|
|
146
|
+
}
|
|
147
|
+
return 'This will stop and restart the app, update the saved source or image, and may run upgrade migrations.';
|
|
129
148
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
function formatUpgradeForceRequiredMessage(runtime, flags) {
|
|
150
|
+
return [
|
|
151
|
+
`\`nb app upgrade\` needs confirmation in non-interactive mode before upgrading "${runtime.envName}".`,
|
|
152
|
+
'',
|
|
153
|
+
formatUpgradeOperationSummary(runtime, flags),
|
|
154
|
+
'',
|
|
155
|
+
'Interactive confirmation is unavailable in the current AI agent session, and the agent will not add `--force` on your behalf.',
|
|
156
|
+
'',
|
|
157
|
+
'To continue:',
|
|
158
|
+
`- re-run \`${buildUpgradeCliCommand(runtime.envName, flags, { force: true })}\``,
|
|
159
|
+
`- or switch to an interactive terminal and re-run \`${buildUpgradeCliCommand(runtime.envName, flags, {
|
|
160
|
+
force: false,
|
|
161
|
+
})}\``,
|
|
162
|
+
].join('\n');
|
|
163
|
+
}
|
|
164
|
+
function formatMissingUpgradeFlagList(options) {
|
|
165
|
+
const missingFlags = [
|
|
166
|
+
options.missingYes ? '`--yes`' : undefined,
|
|
167
|
+
options.missingForce ? '`--force`' : undefined,
|
|
168
|
+
].filter(Boolean);
|
|
169
|
+
if (missingFlags.length <= 1) {
|
|
170
|
+
return missingFlags[0] ?? '';
|
|
146
171
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
return `${missingFlags[0]} or ${missingFlags[1]}`;
|
|
173
|
+
}
|
|
174
|
+
function formatUpgradeCrossEnvConfirmationRequiredMessage(currentEnv, runtime, flags, options) {
|
|
175
|
+
return [
|
|
176
|
+
`Refusing to upgrade env "${runtime.envName}" because the current env is "${currentEnv}" and interactive confirmation is unavailable in the current AI agent session.`,
|
|
177
|
+
'',
|
|
178
|
+
formatUpgradeOperationSummary(runtime, flags),
|
|
179
|
+
'',
|
|
180
|
+
`For safety, the agent will not switch envs automatically and will not add ${formatMissingUpgradeFlagList(options)} on your behalf.`,
|
|
181
|
+
'',
|
|
182
|
+
'To continue:',
|
|
183
|
+
`- run \`nb env use ${runtime.envName}\` yourself, then re-run \`${buildUpgradeCliCommand(runtime.envName, flags, {
|
|
184
|
+
yes: false,
|
|
185
|
+
force: true,
|
|
186
|
+
})}\``,
|
|
187
|
+
`- or re-run \`${buildUpgradeCliCommand(runtime.envName, flags, { yes: true, force: true })}\``,
|
|
188
|
+
].join('\n');
|
|
189
|
+
}
|
|
190
|
+
function buildLicenseSyncArgv(envName, flags, options) {
|
|
191
|
+
const argv = ['--env', envName, '--yes', '--skip-if-no-license'];
|
|
192
|
+
if (flags.verbose) {
|
|
193
|
+
argv.push('--verbose');
|
|
158
194
|
}
|
|
159
|
-
|
|
160
|
-
|
|
195
|
+
if (options?.version) {
|
|
196
|
+
argv.push('--version', options.version);
|
|
161
197
|
}
|
|
198
|
+
return argv;
|
|
162
199
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
200
|
+
function buildEnvUpdateArgv(envName, flags) {
|
|
201
|
+
const argv = [envName];
|
|
202
|
+
if (flags.verbose) {
|
|
203
|
+
argv.push('--verbose');
|
|
167
204
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
205
|
+
return argv;
|
|
206
|
+
}
|
|
207
|
+
function formatEnvUpdateWarning(envName, message) {
|
|
208
|
+
return [
|
|
209
|
+
`NocoBase was upgraded for "${envName}", but the CLI could not refresh the saved env runtime.`,
|
|
210
|
+
`Run \`nb env update ${envName}\` to refresh it manually.`,
|
|
211
|
+
`Details: ${message}`,
|
|
212
|
+
].join(' ');
|
|
213
|
+
}
|
|
214
|
+
async function runWithSuppressedTargetEnvLog(task) {
|
|
215
|
+
const previousTargetEnv = process.env.NB_SKIP_TARGET_ENV_LOG;
|
|
216
|
+
process.env.NB_SKIP_TARGET_ENV_LOG = '1';
|
|
173
217
|
try {
|
|
174
|
-
|
|
175
|
-
const result = await requestAppHealthCheck({
|
|
176
|
-
healthCheckUrl,
|
|
177
|
-
fetchImpl: params.fetchImpl,
|
|
178
|
-
});
|
|
179
|
-
if (result.ok) {
|
|
180
|
-
stopTask();
|
|
181
|
-
spinnerActive = false;
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
lastMessage = result.message;
|
|
185
|
-
const elapsedSeconds = Math.max(1, Math.floor((Date.now() - startedAt) / 1000));
|
|
186
|
-
updateTask(`Waiting for NocoBase to become ready for "${params.envName}"... (${elapsedSeconds}s elapsed, last status: ${lastMessage})`);
|
|
187
|
-
await sleep(APP_HEALTH_CHECK_INTERVAL_MS);
|
|
188
|
-
}
|
|
218
|
+
return await task();
|
|
189
219
|
}
|
|
190
220
|
finally {
|
|
191
|
-
if (
|
|
192
|
-
|
|
221
|
+
if (previousTargetEnv === undefined) {
|
|
222
|
+
delete process.env.NB_SKIP_TARGET_ENV_LOG;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
process.env.NB_SKIP_TARGET_ENV_LOG = previousTargetEnv;
|
|
193
226
|
}
|
|
194
227
|
}
|
|
195
|
-
const logHint = params.containerName
|
|
196
|
-
? ` You can inspect startup logs with: docker logs ${params.containerName}`
|
|
197
|
-
: '';
|
|
198
|
-
throw new Error(`The upgraded app for "${params.envName}" did not become ready in time. Expected \`${healthCheckUrl}\` to respond with \`ok\`, but the last status was: ${lastMessage}.${logHint}`);
|
|
199
|
-
}
|
|
200
|
-
async function dockerContainerExists(containerName) {
|
|
201
|
-
return await commandSucceeds('docker', ['container', 'inspect', containerName]);
|
|
202
228
|
}
|
|
203
|
-
async function
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
229
|
+
async function runWithSuppressedStartSuccessLog(task) {
|
|
230
|
+
const previousStartSuccess = process.env.NB_SKIP_APP_START_SUCCESS_LOG;
|
|
231
|
+
process.env.NB_SKIP_APP_START_SUCCESS_LOG = '1';
|
|
232
|
+
try {
|
|
233
|
+
return await task();
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
if (previousStartSuccess === undefined) {
|
|
237
|
+
delete process.env.NB_SKIP_APP_START_SUCCESS_LOG;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
process.env.NB_SKIP_APP_START_SUCCESS_LOG = previousStartSuccess;
|
|
241
|
+
}
|
|
207
242
|
}
|
|
208
|
-
await run('docker', ['network', 'create', name], {
|
|
209
|
-
errorName: 'docker network create',
|
|
210
|
-
stdio: 'ignore',
|
|
211
|
-
});
|
|
212
243
|
}
|
|
213
244
|
export default class AppUpgrade extends Command {
|
|
214
245
|
static hidden = false;
|
|
215
|
-
static description = 'Upgrade the selected NocoBase app.
|
|
246
|
+
static description = 'Upgrade the selected NocoBase app. The CLI stops the current app, optionally replaces the saved source or image, then starts the app again. Use --version to upgrade to a specific saved source version or image tag.';
|
|
216
247
|
static examples = [
|
|
217
248
|
'<%= config.bin %> <%= command.id %>',
|
|
249
|
+
'<%= config.bin %> <%= command.id %> --force',
|
|
218
250
|
'<%= config.bin %> <%= command.id %> --env local',
|
|
251
|
+
'<%= config.bin %> <%= command.id %> --env local --force',
|
|
219
252
|
'<%= config.bin %> <%= command.id %> --env local -s',
|
|
220
253
|
'<%= config.bin %> <%= command.id %> --env local --version beta',
|
|
221
254
|
'<%= config.bin %> <%= command.id %> --env local --verbose',
|
|
@@ -231,9 +264,20 @@ export default class AppUpgrade extends Command {
|
|
|
231
264
|
description: 'Confirm using --env when it targets a different env than the current env',
|
|
232
265
|
default: false,
|
|
233
266
|
}),
|
|
234
|
-
|
|
267
|
+
force: Flags.boolean({
|
|
268
|
+
char: 'f',
|
|
269
|
+
description: 'Skip the upgrade confirmation prompt',
|
|
270
|
+
default: false,
|
|
271
|
+
}),
|
|
272
|
+
'skip-download': Flags.boolean({
|
|
235
273
|
char: 's',
|
|
236
|
-
description: 'Restart with the saved local
|
|
274
|
+
description: 'Restart with the saved local source or Docker image without downloading updates first',
|
|
275
|
+
required: false,
|
|
276
|
+
}),
|
|
277
|
+
'skip-code-update': Flags.boolean({
|
|
278
|
+
hidden: true,
|
|
279
|
+
deprecated: true,
|
|
280
|
+
description: 'Deprecated alias for --skip-download',
|
|
237
281
|
required: false,
|
|
238
282
|
}),
|
|
239
283
|
version: Flags.string({
|
|
@@ -247,9 +291,6 @@ export default class AppUpgrade extends Command {
|
|
|
247
291
|
};
|
|
248
292
|
static resolveUpgradeVersion(runtime, flags) {
|
|
249
293
|
const requestedVersion = trimValue(flags.version);
|
|
250
|
-
if (requestedVersion && flags['skip-code-update']) {
|
|
251
|
-
throw new Error('`--version` and `--skip-code-update` cannot be used together. Use `--version` to download a specific upgrade target, or `--skip-code-update` to restart the saved code/image as-is.');
|
|
252
|
-
}
|
|
253
294
|
if (runtime.kind === 'local' && runtime.source === 'local') {
|
|
254
295
|
if (requestedVersion) {
|
|
255
296
|
throw new Error([
|
|
@@ -260,6 +301,11 @@ export default class AppUpgrade extends Command {
|
|
|
260
301
|
}
|
|
261
302
|
return {};
|
|
262
303
|
}
|
|
304
|
+
if (shouldSkipDownload(flags)) {
|
|
305
|
+
return {
|
|
306
|
+
persistDownloadVersion: requestedVersion || undefined,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
263
309
|
const savedVersion = readEnvValue(runtime.env, 'downloadVersion');
|
|
264
310
|
const downloadVersion = requestedVersion || savedVersion;
|
|
265
311
|
if (!downloadVersion) {
|
|
@@ -276,15 +322,15 @@ export default class AppUpgrade extends Command {
|
|
|
276
322
|
}
|
|
277
323
|
static buildLocalDownloadArgv(runtime, downloadVersion, options) {
|
|
278
324
|
const argv = ['-y', '--no-intro', '--source', runtime.source, '--replace'];
|
|
279
|
-
const gitUrl = readEnvValue(runtime.env, 'gitUrl');
|
|
280
|
-
const npmRegistry = readEnvValue(runtime.env, 'npmRegistry');
|
|
281
325
|
if (options?.verbose) {
|
|
282
326
|
argv.push('--verbose');
|
|
283
327
|
}
|
|
284
328
|
argv.push('--version', downloadVersion, '--output-dir', runtime.projectRoot);
|
|
329
|
+
const gitUrl = readEnvValue(runtime.env, 'gitUrl');
|
|
285
330
|
if (gitUrl) {
|
|
286
331
|
argv.push('--git-url', gitUrl);
|
|
287
332
|
}
|
|
333
|
+
const npmRegistry = readEnvValue(runtime.env, 'npmRegistry');
|
|
288
334
|
if (npmRegistry) {
|
|
289
335
|
argv.push('--npm-registry', npmRegistry);
|
|
290
336
|
}
|
|
@@ -299,278 +345,18 @@ export default class AppUpgrade extends Command {
|
|
|
299
345
|
}
|
|
300
346
|
return argv;
|
|
301
347
|
}
|
|
302
|
-
static buildDockerDownloadArgv(runtime,
|
|
348
|
+
static buildDockerDownloadArgv(runtime, downloadVersion, options) {
|
|
303
349
|
const argv = ['-y', '--no-intro'];
|
|
304
350
|
if (options?.verbose) {
|
|
305
351
|
argv.push('--verbose');
|
|
306
352
|
}
|
|
307
|
-
argv.push('--source', 'docker', '--replace', '--docker-registry',
|
|
353
|
+
argv.push('--source', 'docker', '--replace', '--docker-registry', readEnvValue(runtime.env, 'dockerRegistry') || DEFAULT_DOCKER_REGISTRY, '--version', downloadVersion);
|
|
308
354
|
const dockerPlatform = normalizeDockerPlatform(runtime.env.config.dockerPlatform);
|
|
309
355
|
if (dockerPlatform) {
|
|
310
356
|
argv.push('--docker-platform', dockerPlatform);
|
|
311
357
|
}
|
|
312
358
|
return argv;
|
|
313
359
|
}
|
|
314
|
-
static buildLocalStartArgv(runtime) {
|
|
315
|
-
const argv = ['start', '--quickstart'];
|
|
316
|
-
const appPort = runtime.env.appPort === undefined || runtime.env.appPort === null
|
|
317
|
-
? ''
|
|
318
|
-
: trimValue(runtime.env.appPort);
|
|
319
|
-
if (appPort) {
|
|
320
|
-
argv.push('--port', appPort);
|
|
321
|
-
}
|
|
322
|
-
argv.push('--daemon');
|
|
323
|
-
return argv;
|
|
324
|
-
}
|
|
325
|
-
static async buildDockerUpgradePlan(runtime, downloadVersion) {
|
|
326
|
-
const dockerRegistry = readEnvValue(runtime.env, 'dockerRegistry') || DEFAULT_DOCKER_REGISTRY;
|
|
327
|
-
const appPort = runtime.env.appPort === undefined || runtime.env.appPort === null
|
|
328
|
-
? ''
|
|
329
|
-
: trimValue(runtime.env.appPort);
|
|
330
|
-
const storagePath = readEnvValue(runtime.env, 'storagePath');
|
|
331
|
-
const envFile = await resolveDockerEnvFileArg(runtime.envName, runtime.env.config);
|
|
332
|
-
const appKey = readEnvValue(runtime.env, 'appKey');
|
|
333
|
-
const timeZone = readEnvValue(runtime.env, 'timezone');
|
|
334
|
-
const builtinDbConnection = runtime.env.config.builtinDb ? deriveBuiltinDbConnection(runtime) : undefined;
|
|
335
|
-
const dbDialect = builtinDbConnection?.dbDialect || readEnvValue(runtime.env, 'dbDialect');
|
|
336
|
-
const dbHost = builtinDbConnection?.dbHost || readEnvValue(runtime.env, 'dbHost');
|
|
337
|
-
const dbPort = builtinDbConnection?.dbPort || readEnvValue(runtime.env, 'dbPort');
|
|
338
|
-
const dbDatabase = readEnvValue(runtime.env, 'dbDatabase');
|
|
339
|
-
const dbUser = readEnvValue(runtime.env, 'dbUser');
|
|
340
|
-
const dbPassword = readEnvValue(runtime.env, 'dbPassword');
|
|
341
|
-
const dbSchema = readEnvValue(runtime.env, 'dbSchema');
|
|
342
|
-
const dbTablePrefix = readEnvValue(runtime.env, 'dbTablePrefix');
|
|
343
|
-
const dbUnderscored = typeof runtime.env.config.dbUnderscored === 'boolean'
|
|
344
|
-
? runtime.env.config.dbUnderscored
|
|
345
|
-
: undefined;
|
|
346
|
-
const networkName = trimValue(runtime.dockerNetworkName || runtime.workspaceName);
|
|
347
|
-
const missing = [];
|
|
348
|
-
if (!networkName) {
|
|
349
|
-
missing.push('docker.network');
|
|
350
|
-
}
|
|
351
|
-
if (!storagePath) {
|
|
352
|
-
missing.push('storagePath');
|
|
353
|
-
}
|
|
354
|
-
if (!appKey) {
|
|
355
|
-
missing.push('appKey');
|
|
356
|
-
}
|
|
357
|
-
if (!timeZone) {
|
|
358
|
-
missing.push('timezone');
|
|
359
|
-
}
|
|
360
|
-
if (!dbDialect) {
|
|
361
|
-
missing.push('dbDialect');
|
|
362
|
-
}
|
|
363
|
-
if (!dbHost) {
|
|
364
|
-
missing.push('dbHost');
|
|
365
|
-
}
|
|
366
|
-
if (!dbPort) {
|
|
367
|
-
missing.push('dbPort');
|
|
368
|
-
}
|
|
369
|
-
if (!dbDatabase) {
|
|
370
|
-
missing.push('dbDatabase');
|
|
371
|
-
}
|
|
372
|
-
if (!dbUser) {
|
|
373
|
-
missing.push('dbUser');
|
|
374
|
-
}
|
|
375
|
-
if (!dbPassword) {
|
|
376
|
-
missing.push('dbPassword');
|
|
377
|
-
}
|
|
378
|
-
if (missing.length > 0) {
|
|
379
|
-
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.`);
|
|
380
|
-
}
|
|
381
|
-
const imageRef = resolveDockerImageRef(dockerRegistry, downloadVersion, {
|
|
382
|
-
defaultRegistry: DEFAULT_DOCKER_REGISTRY,
|
|
383
|
-
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
384
|
-
});
|
|
385
|
-
const args = [
|
|
386
|
-
'run',
|
|
387
|
-
'-d',
|
|
388
|
-
'--name',
|
|
389
|
-
runtime.containerName,
|
|
390
|
-
'--restart',
|
|
391
|
-
'always',
|
|
392
|
-
'--network',
|
|
393
|
-
networkName,
|
|
394
|
-
];
|
|
395
|
-
if (appPort) {
|
|
396
|
-
args.push('-p', `${appPort}:80`);
|
|
397
|
-
}
|
|
398
|
-
if (envFile) {
|
|
399
|
-
args.push('--env-file', envFile);
|
|
400
|
-
}
|
|
401
|
-
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}`);
|
|
402
|
-
pushOptionalEnvArg(args, 'DB_SCHEMA', dbSchema || undefined);
|
|
403
|
-
pushOptionalEnvArg(args, 'DB_TABLE_PREFIX', dbTablePrefix || undefined);
|
|
404
|
-
pushOptionalEnvArg(args, 'DB_UNDERSCORED', dbUnderscored);
|
|
405
|
-
args.push(imageRef);
|
|
406
|
-
return {
|
|
407
|
-
containerName: runtime.containerName,
|
|
408
|
-
networkName,
|
|
409
|
-
dockerRegistry,
|
|
410
|
-
downloadVersion,
|
|
411
|
-
imageRef,
|
|
412
|
-
appPort: appPort || undefined,
|
|
413
|
-
storagePath,
|
|
414
|
-
envFile,
|
|
415
|
-
appKey,
|
|
416
|
-
timeZone,
|
|
417
|
-
dbDialect,
|
|
418
|
-
dbHost,
|
|
419
|
-
dbPort,
|
|
420
|
-
dbDatabase,
|
|
421
|
-
dbUser,
|
|
422
|
-
dbPassword,
|
|
423
|
-
args,
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
static async upgradeLocal(runCommand, runtime, downloadVersion, flags, commandStdio) {
|
|
427
|
-
const displayUrl = formatDisplayUrl(resolveApiBaseUrl(runtime), trimValue(runtime.env.appPort));
|
|
428
|
-
startTask(`Stopping NocoBase for "${runtime.envName}" before upgrade...`);
|
|
429
|
-
try {
|
|
430
|
-
await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
|
|
431
|
-
stdio: commandStdio,
|
|
432
|
-
});
|
|
433
|
-
succeedTask(`Stopped the current NocoBase process for "${runtime.envName}".`);
|
|
434
|
-
}
|
|
435
|
-
catch (error) {
|
|
436
|
-
stopTask();
|
|
437
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
438
|
-
printInfo(`No running background process was stopped for "${runtime.envName}". Continuing with the upgrade. (${message})`);
|
|
439
|
-
}
|
|
440
|
-
if (!flags['skip-code-update'] && (runtime.source === 'npm' || runtime.source === 'git')) {
|
|
441
|
-
startTask(`Refreshing NocoBase files for "${runtime.envName}" from the saved ${runtime.source} source...`);
|
|
442
|
-
try {
|
|
443
|
-
await runCommand('source:download', AppUpgrade.buildLocalDownloadArgv(runtime, downloadVersion, {
|
|
444
|
-
verbose: flags.verbose,
|
|
445
|
-
}));
|
|
446
|
-
succeedTask(`NocoBase files are up to date for "${runtime.envName}".`);
|
|
447
|
-
}
|
|
448
|
-
catch (error) {
|
|
449
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
450
|
-
failTask(`Failed to refresh NocoBase files for "${runtime.envName}".`);
|
|
451
|
-
throw new Error(formatLocalDownloadFailure(runtime.envName, runtime.source, message));
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
else if (flags['skip-code-update']) {
|
|
455
|
-
printInfo(`Skipping code download for "${runtime.envName}" (--skip-code-update).`);
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
printInfo(`Skipping code download for "${runtime.envName}" because this env is managed from an existing local app path.`);
|
|
459
|
-
}
|
|
460
|
-
await ensureLocalPostinstall(runtime, {
|
|
461
|
-
verbose: flags.verbose,
|
|
462
|
-
onStartTask: startTask,
|
|
463
|
-
onSucceedTask: succeedTask,
|
|
464
|
-
onFailTask: failTask,
|
|
465
|
-
});
|
|
466
|
-
startTask(`Starting upgraded NocoBase for "${runtime.envName}"...`);
|
|
467
|
-
try {
|
|
468
|
-
await runLocalNocoBaseCommand(runtime, AppUpgrade.buildLocalStartArgv(runtime), {
|
|
469
|
-
stdio: commandStdio,
|
|
470
|
-
});
|
|
471
|
-
succeedTask(`Upgraded NocoBase is starting for "${runtime.envName}".`);
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
475
|
-
failTask(`Failed to start upgraded NocoBase for "${runtime.envName}".`);
|
|
476
|
-
throw new Error(formatLocalStartFailure(runtime.envName, runtime.source, trimValue(runtime.env.appPort), message));
|
|
477
|
-
}
|
|
478
|
-
await waitForAppHealthCheck({
|
|
479
|
-
envName: runtime.envName,
|
|
480
|
-
apiBaseUrl: resolveApiBaseUrl(runtime),
|
|
481
|
-
});
|
|
482
|
-
return displayUrl;
|
|
483
|
-
}
|
|
484
|
-
static async upgradeDocker(runCommand, runtime, downloadVersion, flags, commandStdio) {
|
|
485
|
-
const apiBaseUrl = resolveApiBaseUrl(runtime);
|
|
486
|
-
const containerExists = await dockerContainerExists(runtime.containerName);
|
|
487
|
-
if (!flags['skip-code-update']) {
|
|
488
|
-
const plan = await AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
489
|
-
startTask(`Refreshing the Docker image for "${runtime.envName}"...`);
|
|
490
|
-
try {
|
|
491
|
-
await runCommand('source:download', AppUpgrade.buildDockerDownloadArgv(runtime, plan, {
|
|
492
|
-
verbose: flags.verbose,
|
|
493
|
-
}));
|
|
494
|
-
succeedTask(`Docker image is ready for "${runtime.envName}".`);
|
|
495
|
-
}
|
|
496
|
-
catch (error) {
|
|
497
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
498
|
-
failTask(`Failed to refresh the Docker image for "${runtime.envName}".`);
|
|
499
|
-
throw new Error(formatDockerDownloadFailure(runtime.envName, message));
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
printInfo(`Skipping image download for "${runtime.envName}" (--skip-code-update).`);
|
|
504
|
-
}
|
|
505
|
-
if (containerExists) {
|
|
506
|
-
startTask(`Stopping the current Docker app for "${runtime.envName}"...`);
|
|
507
|
-
try {
|
|
508
|
-
const state = await stopDockerContainer(runtime.containerName, {
|
|
509
|
-
stdio: commandStdio,
|
|
510
|
-
});
|
|
511
|
-
succeedTask(state === 'already-stopped'
|
|
512
|
-
? `The current Docker app was already stopped for "${runtime.envName}".`
|
|
513
|
-
: `Stopped the current Docker app for "${runtime.envName}".`);
|
|
514
|
-
}
|
|
515
|
-
catch (error) {
|
|
516
|
-
stopTask();
|
|
517
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
518
|
-
printInfo(`Could not stop the existing Docker container for "${runtime.envName}" cleanly. Continuing with container recreation. (${message})`);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
if (flags['skip-code-update'] && containerExists) {
|
|
522
|
-
startTask(`Starting NocoBase for "${runtime.envName}" with the saved Docker image...`);
|
|
523
|
-
try {
|
|
524
|
-
const state = await startDockerContainer(runtime.containerName, {
|
|
525
|
-
stdio: commandStdio,
|
|
526
|
-
});
|
|
527
|
-
succeedTask(state === 'already-running'
|
|
528
|
-
? `NocoBase is already running for "${runtime.envName}".`
|
|
529
|
-
: `NocoBase is starting for "${runtime.envName}".`);
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
533
|
-
failTask(`Failed to start the Docker app for "${runtime.envName}".`);
|
|
534
|
-
throw new Error(formatDockerStartFailure(runtime.envName, message));
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
const plan = await AppUpgrade.buildDockerUpgradePlan(runtime, downloadVersion);
|
|
539
|
-
const displayUrl = formatDisplayUrl(apiBaseUrl, plan.appPort);
|
|
540
|
-
startTask(`Recreating the Docker app container for "${runtime.envName}"...`);
|
|
541
|
-
try {
|
|
542
|
-
if (containerExists) {
|
|
543
|
-
await run('docker', ['rm', '-f', runtime.containerName], {
|
|
544
|
-
errorName: 'docker rm',
|
|
545
|
-
stdio: commandStdio,
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
await ensureDockerNetwork(plan.networkName);
|
|
549
|
-
await run('docker', plan.args, {
|
|
550
|
-
errorName: 'docker run',
|
|
551
|
-
stdio: commandStdio,
|
|
552
|
-
});
|
|
553
|
-
succeedTask(`Docker app container is ready for "${runtime.envName}".`);
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
557
|
-
failTask(`Failed to recreate the Docker app for "${runtime.envName}".`);
|
|
558
|
-
throw new Error(formatDockerStartFailure(runtime.envName, message));
|
|
559
|
-
}
|
|
560
|
-
await waitForAppHealthCheck({
|
|
561
|
-
envName: runtime.envName,
|
|
562
|
-
apiBaseUrl,
|
|
563
|
-
containerName: runtime.containerName,
|
|
564
|
-
});
|
|
565
|
-
return displayUrl;
|
|
566
|
-
}
|
|
567
|
-
await waitForAppHealthCheck({
|
|
568
|
-
envName: runtime.envName,
|
|
569
|
-
apiBaseUrl,
|
|
570
|
-
containerName: runtime.containerName,
|
|
571
|
-
});
|
|
572
|
-
return formatDisplayUrl(apiBaseUrl, trimValue(runtime.env.appPort));
|
|
573
|
-
}
|
|
574
360
|
static async persistDownloadVersion(runtime, downloadVersion) {
|
|
575
361
|
const { name: _name, ...envConfig } = runtime.env.config;
|
|
576
362
|
try {
|
|
@@ -587,18 +373,8 @@ export default class AppUpgrade extends Command {
|
|
|
587
373
|
async run() {
|
|
588
374
|
const { flags } = await this.parse(AppUpgrade);
|
|
589
375
|
const parsed = flags;
|
|
590
|
-
const requestedEnv = parsed.env
|
|
591
|
-
|
|
592
|
-
const confirmed = await ensureCrossEnvConfirmed({
|
|
593
|
-
command: this,
|
|
594
|
-
requestedEnv,
|
|
595
|
-
yes: parsed.yes,
|
|
596
|
-
});
|
|
597
|
-
if (!confirmed) {
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
const commandStdio = parsed.verbose ? 'inherit' : 'ignore';
|
|
376
|
+
const requestedEnv = normalizeEnvName(parsed.env);
|
|
377
|
+
const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
|
|
602
378
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
603
379
|
if (!runtime) {
|
|
604
380
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -617,16 +393,123 @@ export default class AppUpgrade extends Command {
|
|
|
617
393
|
'Use a local or Docker env if you need CLI-managed upgrades right now.',
|
|
618
394
|
].join('\n'));
|
|
619
395
|
}
|
|
396
|
+
const interactiveTerminal = isInteractiveTerminal();
|
|
397
|
+
if (explicitEnvSelection) {
|
|
398
|
+
if (!interactiveTerminal) {
|
|
399
|
+
const currentEnv = normalizeEnvName(await getCurrentEnvName());
|
|
400
|
+
if (currentEnv && currentEnv !== requestedEnv) {
|
|
401
|
+
const missingYes = !parsed.yes;
|
|
402
|
+
const missingForce = !parsed.force;
|
|
403
|
+
if (missingYes || missingForce) {
|
|
404
|
+
this.error(formatUpgradeCrossEnvConfirmationRequiredMessage(currentEnv, runtime, parsed, {
|
|
405
|
+
missingYes,
|
|
406
|
+
missingForce,
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
413
|
+
command: this,
|
|
414
|
+
requestedEnv,
|
|
415
|
+
yes: parsed.yes,
|
|
416
|
+
});
|
|
417
|
+
if (!confirmed) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (!interactiveTerminal) {
|
|
423
|
+
if (!parsed.force) {
|
|
424
|
+
this.error(formatUpgradeForceRequiredMessage(runtime, parsed));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else if (!parsed.force) {
|
|
428
|
+
let confirmed = false;
|
|
429
|
+
try {
|
|
430
|
+
confirmed = await confirm({
|
|
431
|
+
message: `Upgrade "${runtime.envName}"? ${formatUpgradePromptSummary(parsed)}`,
|
|
432
|
+
default: false,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (!confirmed) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
620
442
|
announceTargetEnv(runtime.envName);
|
|
621
443
|
try {
|
|
622
444
|
const resolvedVersion = AppUpgrade.resolveUpgradeVersion(runtime, parsed);
|
|
445
|
+
const skipDownload = shouldSkipDownload(parsed);
|
|
623
446
|
const runCommand = this.config.runCommand.bind(this.config);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
447
|
+
await runWithSuppressedTargetEnvLog(async () => {
|
|
448
|
+
await runCommand('app:stop', buildManagedActionArgv(runtime.envName, parsed));
|
|
449
|
+
});
|
|
450
|
+
if (skipDownload) {
|
|
451
|
+
printInfo(`Skipping source download for "${runtime.envName}" (--skip-download).`);
|
|
452
|
+
printInfo(`Skipping commercial plugin sync for "${runtime.envName}" (--skip-download).`);
|
|
453
|
+
}
|
|
454
|
+
else if (runtime.kind === 'local' && runtime.source === 'local') {
|
|
455
|
+
printInfo(`Skipping source download for "${runtime.envName}" because this env is managed from an existing local app path.`);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
const downloadVersion = resolvedVersion.downloadVersion;
|
|
459
|
+
if (!downloadVersion) {
|
|
460
|
+
throw new Error(`Missing downloadVersion for "${runtime.envName}".`);
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
if (runtime.kind === 'docker') {
|
|
464
|
+
await runCommand('source:download', AppUpgrade.buildDockerDownloadArgv(runtime, downloadVersion, {
|
|
465
|
+
verbose: parsed.verbose,
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
else if (isDownloadableLocalRuntime(runtime)) {
|
|
469
|
+
await runCommand('source:download', AppUpgrade.buildLocalDownloadArgv(runtime, downloadVersion, {
|
|
470
|
+
verbose: parsed.verbose,
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
throw new Error(`Skipping source download for "${runtime.envName}" because this env is managed from an existing local app path.`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
479
|
+
if (runtime.kind === 'docker') {
|
|
480
|
+
throw new Error(formatDockerDownloadFailure(runtime.envName, message));
|
|
481
|
+
}
|
|
482
|
+
if (isDownloadableLocalRuntime(runtime)) {
|
|
483
|
+
throw new Error(formatLocalDownloadFailure(runtime.envName, runtime.source, message));
|
|
484
|
+
}
|
|
485
|
+
throw new Error(message);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (!skipDownload) {
|
|
489
|
+
await runWithSuppressedTargetEnvLog(async () => {
|
|
490
|
+
await runCommand('license:plugins:sync', buildLicenseSyncArgv(runtime.envName, parsed, {
|
|
491
|
+
version: resolvedVersion.persistDownloadVersion,
|
|
492
|
+
}));
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
await runWithSuppressedTargetEnvLog(async () => {
|
|
496
|
+
await runWithSuppressedStartSuccessLog(async () => {
|
|
497
|
+
await runCommand('app:start', buildManagedActionArgv(runtime.envName, parsed, { quickstart: true }));
|
|
498
|
+
});
|
|
499
|
+
});
|
|
627
500
|
if (resolvedVersion.persistDownloadVersion) {
|
|
628
501
|
await AppUpgrade.persistDownloadVersion(runtime, resolvedVersion.persistDownloadVersion);
|
|
629
502
|
}
|
|
503
|
+
try {
|
|
504
|
+
await runWithSuppressedTargetEnvLog(async () => {
|
|
505
|
+
await runCommand('env:update', buildEnvUpdateArgv(runtime.envName, parsed));
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
510
|
+
printWarning(formatEnvUpdateWarning(runtime.envName, message));
|
|
511
|
+
}
|
|
512
|
+
const displayUrl = formatDisplayUrl(runtime.env.baseUrl, runtime.env.appPort === undefined || runtime.env.appPort === null ? undefined : String(runtime.env.appPort));
|
|
630
513
|
succeedTask(`NocoBase has been upgraded for "${runtime.envName}"${displayUrl ? ` at ${displayUrl}` : ''}.`);
|
|
631
514
|
}
|
|
632
515
|
catch (error) {
|