@nocobase/cli 2.1.0-beta.34 → 2.1.0-beta.36
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/bin/run.js +2 -1
- package/bin/session-env.js +12 -0
- package/dist/commands/app/start.js +12 -1
- package/dist/commands/app/upgrade.js +45 -16
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/dist/commands/env/add.js +63 -9
- package/dist/commands/env/auth.js +85 -11
- package/dist/commands/init.js +71 -13
- package/dist/commands/install.js +140 -11
- package/dist/commands/license/activate.js +6 -4
- package/dist/commands/source/dev.js +8 -1
- package/dist/commands/source/publish.js +109 -0
- package/dist/commands/source/registry/logs.js +70 -0
- package/dist/commands/source/registry/start.js +57 -0
- package/dist/commands/source/registry/status.js +33 -0
- package/dist/commands/source/registry/stop.js +48 -0
- package/dist/commands/v1.js +210 -0
- package/dist/lib/app-managed-resources.js +42 -2
- package/dist/lib/app-runtime.js +13 -4
- package/dist/lib/auth-store.js +28 -5
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +23 -13
- package/dist/lib/env-config.js +6 -0
- package/dist/lib/run-npm.js +35 -10
- package/dist/lib/source-publish.js +306 -0
- package/dist/lib/source-registry.js +188 -0
- package/package.json +6 -3
package/dist/lib/auth-store.js
CHANGED
|
@@ -159,6 +159,9 @@ export class Env {
|
|
|
159
159
|
get auth() {
|
|
160
160
|
return this.config.auth;
|
|
161
161
|
}
|
|
162
|
+
get authType() {
|
|
163
|
+
return resolveConfiguredAuthType(this.config);
|
|
164
|
+
}
|
|
162
165
|
get runtime() {
|
|
163
166
|
return this.config.runtime;
|
|
164
167
|
}
|
|
@@ -210,6 +213,9 @@ export class Env {
|
|
|
210
213
|
put('DB_DATABASE', this.config.dbDatabase);
|
|
211
214
|
put('DB_USER', this.config.dbUser);
|
|
212
215
|
put('DB_PASSWORD', this.config.dbPassword);
|
|
216
|
+
put('DB_SCHEMA', this.config.dbSchema);
|
|
217
|
+
put('DB_TABLE_PREFIX', this.config.dbTablePrefix);
|
|
218
|
+
put('DB_UNDERSCORED', this.config.dbUnderscored);
|
|
213
219
|
return out;
|
|
214
220
|
}
|
|
215
221
|
}
|
|
@@ -253,27 +259,38 @@ async function writeEnv(envName, updater, options = {}) {
|
|
|
253
259
|
config.envs[envName] = updater(previous);
|
|
254
260
|
await saveAuthConfig(config, options);
|
|
255
261
|
}
|
|
262
|
+
function normalizeConfiguredAuthType(value) {
|
|
263
|
+
return value === 'token' || value === 'oauth' ? value : undefined;
|
|
264
|
+
}
|
|
265
|
+
export function resolveConfiguredAuthType(config) {
|
|
266
|
+
return normalizeConfiguredAuthType(config?.authType) ?? normalizeConfiguredAuthType(config?.auth?.type);
|
|
267
|
+
}
|
|
256
268
|
export async function upsertEnv(envName, config, options = {}) {
|
|
257
269
|
await writeEnv(envName, (previous) => {
|
|
258
|
-
const { apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, accessToken, ...rest } = config;
|
|
270
|
+
const { apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, accessToken, authType, ...rest } = config;
|
|
259
271
|
const nextApiBaseUrl = readEnvApiBaseUrl(config);
|
|
260
272
|
const previousApiBaseUrl = readEnvApiBaseUrl(previous);
|
|
261
273
|
const baseUrlChanged = previousApiBaseUrl !== nextApiBaseUrl;
|
|
274
|
+
const previousAuthType = resolveConfiguredAuthType(previous);
|
|
275
|
+
const requestedAuthType = normalizeConfiguredAuthType(authType);
|
|
276
|
+
const nextAuthType = requestedAuthType ?? (accessToken ? 'token' : previousAuthType);
|
|
262
277
|
const nextAuth = accessToken
|
|
263
278
|
? {
|
|
264
279
|
type: 'token',
|
|
265
280
|
accessToken,
|
|
266
281
|
}
|
|
267
|
-
: baseUrlChanged || previous?.auth?.type === 'token'
|
|
282
|
+
: nextAuthType === 'token' || baseUrlChanged || previous?.auth?.type === 'token'
|
|
268
283
|
? undefined
|
|
269
284
|
: previous?.auth;
|
|
270
285
|
const authChanged = !areAuthConfigsEquivalent(previous?.auth, nextAuth);
|
|
286
|
+
const authTypeChanged = previousAuthType !== nextAuthType;
|
|
271
287
|
return {
|
|
272
288
|
...previous,
|
|
273
289
|
apiBaseUrl: nextApiBaseUrl,
|
|
290
|
+
authType: nextAuthType,
|
|
274
291
|
auth: nextAuth,
|
|
275
292
|
...rest,
|
|
276
|
-
runtime: baseUrlChanged || authChanged ? undefined : previous?.runtime,
|
|
293
|
+
runtime: baseUrlChanged || authChanged || authTypeChanged ? undefined : previous?.runtime,
|
|
277
294
|
};
|
|
278
295
|
}, options);
|
|
279
296
|
}
|
|
@@ -282,26 +299,32 @@ export async function updateEnvConnection(envName, updates, options = {}) {
|
|
|
282
299
|
const nextApiBaseUrl = readEnvApiBaseUrl(updates) ?? readEnvApiBaseUrl(previous);
|
|
283
300
|
const previousApiBaseUrl = readEnvApiBaseUrl(previous);
|
|
284
301
|
const baseUrlChanged = previousApiBaseUrl !== nextApiBaseUrl;
|
|
302
|
+
const previousAuthType = resolveConfiguredAuthType(previous);
|
|
303
|
+
const requestedAuthType = normalizeConfiguredAuthType(updates.authType);
|
|
304
|
+
const nextAuthType = requestedAuthType ?? (updates.accessToken ? 'token' : previousAuthType);
|
|
285
305
|
const nextAuth = updates.accessToken
|
|
286
306
|
? {
|
|
287
307
|
type: 'token',
|
|
288
308
|
accessToken: updates.accessToken,
|
|
289
309
|
}
|
|
290
|
-
: baseUrlChanged || previous?.auth?.type === 'token'
|
|
310
|
+
: nextAuthType === 'token' || baseUrlChanged || previous?.auth?.type === 'token'
|
|
291
311
|
? undefined
|
|
292
312
|
: previous?.auth;
|
|
293
313
|
const authChanged = !areAuthConfigsEquivalent(previous?.auth, nextAuth);
|
|
314
|
+
const authTypeChanged = previousAuthType !== nextAuthType;
|
|
294
315
|
return {
|
|
295
316
|
...previous,
|
|
296
317
|
...(nextApiBaseUrl !== undefined ? { apiBaseUrl: nextApiBaseUrl } : {}),
|
|
318
|
+
authType: nextAuthType,
|
|
297
319
|
auth: nextAuth,
|
|
298
|
-
runtime: baseUrlChanged || authChanged ? undefined : previous?.runtime,
|
|
320
|
+
runtime: baseUrlChanged || authChanged || authTypeChanged ? undefined : previous?.runtime,
|
|
299
321
|
};
|
|
300
322
|
}, options);
|
|
301
323
|
}
|
|
302
324
|
export async function setEnvOauthSession(envName, auth, options = {}) {
|
|
303
325
|
await writeEnv(envName, (previous) => ({
|
|
304
326
|
...previous,
|
|
327
|
+
authType: 'oauth',
|
|
305
328
|
auth,
|
|
306
329
|
runtime: options.preserveRuntime ? previous?.runtime : undefined,
|
|
307
330
|
}), options);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { promises as fs } from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { getCurrentEnvName, getEnv, listEnvs } from './auth-store.js';
|
|
13
|
+
import { updateEnvRuntime } from './bootstrap.js';
|
|
14
|
+
import { resolveDefaultConfigScope } from './cli-home.js';
|
|
15
|
+
import { commandOutput } from './run-npm.js';
|
|
16
|
+
import { loadRuntimeSync } from './runtime-store.js';
|
|
17
|
+
import { failTask, startTask, succeedTask } from './ui.js';
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const CLI_PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
20
|
+
const CLI_CONFIG_FILE = path.join(CLI_PACKAGE_ROOT, 'nocobase-ctl.config.json');
|
|
21
|
+
const CLI_ENTRY_FILE = path.join(CLI_PACKAGE_ROOT, 'bin', 'run.js');
|
|
22
|
+
export const BACKUP_POLL_INTERVAL_MS = 2_000;
|
|
23
|
+
export const BACKUP_CREATE_TIMEOUT_MS = 600_000;
|
|
24
|
+
export const BACKUP_RUNTIME_COMMANDS = {
|
|
25
|
+
create: 'backup create',
|
|
26
|
+
status: 'backup status',
|
|
27
|
+
download: 'backup download',
|
|
28
|
+
restoreUpload: 'backup restore-upload',
|
|
29
|
+
};
|
|
30
|
+
function hasRequiredBackupCommands(runtime, commandIds) {
|
|
31
|
+
if (!runtime) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const available = new Set(runtime.commands.map((command) => command.commandId));
|
|
35
|
+
return commandIds.every((commandId) => available.has(commandId));
|
|
36
|
+
}
|
|
37
|
+
function formatMissingBackupRuntimeCommands(envName, commandIds) {
|
|
38
|
+
const missing = commandIds.map((commandId) => `nb api ${commandId}`).join(', ');
|
|
39
|
+
return [
|
|
40
|
+
`The selected env "${envName}" does not expose the backup API commands required by \`nb backup\`.`,
|
|
41
|
+
`Missing commands: ${missing}`,
|
|
42
|
+
'Enable or upgrade the backup/restore capability for that env, then try again.',
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
export function buildBackupEnvArgv(options) {
|
|
46
|
+
const argv = [];
|
|
47
|
+
if (options.explicitEnvSelection && options.requestedEnv) {
|
|
48
|
+
argv.push('--env', options.requestedEnv);
|
|
49
|
+
}
|
|
50
|
+
if (options.yes || options.explicitEnvSelection) {
|
|
51
|
+
argv.push('--yes');
|
|
52
|
+
}
|
|
53
|
+
return argv;
|
|
54
|
+
}
|
|
55
|
+
export async function resolveBackupTargetEnv(requestedEnv) {
|
|
56
|
+
const scope = resolveDefaultConfigScope();
|
|
57
|
+
const envName = requestedEnv?.trim() || (await getCurrentEnvName({ scope }));
|
|
58
|
+
const env = await getEnv(envName, { scope });
|
|
59
|
+
if (env) {
|
|
60
|
+
return { scope, envName, env };
|
|
61
|
+
}
|
|
62
|
+
const { envs } = await listEnvs({ scope });
|
|
63
|
+
const configuredEnvNames = Object.keys(envs);
|
|
64
|
+
if (!configuredEnvNames.length) {
|
|
65
|
+
throw new Error('No env is configured. Run `nb env add <name> --api-base-url <url>` first.');
|
|
66
|
+
}
|
|
67
|
+
if (requestedEnv?.trim()) {
|
|
68
|
+
throw new Error(`Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`);
|
|
69
|
+
}
|
|
70
|
+
throw new Error([
|
|
71
|
+
`Current env "${envName}" is not configured.`,
|
|
72
|
+
'Switch to an existing env with `nb env use <name>`, or add one with `nb env add <name> --api-base-url <url>`.',
|
|
73
|
+
].join('\n'));
|
|
74
|
+
}
|
|
75
|
+
export async function ensureBackupRuntimeCommands(params) {
|
|
76
|
+
const scope = resolveDefaultConfigScope();
|
|
77
|
+
const env = params.env ?? (await getEnv(params.envName, { scope }));
|
|
78
|
+
const currentRuntime = loadRuntimeSync(env?.runtime?.version, { scope });
|
|
79
|
+
if (hasRequiredBackupCommands(currentRuntime, params.commandIds)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!params.quiet) {
|
|
83
|
+
startTask(`Refreshing env runtime for "${params.envName}" to load backup commands...`);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const runtime = await updateEnvRuntime({
|
|
87
|
+
envName: params.envName,
|
|
88
|
+
scope,
|
|
89
|
+
configFile: CLI_CONFIG_FILE,
|
|
90
|
+
quiet: params.quiet,
|
|
91
|
+
});
|
|
92
|
+
if (!hasRequiredBackupCommands(runtime, params.commandIds)) {
|
|
93
|
+
throw new Error(formatMissingBackupRuntimeCommands(params.envName, params.commandIds));
|
|
94
|
+
}
|
|
95
|
+
if (!params.quiet) {
|
|
96
|
+
succeedTask(`Env runtime is ready for backup commands in "${params.envName}".`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (!params.quiet) {
|
|
101
|
+
failTask(`Failed to refresh backup commands for "${params.envName}".`);
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export async function runBackupCliCommand(argv, options) {
|
|
107
|
+
return await commandOutput(process.execPath, [CLI_ENTRY_FILE, ...argv], {
|
|
108
|
+
errorName: options?.errorName ?? `nb ${argv.join(' ')}`,
|
|
109
|
+
env: {
|
|
110
|
+
NB_SKIP_STARTUP_UPDATE: '1',
|
|
111
|
+
// When the parent CLI already runs in tsx source mode, it sets
|
|
112
|
+
// `_NOCO_CLI_TSX_CHILD=1`. Clear it here so `bin/run.js` can re-exec
|
|
113
|
+
// itself with `--import tsx` again instead of trying to import `.ts`
|
|
114
|
+
// sources without the loader.
|
|
115
|
+
_NOCO_CLI_TSX_CHILD: '',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export async function runBackupCliJsonCommand(argv, options) {
|
|
120
|
+
const output = await runBackupCliCommand([...argv, '--json-output'], options);
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(output);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new Error(`Unexpected JSON output from ${options?.errorName ?? `nb ${argv.join(' ')}`}: ${output || '(empty output)'}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export async function resolveBackupCreateOutputPath(output, remoteName) {
|
|
129
|
+
const requestedOutput = String(output ?? '').trim();
|
|
130
|
+
if (!requestedOutput) {
|
|
131
|
+
return path.resolve(process.cwd(), remoteName);
|
|
132
|
+
}
|
|
133
|
+
const resolvedOutput = path.resolve(process.cwd(), requestedOutput);
|
|
134
|
+
try {
|
|
135
|
+
const stats = await fs.stat(resolvedOutput);
|
|
136
|
+
if (stats.isDirectory()) {
|
|
137
|
+
return path.join(resolvedOutput, remoteName);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Treat non-existing paths as an explicit target file path.
|
|
142
|
+
}
|
|
143
|
+
return resolvedOutput;
|
|
144
|
+
}
|
|
145
|
+
export async function resolveBackupRestoreFilePath(file) {
|
|
146
|
+
const resolvedFile = path.resolve(process.cwd(), file);
|
|
147
|
+
let stats;
|
|
148
|
+
try {
|
|
149
|
+
stats = await fs.stat(resolvedFile);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
throw new Error(`Backup file not found: ${resolvedFile}`);
|
|
153
|
+
}
|
|
154
|
+
if (!stats.isFile()) {
|
|
155
|
+
throw new Error(`Backup restore input must be a file: ${resolvedFile}`);
|
|
156
|
+
}
|
|
157
|
+
return resolvedFile;
|
|
158
|
+
}
|
|
159
|
+
export function resolveBackupWaitApiBaseUrl(env) {
|
|
160
|
+
const baseUrl = String(env.baseUrl ?? '').trim();
|
|
161
|
+
if (baseUrl) {
|
|
162
|
+
return baseUrl.replace(/\/+$/, '');
|
|
163
|
+
}
|
|
164
|
+
const appPort = env.appPort === undefined || env.appPort === null
|
|
165
|
+
? ''
|
|
166
|
+
: String(env.appPort).trim();
|
|
167
|
+
return appPort ? `http://127.0.0.1:${appPort}/api` : undefined;
|
|
168
|
+
}
|
|
169
|
+
export async function sleep(ms) {
|
|
170
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
171
|
+
}
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -137,7 +137,7 @@ function getSwaggerUrl(baseUrl) {
|
|
|
137
137
|
function getHealthCheckUrl(baseUrl) {
|
|
138
138
|
return `${baseUrl.replace(/\/+$/, '')}/__health_check`;
|
|
139
139
|
}
|
|
140
|
-
async function waitForServiceReady(baseUrl, token, role) {
|
|
140
|
+
async function waitForServiceReady(baseUrl, token, role, options) {
|
|
141
141
|
const healthCheckUrl = getHealthCheckUrl(baseUrl);
|
|
142
142
|
const startedAt = Date.now();
|
|
143
143
|
let notified = false;
|
|
@@ -162,18 +162,22 @@ async function waitForServiceReady(baseUrl, token, role) {
|
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
if (!notified) {
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
if (!options?.quiet) {
|
|
166
|
+
printVerbose(`Waiting for health check: ${healthCheckUrl}`);
|
|
167
|
+
updateTask(`Waiting for application readiness (${healthCheckUrl})`);
|
|
168
|
+
}
|
|
167
169
|
notified = true;
|
|
168
170
|
}
|
|
169
171
|
await sleep(APP_RETRY_INTERVAL);
|
|
170
172
|
}
|
|
171
173
|
throw new Error(`The application did not become ready in time. Expected \`${healthCheckUrl}\` to respond with \`ok\`.`);
|
|
172
174
|
}
|
|
173
|
-
async function waitForSwaggerSchema(baseUrl, token, role) {
|
|
175
|
+
async function waitForSwaggerSchema(baseUrl, token, role, options) {
|
|
174
176
|
const swaggerUrl = getSwaggerUrl(baseUrl);
|
|
175
177
|
const startedAt = Date.now();
|
|
176
|
-
|
|
178
|
+
if (!options?.quiet) {
|
|
179
|
+
printVerbose(`Checking swagger schema: ${swaggerUrl}`);
|
|
180
|
+
}
|
|
177
181
|
while (Date.now() - startedAt < APP_RETRY_TIMEOUT) {
|
|
178
182
|
const response = await requestJson(swaggerUrl, { token, role });
|
|
179
183
|
if (response.ok) {
|
|
@@ -182,7 +186,7 @@ async function waitForSwaggerSchema(baseUrl, token, role) {
|
|
|
182
186
|
if (!shouldRetryAppAvailability(response)) {
|
|
183
187
|
return response;
|
|
184
188
|
}
|
|
185
|
-
await waitForServiceReady(baseUrl, token, role);
|
|
189
|
+
await waitForServiceReady(baseUrl, token, role, options);
|
|
186
190
|
}
|
|
187
191
|
return await requestJson(swaggerUrl, { token, role });
|
|
188
192
|
}
|
|
@@ -200,9 +204,9 @@ async function confirmEnableApiDoc() {
|
|
|
200
204
|
async function fetchSwaggerSchema(baseUrl, token, role, context = {}, options = {}) {
|
|
201
205
|
let response = options.retryAppAvailability === false
|
|
202
206
|
? await requestJson(getSwaggerUrl(baseUrl), { token, role })
|
|
203
|
-
: await waitForSwaggerSchema(baseUrl, token, role);
|
|
207
|
+
: await waitForSwaggerSchema(baseUrl, token, role, { quiet: options.quiet });
|
|
204
208
|
if (response.status === 404) {
|
|
205
|
-
if (options.allowEnableApiDoc === false) {
|
|
209
|
+
if (options.allowEnableApiDoc === false || options.quiet) {
|
|
206
210
|
throw new Error('`swagger:get` returned 404. Check the base URL and enable the `API documentation plugin` if needed.');
|
|
207
211
|
}
|
|
208
212
|
printInfo('The API documentation plugin is not enabled.');
|
|
@@ -257,7 +261,7 @@ export function formatSwaggerSchemaError(response, context) {
|
|
|
257
261
|
`Authentication failed while loading the command runtime from \`swagger:get\`${envLabel}.`,
|
|
258
262
|
`Base URL: ${context.baseUrl}`,
|
|
259
263
|
details,
|
|
260
|
-
'Update the API key with `nb env add <name> --api-base-url <url> --auth-type token --token <api-key>`, log in with `nb env auth <name>`, or rerun the command with `--token <api-key>`.',
|
|
264
|
+
'Update the API key with `nb env add <name> --api-base-url <url> --auth-type token --access-token <api-key>`, log in with `nb env auth <name>`, or rerun the command with `--access-token <api-key>`.',
|
|
261
265
|
commandHint,
|
|
262
266
|
].join('\n');
|
|
263
267
|
}
|
|
@@ -368,10 +372,14 @@ export async function updateEnvRuntime(options) {
|
|
|
368
372
|
.filter(Boolean)
|
|
369
373
|
.join('\n'));
|
|
370
374
|
}
|
|
371
|
-
|
|
375
|
+
if (!options.quiet) {
|
|
376
|
+
updateTask('Loading command runtime...');
|
|
377
|
+
}
|
|
372
378
|
try {
|
|
373
|
-
|
|
374
|
-
|
|
379
|
+
if (!options.quiet) {
|
|
380
|
+
printVerbose(`Runtime source: ${baseUrl}`);
|
|
381
|
+
}
|
|
382
|
+
const document = await fetchSwaggerSchema(baseUrl, token, options.role, { envName }, { quiet: options.quiet });
|
|
375
383
|
const runtime = await generateRuntime(document, options.configFile, baseUrl);
|
|
376
384
|
await saveRuntime(runtime, { scope: options.scope });
|
|
377
385
|
if (options.baseUrl !== undefined || options.token !== undefined) {
|
|
@@ -388,6 +396,8 @@ export async function updateEnvRuntime(options) {
|
|
|
388
396
|
return runtime;
|
|
389
397
|
}
|
|
390
398
|
finally {
|
|
391
|
-
|
|
399
|
+
if (!options.quiet) {
|
|
400
|
+
stopTask();
|
|
401
|
+
}
|
|
392
402
|
}
|
|
393
403
|
}
|
package/dist/lib/env-config.js
CHANGED
|
@@ -26,6 +26,8 @@ const STRING_ENV_CONFIG_KEYS = [
|
|
|
26
26
|
'dbDatabase',
|
|
27
27
|
'dbUser',
|
|
28
28
|
'dbPassword',
|
|
29
|
+
'dbSchema',
|
|
30
|
+
'dbTablePrefix',
|
|
29
31
|
'rootUsername',
|
|
30
32
|
'rootEmail',
|
|
31
33
|
'rootPassword',
|
|
@@ -36,6 +38,7 @@ const BOOLEAN_ENV_CONFIG_KEYS = [
|
|
|
36
38
|
'devDependencies',
|
|
37
39
|
'build',
|
|
38
40
|
'buildDts',
|
|
41
|
+
'dbUnderscored',
|
|
39
42
|
];
|
|
40
43
|
function trimConfigValue(value) {
|
|
41
44
|
const text = String(value ?? '').trim();
|
|
@@ -80,6 +83,9 @@ export function buildStoredEnvConfig(input) {
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
const authType = trimConfigValue(input.authType);
|
|
86
|
+
if (authType === 'token' || authType === 'oauth') {
|
|
87
|
+
envConfig.authType = authType;
|
|
88
|
+
}
|
|
83
89
|
const accessToken = trimConfigValue(input.accessToken);
|
|
84
90
|
if (authType === 'token' && accessToken) {
|
|
85
91
|
envConfig.accessToken = accessToken;
|
package/dist/lib/run-npm.js
CHANGED
|
@@ -31,6 +31,14 @@ function pathExists(candidate) {
|
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
function isDirectory(candidate) {
|
|
35
|
+
try {
|
|
36
|
+
return Boolean(candidate) && fs.statSync(candidate).isDirectory();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
34
42
|
function hasLocalNocoBaseBinary(candidate) {
|
|
35
43
|
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1'))
|
|
36
44
|
|| pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1.cmd')));
|
|
@@ -44,21 +52,24 @@ export function resolveCwd(cwd) {
|
|
|
44
52
|
}
|
|
45
53
|
export function resolveProjectCwd(cwd) {
|
|
46
54
|
const normalizedCwd = typeof cwd === 'string' && cwd.trim() === '' ? undefined : cwd;
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
55
|
+
const fallback = resolveCwd(normalizedCwd);
|
|
56
|
+
const hasExplicitInput = normalizedCwd !== undefined;
|
|
57
|
+
if (hasExplicitInput && !pathExists(fallback)) {
|
|
58
|
+
throw new Error(`The specified --cwd does not exist: ${fallback}`);
|
|
51
59
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
if (hasExplicitInput && !isDirectory(fallback)) {
|
|
61
|
+
throw new Error(`The specified --cwd is not a directory: ${fallback}`);
|
|
62
|
+
}
|
|
63
|
+
let current = hasExplicitInput ? fallback : process.cwd();
|
|
55
64
|
while (true) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return candidate;
|
|
65
|
+
if (hasLocalNocoBaseBinary(current)) {
|
|
66
|
+
return current;
|
|
59
67
|
}
|
|
60
68
|
const parent = path.dirname(current);
|
|
61
69
|
if (parent === current) {
|
|
70
|
+
if (hasExplicitInput) {
|
|
71
|
+
throw new Error(`Couldn't find a NocoBase source project from --cwd: ${fallback}`);
|
|
72
|
+
}
|
|
62
73
|
return fallback;
|
|
63
74
|
}
|
|
64
75
|
current = parent;
|
|
@@ -78,6 +89,20 @@ export function run(name, args, options) {
|
|
|
78
89
|
},
|
|
79
90
|
windowsHide: process.platform === 'win32',
|
|
80
91
|
});
|
|
92
|
+
if (options?.stdio === 'pipe') {
|
|
93
|
+
child.stdout?.setEncoding('utf8');
|
|
94
|
+
child.stderr?.setEncoding('utf8');
|
|
95
|
+
if (options.onStdout) {
|
|
96
|
+
child.stdout?.on('data', (chunk) => {
|
|
97
|
+
options.onStdout?.(String(chunk));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (options.onStderr) {
|
|
101
|
+
child.stderr?.on('data', (chunk) => {
|
|
102
|
+
options.onStderr?.(String(chunk));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
81
106
|
const cleanupSignalForwarding = forwardSignalsToChild(child);
|
|
82
107
|
child.once('error', (error) => {
|
|
83
108
|
cleanupSignalForwarding();
|