@nocobase/cli 2.1.9 → 2.1.11-test.1
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/restart.js +38 -0
- package/dist/commands/app/start.js +92 -0
- package/dist/commands/app/upgrade.js +11 -0
- package/dist/commands/init.js +15 -1
- package/dist/commands/install.js +135 -4
- package/dist/commands/self/update.js +1 -1
- package/dist/commands/source/dev.js +9 -5
- package/dist/commands/source/download.js +66 -0
- package/dist/lib/app-managed-resources.js +101 -1
- package/dist/lib/auth-store.js +7 -1
- package/dist/lib/cli-config.js +19 -0
- package/dist/lib/env-config.js +1 -0
- package/dist/lib/hook-script.js +160 -0
- package/dist/lib/run-npm.js +4 -0
- package/dist/lib/self-manager.js +64 -11
- package/dist/lib/source-publish.js +2 -2
- package/dist/lib/startup-update.js +1 -1
- package/package.json +2 -3
|
@@ -14,6 +14,7 @@ import { recreateSavedDockerApp } from '../../lib/app-managed-resources.js';
|
|
|
14
14
|
import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
|
|
15
15
|
import { run } from '../../lib/run-npm.js';
|
|
16
16
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
17
|
+
import { buildHookContext, resolveHookScriptPath, runHookScriptHook } from '../../lib/hook-script.js';
|
|
17
18
|
function argvHasToken(argv, tokens) {
|
|
18
19
|
return tokens.some((token) => argv.includes(token));
|
|
19
20
|
}
|
|
@@ -62,6 +63,39 @@ function resolveDisplayAppUrl(apiBaseUrl, port, appPublicPath) {
|
|
|
62
63
|
}
|
|
63
64
|
return formatAppUrl(port, appPublicPath);
|
|
64
65
|
}
|
|
66
|
+
async function runDockerAfterAppStartHookIfNeeded(runtime) {
|
|
67
|
+
const hookScript = runtime.env.config?.hookScript;
|
|
68
|
+
if (!hookScript) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
72
|
+
appPath: runtime.env.appPath,
|
|
73
|
+
hookScript,
|
|
74
|
+
});
|
|
75
|
+
if (!hookScriptPath) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const context = buildHookContext({
|
|
79
|
+
phase: 'app-start',
|
|
80
|
+
command: 'app:restart',
|
|
81
|
+
envName: runtime.envName,
|
|
82
|
+
source: runtime.source,
|
|
83
|
+
version: runtime.env.config?.downloadVersion,
|
|
84
|
+
appPath: runtime.env.appPath,
|
|
85
|
+
sourcePath: runtime.env.sourcePath,
|
|
86
|
+
storagePath: runtime.env.storagePath,
|
|
87
|
+
hookScript: String(hookScript).trim(),
|
|
88
|
+
envConfig: runtime.env.config,
|
|
89
|
+
});
|
|
90
|
+
if (!context) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await runHookScriptHook({
|
|
94
|
+
hookScriptPath,
|
|
95
|
+
hookName: 'afterAppStart',
|
|
96
|
+
context,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
65
99
|
export default class AppRestart extends Command {
|
|
66
100
|
static hidden = false;
|
|
67
101
|
static description = 'Restart NocoBase for the selected env. When applicable, the CLI synchronizes licensed commercial plugins first, then restarts the local app or recreates the saved Docker container.';
|
|
@@ -199,6 +233,7 @@ export default class AppRestart extends Command {
|
|
|
199
233
|
logHint: `You can inspect startup logs with \`nb app logs --env ${runtime.envName}\`.`,
|
|
200
234
|
...(flags.verbose ? { verbose: true } : {}),
|
|
201
235
|
});
|
|
236
|
+
await runDockerAfterAppStartHookIfNeeded(runtime);
|
|
202
237
|
succeedTask(`NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
|
|
203
238
|
return;
|
|
204
239
|
}
|
|
@@ -222,6 +257,9 @@ export default class AppRestart extends Command {
|
|
|
222
257
|
if (daemonFlagWasProvided) {
|
|
223
258
|
startArgv.push(flags.daemon === false ? '--no-daemon' : '--daemon');
|
|
224
259
|
}
|
|
260
|
+
if (runtime.env.config?.hookScript) {
|
|
261
|
+
startArgv.push('--hook-command', 'app:restart');
|
|
262
|
+
}
|
|
225
263
|
await runWithSuppressedTargetEnvLog(async () => {
|
|
226
264
|
await runCommand('app:start', startArgv);
|
|
227
265
|
});
|
|
@@ -18,6 +18,7 @@ import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
|
|
|
18
18
|
import { readManagedRuntimeEnvValues } from '../../lib/managed-env-file.js';
|
|
19
19
|
import { run } from '../../lib/run-npm.js';
|
|
20
20
|
import { announceTargetEnv, failTask, printInfo, printWarning, startTask, succeedTask } from '../../lib/ui.js';
|
|
21
|
+
import { buildHookContext, resolveHookScriptPath, runHookScriptHook, } from '../../lib/hook-script.js';
|
|
21
22
|
function shouldPrintStartSuccess() {
|
|
22
23
|
return process.env.NB_SKIP_APP_START_SUCCESS_LOG !== '1';
|
|
23
24
|
}
|
|
@@ -34,6 +35,57 @@ function buildLicenseSyncArgv(envName, options) {
|
|
|
34
35
|
}
|
|
35
36
|
return argv;
|
|
36
37
|
}
|
|
38
|
+
function resolveHookCommand(value) {
|
|
39
|
+
const text = String(value ?? '').trim();
|
|
40
|
+
if (text === 'app:restart' || text === 'app:upgrade') {
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
return 'app:start';
|
|
44
|
+
}
|
|
45
|
+
function resolveAppStartHookPhase(options) {
|
|
46
|
+
if (options.isPreparedEnv) {
|
|
47
|
+
return 'init';
|
|
48
|
+
}
|
|
49
|
+
if (options.command === 'app:upgrade') {
|
|
50
|
+
return 'upgrade';
|
|
51
|
+
}
|
|
52
|
+
return 'app-start';
|
|
53
|
+
}
|
|
54
|
+
async function runRuntimeHookIfNeeded(params) {
|
|
55
|
+
const hookScript = params.runtime.env.config?.hookScript;
|
|
56
|
+
if (!hookScript) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
60
|
+
appPath: params.runtime.env.appPath,
|
|
61
|
+
hookScript,
|
|
62
|
+
});
|
|
63
|
+
if (!hookScriptPath) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const context = buildHookContext({
|
|
67
|
+
phase: params.phase,
|
|
68
|
+
command: params.command,
|
|
69
|
+
envName: params.runtime.envName,
|
|
70
|
+
source: params.runtime.source,
|
|
71
|
+
version: params.runtime.env.config?.downloadVersion,
|
|
72
|
+
appPath: params.runtime.env.appPath,
|
|
73
|
+
sourcePath: params.runtime.env.sourcePath,
|
|
74
|
+
storagePath: params.runtime.env.storagePath,
|
|
75
|
+
hookScript: String(hookScript).trim(),
|
|
76
|
+
envConfig: params.hookName === 'afterAppStart' && params.phase === 'init'
|
|
77
|
+
? { ...params.runtime.env.config, setupState: 'installed' }
|
|
78
|
+
: params.runtime.env.config,
|
|
79
|
+
});
|
|
80
|
+
if (!context) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await runHookScriptHook({
|
|
84
|
+
hookScriptPath,
|
|
85
|
+
hookName: params.hookName,
|
|
86
|
+
context,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
37
89
|
async function runWithSuppressedTargetEnvLog(task) {
|
|
38
90
|
const previousTargetEnv = process.env.NB_SKIP_TARGET_ENV_LOG;
|
|
39
91
|
process.env.NB_SKIP_TARGET_ENV_LOG = '1';
|
|
@@ -171,6 +223,10 @@ export default class AppStart extends Command {
|
|
|
171
223
|
description: 'Show raw startup output from the underlying local or Docker command',
|
|
172
224
|
default: false,
|
|
173
225
|
}),
|
|
226
|
+
'hook-command': Flags.string({
|
|
227
|
+
hidden: true,
|
|
228
|
+
options: ['app:start', 'app:restart', 'app:upgrade'],
|
|
229
|
+
}),
|
|
174
230
|
};
|
|
175
231
|
async run() {
|
|
176
232
|
const { flags } = await this.parse(AppStart);
|
|
@@ -192,6 +248,12 @@ export default class AppStart extends Command {
|
|
|
192
248
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
193
249
|
const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config);
|
|
194
250
|
const isPreparedEnv = isPreparedSetupState(runtime?.env.config?.setupState);
|
|
251
|
+
const hookCommand = resolveHookCommand(flags['hook-command']);
|
|
252
|
+
const hookPhase = resolveAppStartHookPhase({
|
|
253
|
+
isPreparedEnv,
|
|
254
|
+
command: hookCommand,
|
|
255
|
+
});
|
|
256
|
+
const shouldRunBeforeAppInstall = isPreparedEnv || hookCommand === 'app:upgrade';
|
|
195
257
|
const runCommand = this.config.runCommand.bind(this.config);
|
|
196
258
|
const commandStdio = flags.verbose ? 'inherit' : 'ignore';
|
|
197
259
|
if (!runtime) {
|
|
@@ -249,6 +311,14 @@ export default class AppStart extends Command {
|
|
|
249
311
|
errorName: 'docker rm',
|
|
250
312
|
stdio: commandStdio,
|
|
251
313
|
}).catch(() => undefined);
|
|
314
|
+
if (shouldRunBeforeAppInstall) {
|
|
315
|
+
await runRuntimeHookIfNeeded({
|
|
316
|
+
hookName: 'beforeAppInstall',
|
|
317
|
+
runtime,
|
|
318
|
+
phase: hookPhase,
|
|
319
|
+
command: hookCommand,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
252
322
|
await recreateSavedDockerApp(runtime, {
|
|
253
323
|
initEnvVars: isPreparedEnv ? preparedInitEnvVars : undefined,
|
|
254
324
|
verbose: flags.verbose,
|
|
@@ -270,6 +340,12 @@ export default class AppStart extends Command {
|
|
|
270
340
|
if (isPreparedEnv) {
|
|
271
341
|
await finalizePreparedEnv(runtime.envName);
|
|
272
342
|
}
|
|
343
|
+
await runRuntimeHookIfNeeded({
|
|
344
|
+
hookName: 'afterAppStart',
|
|
345
|
+
runtime,
|
|
346
|
+
phase: hookPhase,
|
|
347
|
+
command: hookCommand,
|
|
348
|
+
});
|
|
273
349
|
if (shouldPrintStartSuccess()) {
|
|
274
350
|
succeedTask(`NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
|
|
275
351
|
}
|
|
@@ -285,6 +361,8 @@ export default class AppStart extends Command {
|
|
|
285
361
|
const downloadableRuntime = runtime;
|
|
286
362
|
await ensureSavedLocalSource(downloadableRuntime, runCommand, {
|
|
287
363
|
verbose: flags.verbose,
|
|
364
|
+
hookPhase: isPreparedEnv ? 'init' : 'restore',
|
|
365
|
+
hookCommand,
|
|
288
366
|
onStartTask: startTask,
|
|
289
367
|
onSucceedTask: succeedTask,
|
|
290
368
|
onFailTask: failTask,
|
|
@@ -381,6 +459,14 @@ export default class AppStart extends Command {
|
|
|
381
459
|
...managedAppLifecycleEnvVars(),
|
|
382
460
|
...(isPreparedEnv ? preparedInitEnvVars : {}),
|
|
383
461
|
};
|
|
462
|
+
if (shouldRunBeforeAppInstall) {
|
|
463
|
+
await runRuntimeHookIfNeeded({
|
|
464
|
+
hookName: 'beforeAppInstall',
|
|
465
|
+
runtime,
|
|
466
|
+
phase: hookPhase,
|
|
467
|
+
command: hookCommand,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
384
470
|
await runLocalNocoBaseCommand(runtime, npmArgs, {
|
|
385
471
|
env: startEnv,
|
|
386
472
|
stdio: commandStdio,
|
|
@@ -394,6 +480,12 @@ export default class AppStart extends Command {
|
|
|
394
480
|
if (isPreparedEnv) {
|
|
395
481
|
await finalizePreparedEnv(runtime.envName);
|
|
396
482
|
}
|
|
483
|
+
await runRuntimeHookIfNeeded({
|
|
484
|
+
hookName: 'afterAppStart',
|
|
485
|
+
runtime,
|
|
486
|
+
phase: hookPhase,
|
|
487
|
+
command: hookCommand,
|
|
488
|
+
});
|
|
397
489
|
if (shouldPrintStartSuccess()) {
|
|
398
490
|
succeedTask(`NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
|
|
399
491
|
}
|
|
@@ -12,6 +12,7 @@ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '..
|
|
|
12
12
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
13
13
|
import { DEFAULT_DOCKER_REGISTRY } from "../../lib/docker-image.js";
|
|
14
14
|
import { confirm } from "../../lib/inquirer.js";
|
|
15
|
+
import { resolveHookScriptPath } from '../../lib/hook-script.js';
|
|
15
16
|
import { announceTargetEnv, isInteractiveTerminal, printInfo, printWarning, succeedTask } from '../../lib/ui.js';
|
|
16
17
|
import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
|
|
17
18
|
function trimValue(value) {
|
|
@@ -345,6 +346,13 @@ export default class AppUpgrade extends Command {
|
|
|
345
346
|
if (runtime.env.config.buildDts === true) {
|
|
346
347
|
argv.push('--build-dts');
|
|
347
348
|
}
|
|
349
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
350
|
+
appPath: runtime.env.appPath,
|
|
351
|
+
hookScript: runtime.env.config.hookScript,
|
|
352
|
+
});
|
|
353
|
+
if (hookScriptPath) {
|
|
354
|
+
argv.push('--hook-script', hookScriptPath, '--hook-phase', 'upgrade', '--hook-command', 'app:upgrade', '--hook-env-name', runtime.envName, '--hook-app-path', runtime.env.appPath, '--hook-storage-path', runtime.env.storagePath);
|
|
355
|
+
}
|
|
348
356
|
return argv;
|
|
349
357
|
}
|
|
350
358
|
static buildDockerDownloadArgv(runtime, downloadVersion, options) {
|
|
@@ -497,6 +505,9 @@ export default class AppUpgrade extends Command {
|
|
|
497
505
|
await runWithSuppressedTargetEnvLog(async () => {
|
|
498
506
|
const startArgv = buildManagedActionArgv(runtime.envName, parsed, { quickstart: true });
|
|
499
507
|
startArgv.push('--no-sync-licensed-plugins');
|
|
508
|
+
if (runtime.env.config.hookScript) {
|
|
509
|
+
startArgv.push('--hook-command', 'app:upgrade');
|
|
510
|
+
}
|
|
500
511
|
await runWithSuppressedStartSuccessLog(async () => {
|
|
501
512
|
await runCommand('app:start', startArgv);
|
|
502
513
|
});
|
package/dist/commands/init.js
CHANGED
|
@@ -15,7 +15,7 @@ import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
|
15
15
|
import { getEnv, upsertEnv } from "../lib/auth-store.js";
|
|
16
16
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
17
17
|
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
18
|
-
import { resolveDefaultConfigScope } from '../lib/cli-home.js';
|
|
18
|
+
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
|
|
19
19
|
import { resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
|
|
20
20
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
21
21
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
@@ -25,6 +25,7 @@ import { installNocoBaseSkills, isNpmRegistryUnavailable } from '../lib/skills-m
|
|
|
25
25
|
import { omitKeys, pickKeys } from "../lib/object-utils.js";
|
|
26
26
|
import { ENV_CONFIG_SCHEMA_VERSION } from '../lib/env-config.js';
|
|
27
27
|
import { printInfo, printStage, printVerbose, printWarning } from '../lib/ui.js';
|
|
28
|
+
import { persistHookScript } from '../lib/hook-script.js';
|
|
28
29
|
import Download from "./download.js";
|
|
29
30
|
import EnvAdd from "./env/add.js";
|
|
30
31
|
import Install, { defaultDbPortForDialect } from "./install.js";
|
|
@@ -1029,6 +1030,14 @@ Prompt modes:
|
|
|
1029
1030
|
const skipDownload = results.skipDownload === true;
|
|
1030
1031
|
const appKey = resolveManagedAppKey(results.appKey ?? existingEnv?.config.appKey);
|
|
1031
1032
|
const timeZone = resolveManagedTimeZone(results.timeZone ?? existingEnv?.config.timezone);
|
|
1033
|
+
const hookScriptInput = String(flags['hook-script'] ?? '').trim();
|
|
1034
|
+
const hookAppPath = appPath || `./${envName}/`;
|
|
1035
|
+
const hookScript = hookScriptInput
|
|
1036
|
+
? await persistHookScript({
|
|
1037
|
+
sourcePath: hookScriptInput,
|
|
1038
|
+
appPath: resolveConfiguredEnvPath(hookAppPath) ?? resolveEnvRelativePath(hookAppPath),
|
|
1039
|
+
})
|
|
1040
|
+
: String(results.hookScript ?? '').trim();
|
|
1032
1041
|
const builtinDb = explicitDbHostFlag(flags)
|
|
1033
1042
|
? false
|
|
1034
1043
|
: results.builtinDb === undefined
|
|
@@ -1055,6 +1064,7 @@ Prompt modes:
|
|
|
1055
1064
|
...(dockerPlatform ? { dockerPlatform } : {}),
|
|
1056
1065
|
...(gitUrl ? { gitUrl } : {}),
|
|
1057
1066
|
...(npmRegistry ? { npmRegistry } : {}),
|
|
1067
|
+
...(hookScript ? { hookScript } : {}),
|
|
1058
1068
|
...(appPath ? { appPath } : {}),
|
|
1059
1069
|
...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
|
|
1060
1070
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
@@ -1130,6 +1140,10 @@ Prompt modes:
|
|
|
1130
1140
|
if (flags['prepare-only']) {
|
|
1131
1141
|
argv.push('--prepare-only');
|
|
1132
1142
|
}
|
|
1143
|
+
const hookScript = String(results.hookScript ?? flags['hook-script'] ?? '').trim();
|
|
1144
|
+
if (hookScript) {
|
|
1145
|
+
argv.push('--hook-script', hookScript);
|
|
1146
|
+
}
|
|
1133
1147
|
if (flags.verbose) {
|
|
1134
1148
|
argv.push('--verbose');
|
|
1135
1149
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -15,7 +15,7 @@ import { appendAppPublicPath } from '../lib/app-public-path.js';
|
|
|
15
15
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
16
16
|
import { applyCliLocale, localeText, resolveCliLocale, translateCli } from "../lib/cli-locale.js";
|
|
17
17
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
|
|
18
|
-
import { defaultDockerContainerPrefix, defaultDockerNetworkName } from '../lib/app-runtime.js';
|
|
18
|
+
import { defaultDockerContainerPrefix, defaultDockerNetworkName, managedAppLifecycleEnvVars, } from '../lib/app-runtime.js';
|
|
19
19
|
import { resolveDefaultApiHost, resolveDockerContainerPrefix, resolveDockerNetworkName } from '../lib/cli-config.js';
|
|
20
20
|
import { DEFAULT_DOCKER_VERSION, resolveDockerImageRef } from "../lib/docker-image.js";
|
|
21
21
|
import { findAvailableTcpPort, validateAppPublicPath, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
@@ -29,6 +29,7 @@ import { buildStoredEnvConfig } from '../lib/env-config.js';
|
|
|
29
29
|
import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
|
|
30
30
|
import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
31
31
|
import { buildInitAppEnvVarsFromConfig } from '../lib/managed-init-env.js';
|
|
32
|
+
import { buildHookContext, persistHookScript, resolveHookScriptPath, runHookScriptHook, } from '../lib/hook-script.js';
|
|
32
33
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
33
34
|
import Download, { defaultDockerRegistryForLang } from './download.js';
|
|
34
35
|
import EnvAdd from "./env/add.js";
|
|
@@ -472,7 +473,10 @@ export default class Install extends Command {
|
|
|
472
473
|
description: 'Skip the download step and reuse an existing local app directory or Docker image',
|
|
473
474
|
default: false,
|
|
474
475
|
}),
|
|
475
|
-
|
|
476
|
+
'hook-script': Flags.string({
|
|
477
|
+
description: 'Hook module copied to <appPath>/.nb/hooks.mjs and run before npm/git dependency installation.',
|
|
478
|
+
}),
|
|
479
|
+
...omitKeys(Download.flags, ['yes', 'hook-script']),
|
|
476
480
|
};
|
|
477
481
|
/** Environment name only: run before {@link Install.prompts} (see `run`). */
|
|
478
482
|
static envPrompts = {
|
|
@@ -1095,6 +1099,7 @@ export default class Install extends Command {
|
|
|
1095
1099
|
const appPort = Install.toOptionalPromptString(config.appPort);
|
|
1096
1100
|
const storagePath = Install.toOptionalPromptString(config.storagePath);
|
|
1097
1101
|
const appPublicPath = Install.toOptionalPromptString(config.appPublicPath);
|
|
1102
|
+
const hookScript = Install.toOptionalPromptString(config.hookScript);
|
|
1098
1103
|
const apiBaseUrl = Install.toOptionalPromptString(config.apiBaseUrl);
|
|
1099
1104
|
const downloadVersion = Install.toOptionalPromptString(config.downloadVersion);
|
|
1100
1105
|
const dockerRegistry = Install.toOptionalPromptString(config.dockerRegistry);
|
|
@@ -1125,6 +1130,7 @@ export default class Install extends Command {
|
|
|
1125
1130
|
...(appPort ? { appPort } : {}),
|
|
1126
1131
|
...(storagePath ? { storagePath } : {}),
|
|
1127
1132
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1133
|
+
...(hookScript ? { hookScript } : {}),
|
|
1128
1134
|
};
|
|
1129
1135
|
const downloadPreset = {
|
|
1130
1136
|
...(source ? { source } : {}),
|
|
@@ -1916,6 +1922,12 @@ export default class Install extends Command {
|
|
|
1916
1922
|
if (results.buildDts) {
|
|
1917
1923
|
argv.push('--build-dts');
|
|
1918
1924
|
}
|
|
1925
|
+
Install.pushDownloadArgIfValue(argv, '--hook-script', results.hookScript);
|
|
1926
|
+
Install.pushDownloadArgIfValue(argv, '--hook-phase', results.hookPhase);
|
|
1927
|
+
Install.pushDownloadArgIfValue(argv, '--hook-command', results.hookCommand);
|
|
1928
|
+
Install.pushDownloadArgIfValue(argv, '--hook-env-name', results.hookEnvName);
|
|
1929
|
+
Install.pushDownloadArgIfValue(argv, '--hook-app-path', results.hookAppPath);
|
|
1930
|
+
Install.pushDownloadArgIfValue(argv, '--hook-storage-path', results.hookStoragePath);
|
|
1919
1931
|
return argv;
|
|
1920
1932
|
}
|
|
1921
1933
|
static resolveLocalProjectRoot(params) {
|
|
@@ -1942,6 +1954,40 @@ export default class Install extends Command {
|
|
|
1942
1954
|
buildDts: false,
|
|
1943
1955
|
};
|
|
1944
1956
|
}
|
|
1957
|
+
static resolveAbsoluteAppPath(envName, appResults) {
|
|
1958
|
+
const configuredAppPath = resolveConfiguredAppPathValue(appResults) ?? defaultInstallAppPath(envName);
|
|
1959
|
+
return resolveConfiguredEnvPath(configuredAppPath) ?? resolveEnvRelativePath(defaultInstallAppPath(envName));
|
|
1960
|
+
}
|
|
1961
|
+
static resolveAbsoluteStoragePath(envName, appResults) {
|
|
1962
|
+
const configuredStoragePath = resolveConfiguredStoragePathValue(appResults, envName);
|
|
1963
|
+
return (resolveConfiguredEnvPath(configuredStoragePath) ?? resolveEnvRelativePath(defaultInstallStoragePath(envName)));
|
|
1964
|
+
}
|
|
1965
|
+
async prepareHookScriptForInstall(params) {
|
|
1966
|
+
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
1967
|
+
const storagePath = Install.resolveAbsoluteStoragePath(params.envName, params.appResults);
|
|
1968
|
+
const inputHookScript = Install.toOptionalPromptString(params.hookScript);
|
|
1969
|
+
if (inputHookScript) {
|
|
1970
|
+
params.appResults.hookScript = await persistHookScript({
|
|
1971
|
+
sourcePath: inputHookScript,
|
|
1972
|
+
appPath,
|
|
1973
|
+
});
|
|
1974
|
+
printInfo(`Saved hook script to ${path.join(appPath, String(params.appResults.hookScript))}.`);
|
|
1975
|
+
}
|
|
1976
|
+
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
1977
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
1978
|
+
appPath,
|
|
1979
|
+
hookScript: savedHookScript,
|
|
1980
|
+
});
|
|
1981
|
+
if (!hookScriptPath) {
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
params.downloadResults.hookScript = hookScriptPath;
|
|
1985
|
+
params.downloadResults.hookPhase = 'init';
|
|
1986
|
+
params.downloadResults.hookCommand = 'init';
|
|
1987
|
+
params.downloadResults.hookEnvName = params.envName;
|
|
1988
|
+
params.downloadResults.hookAppPath = appPath;
|
|
1989
|
+
params.downloadResults.hookStoragePath = storagePath;
|
|
1990
|
+
}
|
|
1945
1991
|
commandStdio(verbose) {
|
|
1946
1992
|
return verbose ? 'inherit' : 'ignore';
|
|
1947
1993
|
}
|
|
@@ -2011,7 +2057,9 @@ export default class Install extends Command {
|
|
|
2011
2057
|
const dbDialect = String(params.dbResults.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
2012
2058
|
const appKey = Install.resolveManagedAppKey(params.appResults.appKey);
|
|
2013
2059
|
const timeZone = Install.resolveManagedTimeZone(params.appResults.timeZone);
|
|
2060
|
+
const lifecycleEnvVars = managedAppLifecycleEnvVars();
|
|
2014
2061
|
const env = {
|
|
2062
|
+
...lifecycleEnvVars,
|
|
2015
2063
|
STORAGE_PATH: storagePath,
|
|
2016
2064
|
APP_PORT: String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT,
|
|
2017
2065
|
APP_KEY: appKey,
|
|
@@ -2216,6 +2264,44 @@ export default class Install extends Command {
|
|
|
2216
2264
|
}
|
|
2217
2265
|
await this.config.runCommand('env:update', [params.envName]);
|
|
2218
2266
|
}
|
|
2267
|
+
async runInstallHookIfNeeded(params) {
|
|
2268
|
+
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2269
|
+
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
2270
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
2271
|
+
appPath,
|
|
2272
|
+
hookScript: savedHookScript,
|
|
2273
|
+
});
|
|
2274
|
+
if (!hookScriptPath) {
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
const storagePath = Install.resolveAbsoluteStoragePath(params.envName, params.appResults);
|
|
2278
|
+
const sourcePath = params.projectRoot ??
|
|
2279
|
+
resolveConfiguredEnvPath(resolveConfiguredSourcePathValue(params.appResults, params.envName)) ??
|
|
2280
|
+
path.join(appPath, 'source');
|
|
2281
|
+
const context = buildHookContext({
|
|
2282
|
+
phase: 'init',
|
|
2283
|
+
command: 'init',
|
|
2284
|
+
envName: params.envName,
|
|
2285
|
+
source: params.source,
|
|
2286
|
+
version: downloadResultsValue(params.downloadResults, 'version'),
|
|
2287
|
+
appPath,
|
|
2288
|
+
sourcePath,
|
|
2289
|
+
storagePath,
|
|
2290
|
+
hookScript: savedHookScript,
|
|
2291
|
+
envConfig: Install.buildSavedEnvConfig(params, {
|
|
2292
|
+
defaultApiHost: params.defaultApiHost,
|
|
2293
|
+
}),
|
|
2294
|
+
});
|
|
2295
|
+
if (!context) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
this.log(`Running hook ${params.hookName}: ${hookScriptPath}`);
|
|
2299
|
+
await runHookScriptHook({
|
|
2300
|
+
hookScriptPath,
|
|
2301
|
+
hookName: params.hookName,
|
|
2302
|
+
context,
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2219
2305
|
static buildSavedEnvConfig(params, options = {}) {
|
|
2220
2306
|
const appPath = resolveConfiguredAppPathValue(params.appResults);
|
|
2221
2307
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
@@ -2247,6 +2333,7 @@ export default class Install extends Command {
|
|
|
2247
2333
|
devDependencies: downloadResultsValue(params.downloadResults, 'devDependencies'),
|
|
2248
2334
|
build: downloadResultsValue(params.downloadResults, 'build'),
|
|
2249
2335
|
buildDts: downloadResultsValue(params.downloadResults, 'buildDts'),
|
|
2336
|
+
hookScript: params.appResults.hookScript,
|
|
2250
2337
|
...(appPath ? { appPath } : {}),
|
|
2251
2338
|
...(appRootPath && !areConfiguredPathsEquivalent(appRootPath, derivedAppRootPath) ? { appRootPath } : {}),
|
|
2252
2339
|
appPort,
|
|
@@ -2311,6 +2398,9 @@ export default class Install extends Command {
|
|
|
2311
2398
|
yesInitialValues: { resume: parsed.resume },
|
|
2312
2399
|
yes,
|
|
2313
2400
|
});
|
|
2401
|
+
if (resumePreset?.appPreset?.hookScript !== undefined && appResults.hookScript === undefined) {
|
|
2402
|
+
appResults.hookScript = resumePreset.appPreset.hookScript;
|
|
2403
|
+
}
|
|
2314
2404
|
const downloadOpts = Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2315
2405
|
downloadOpts.values = {
|
|
2316
2406
|
...(resumePreset?.downloadPreset ?? {}),
|
|
@@ -2440,6 +2530,12 @@ export default class Install extends Command {
|
|
|
2440
2530
|
appResults,
|
|
2441
2531
|
});
|
|
2442
2532
|
appResults.setupState = 'prepared';
|
|
2533
|
+
await this.prepareHookScriptForInstall({
|
|
2534
|
+
envName,
|
|
2535
|
+
appResults,
|
|
2536
|
+
downloadResults,
|
|
2537
|
+
hookScript: parsed['hook-script'],
|
|
2538
|
+
});
|
|
2443
2539
|
const source = String(downloadResultsValue(downloadResults, 'source') ?? '').trim();
|
|
2444
2540
|
const usesDockerResources = Boolean(dbResults.builtinDb) || source === 'docker';
|
|
2445
2541
|
const dockerNetworkName = usesDockerResources
|
|
@@ -2505,6 +2601,17 @@ export default class Install extends Command {
|
|
|
2505
2601
|
printInfo('Application image ready.');
|
|
2506
2602
|
}
|
|
2507
2603
|
if (!parsed['prepare-only']) {
|
|
2604
|
+
await this.runInstallHookIfNeeded({
|
|
2605
|
+
hookName: 'beforeAppInstall',
|
|
2606
|
+
envName,
|
|
2607
|
+
source,
|
|
2608
|
+
appResults,
|
|
2609
|
+
downloadResults,
|
|
2610
|
+
dbResults,
|
|
2611
|
+
rootResults,
|
|
2612
|
+
envAddResults,
|
|
2613
|
+
defaultApiHost,
|
|
2614
|
+
});
|
|
2508
2615
|
dockerAppPlan = await this.installDockerApp({
|
|
2509
2616
|
envName,
|
|
2510
2617
|
dockerNetworkName,
|
|
@@ -2523,7 +2630,7 @@ export default class Install extends Command {
|
|
|
2523
2630
|
}
|
|
2524
2631
|
else if (source === 'npm' || source === 'git') {
|
|
2525
2632
|
const localSource = source === 'npm' ? 'npm' : 'git';
|
|
2526
|
-
const projectRoot = parsed['skip-download']
|
|
2633
|
+
const projectRoot = parsed['skip-download'] || parsed['prepare-only']
|
|
2527
2634
|
? Install.resolveLocalProjectRoot({
|
|
2528
2635
|
envName,
|
|
2529
2636
|
appResults,
|
|
@@ -2535,10 +2642,22 @@ export default class Install extends Command {
|
|
|
2535
2642
|
downloadResults,
|
|
2536
2643
|
verbose: parsed.verbose,
|
|
2537
2644
|
});
|
|
2538
|
-
if (!parsed['skip-download']) {
|
|
2645
|
+
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2539
2646
|
printInfo('Application files ready.');
|
|
2540
2647
|
}
|
|
2541
2648
|
if (!parsed['prepare-only']) {
|
|
2649
|
+
await this.runInstallHookIfNeeded({
|
|
2650
|
+
hookName: 'beforeAppInstall',
|
|
2651
|
+
envName,
|
|
2652
|
+
source,
|
|
2653
|
+
appResults,
|
|
2654
|
+
downloadResults,
|
|
2655
|
+
dbResults,
|
|
2656
|
+
rootResults,
|
|
2657
|
+
envAddResults,
|
|
2658
|
+
projectRoot,
|
|
2659
|
+
defaultApiHost,
|
|
2660
|
+
});
|
|
2542
2661
|
localAppPlan = await this.startLocalApp({
|
|
2543
2662
|
envName,
|
|
2544
2663
|
source: localSource,
|
|
@@ -2573,6 +2692,18 @@ export default class Install extends Command {
|
|
|
2573
2692
|
});
|
|
2574
2693
|
printInfo(`NocoBase is ready at ${formatInstallDisplayUrl(displayApiBaseUrl)}`);
|
|
2575
2694
|
appResults.setupState = 'installed';
|
|
2695
|
+
await this.runInstallHookIfNeeded({
|
|
2696
|
+
hookName: 'afterAppStart',
|
|
2697
|
+
envName,
|
|
2698
|
+
source,
|
|
2699
|
+
appResults,
|
|
2700
|
+
downloadResults,
|
|
2701
|
+
dbResults,
|
|
2702
|
+
rootResults,
|
|
2703
|
+
envAddResults,
|
|
2704
|
+
projectRoot: localAppPlan?.projectRoot,
|
|
2705
|
+
defaultApiHost,
|
|
2706
|
+
});
|
|
2576
2707
|
}
|
|
2577
2708
|
if (dockerAppPlan || localAppPlan || builtinDbPlan) {
|
|
2578
2709
|
await this.saveInstalledEnv({
|
|
@@ -26,7 +26,7 @@ function formatSkillsUpdateMessage(result, verbose) {
|
|
|
26
26
|
}
|
|
27
27
|
export default class SelfUpdate extends Command {
|
|
28
28
|
static summary = 'Update the globally installed NocoBase CLI';
|
|
29
|
-
static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm install.';
|
|
29
|
+
static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm, pnpm, or yarn install.';
|
|
30
30
|
static examples = [
|
|
31
31
|
'<%= config.bin %> <%= command.id %>',
|
|
32
32
|
'<%= config.bin %> <%= command.id %> --yes',
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
11
|
-
import { ensureLocalPostinstall } from '../../lib/app-managed-resources.js';
|
|
11
|
+
import { ensureLocalPostinstall, ensureNpmSourceDevDependencies } from '../../lib/app-managed-resources.js';
|
|
12
12
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
|
|
13
|
+
import { findAvailableTcpPort } from '../../lib/prompt-validators.js';
|
|
13
14
|
import { announceTargetEnv, failTask, printInfo, startTask, succeedTask } from '../../lib/ui.js';
|
|
14
15
|
function formatUnsupportedRuntimeMessage(kind, envName) {
|
|
15
16
|
if (kind === 'docker') {
|
|
@@ -129,10 +130,7 @@ export default class SourceDev extends Command {
|
|
|
129
130
|
this.error(formatUnsupportedRuntimeMessage(runtime.kind, runtime.envName));
|
|
130
131
|
}
|
|
131
132
|
announceTargetEnv(runtime.envName);
|
|
132
|
-
const devPort = flags.port ||
|
|
133
|
-
(runtime.env.appPort !== undefined && runtime.env.appPort !== null
|
|
134
|
-
? String(runtime.env.appPort).trim()
|
|
135
|
-
: undefined);
|
|
133
|
+
const devPort = flags.port?.trim() || (await findAvailableTcpPort());
|
|
136
134
|
const appUrl = appUrlForPort(devPort);
|
|
137
135
|
if (await isAppAlreadyRunning(appUrl)) {
|
|
138
136
|
this.error([
|
|
@@ -160,6 +158,12 @@ export default class SourceDev extends Command {
|
|
|
160
158
|
}
|
|
161
159
|
printInfo(`Starting NocoBase dev mode for "${runtime.envName}" from ${runtime.projectRoot}. Press Ctrl+C to stop.`);
|
|
162
160
|
try {
|
|
161
|
+
await ensureNpmSourceDevDependencies(runtime, {
|
|
162
|
+
onStartTask: startTask,
|
|
163
|
+
onSucceedTask: succeedTask,
|
|
164
|
+
onFailTask: failTask,
|
|
165
|
+
verbose: true,
|
|
166
|
+
});
|
|
163
167
|
await ensureLocalPostinstall(runtime, {
|
|
164
168
|
onStartTask: startTask,
|
|
165
169
|
onSucceedTask: succeedTask,
|