@nocobase/cli 3.0.0-alpha.4 → 3.0.0-alpha.5
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/portal/create.js +4 -1
- package/dist/commands/portal/dev.js +2 -0
- package/dist/commands/portal/pull.js +4 -1
- package/dist/lib/portal-build-html.js +27 -0
- package/dist/lib/portal-config.js +6 -3
- package/dist/lib/portal-configure.js +4 -3
- package/dist/lib/portal-create.js +75 -9
- package/dist/lib/portal-deploy.js +13 -8
- package/dist/lib/portal-destroy.js +9 -13
- package/dist/lib/portal-dev.js +3 -3
- package/dist/lib/portal-env-files.js +2 -1
- package/dist/lib/portal-list.js +5 -4
- package/dist/lib/portal-source.js +128 -13
- package/dist/lib/run-npm.js +46 -0
- package/dist/locale/en-US.json +13 -1
- package/dist/locale/zh-CN.json +13 -1
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
14
|
import { createPortalWorkspace } from '../../lib/portal-create.js';
|
|
15
|
-
import { printInfo, printSuccess } from '../../lib/ui.js';
|
|
15
|
+
import { printInfo, printSuccess, printWarning } from '../../lib/ui.js';
|
|
16
16
|
const DEFAULT_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
17
17
|
const portalCreateText = (key, values, fallback) => translateCli(`commands.portalCreate.${key}`, values, { fallback });
|
|
18
18
|
export default class PortalCreate extends Command {
|
|
@@ -101,5 +101,8 @@ export default class PortalCreate extends Command {
|
|
|
101
101
|
printInfo(portalCreateText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
102
102
|
printInfo(portalCreateText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
103
103
|
printInfo(portalCreateText('messages.sourceStorage', { sourceStorage: result.sourceStorage }, `Source storage: ${result.sourceStorage}`));
|
|
104
|
+
if (result.installFailed) {
|
|
105
|
+
printWarning(portalCreateText('messages.installFailed', { portalDir: result.portalDir }, `Dependency installation did not finish successfully. Run \`pnpm install\` manually in ${result.portalDir}.`));
|
|
106
|
+
}
|
|
104
107
|
}
|
|
105
108
|
}
|
|
@@ -59,6 +59,8 @@ export default class PortalDev extends Command {
|
|
|
59
59
|
await devPortalWorkspace({
|
|
60
60
|
portal: args.portal,
|
|
61
61
|
env,
|
|
62
|
+
envName,
|
|
63
|
+
cliVersion: String(this.config?.pjson?.version ?? '').trim(),
|
|
62
64
|
onStart: (result) => {
|
|
63
65
|
printInfo(portalDevText('messages.starting', { portal: result.portal }, `Starting portal "${result.portal}"...`));
|
|
64
66
|
printInfo(portalDevText('messages.mode', { mode: result.mode }, `Mode: ${result.mode}`));
|
|
@@ -12,7 +12,7 @@ import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
14
|
import { pullPortalSource } from '../../lib/portal-source.js';
|
|
15
|
-
import { printInfo, printSuccess } from '../../lib/ui.js';
|
|
15
|
+
import { printInfo, printSuccess, printWarning } from '../../lib/ui.js';
|
|
16
16
|
const portalPullText = (key, values, fallback) => translateCli(`commands.portalPull.${key}`, values, { fallback });
|
|
17
17
|
export default class PortalPull extends Command {
|
|
18
18
|
static summary = 'Pull portal source into local files';
|
|
@@ -80,5 +80,8 @@ export default class PortalPull extends Command {
|
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
printSuccess(portalPullText('messages.pulled', { portal: result.portal, portalDir: result.portalDir }, `Pulled portal source "${result.portal}" into ${result.portalDir}.`));
|
|
83
|
+
if (result.installFailed) {
|
|
84
|
+
printWarning(portalPullText('messages.installFailed', { portalDir: result.portalDir }, `Dependency installation did not finish successfully. Run \`pnpm install\` manually in ${result.portalDir}.`));
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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 { readFile, writeFile } from 'node:fs/promises';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
const BUILD_HTML_SCRIPT_PATH = path.join('scripts', 'build-html.mjs');
|
|
12
|
+
const BUILD_HTML_ENV_FILES_PATTERN = /return\s+\[\s*["']\.env["']\s*,\s*["']\.env\.local["']\s*,\s*`\.env\.\$\{mode\}`\s*,\s*`\.env\.\$\{mode\}\.local`\s*\]\.map\(\s*\(?file\)?\s*=>\s*path\.join\(rootDir,\s*file\)\s*\);/m;
|
|
13
|
+
const BUILD_HTML_ENV_ONLY_REPLACEMENT = 'return [".env"].map((file) => path.join(rootDir, file));';
|
|
14
|
+
export async function ensurePortalBuildHtmlReadsEnvOnly(portalDir) {
|
|
15
|
+
const scriptPath = path.join(portalDir, BUILD_HTML_SCRIPT_PATH);
|
|
16
|
+
let content;
|
|
17
|
+
try {
|
|
18
|
+
content = await readFile(scriptPath, 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const nextContent = content.replace(BUILD_HTML_ENV_FILES_PATTERN, BUILD_HTML_ENV_ONLY_REPLACEMENT);
|
|
24
|
+
if (nextContent !== content) {
|
|
25
|
+
await writeFile(scriptPath, nextContent, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -19,9 +19,10 @@ const UPDATE_PORTAL_OPERATION = {
|
|
|
19
19
|
bodyRequired: true,
|
|
20
20
|
parameters: [
|
|
21
21
|
{
|
|
22
|
-
name: '
|
|
23
|
-
flagName: '
|
|
22
|
+
name: 'filter',
|
|
23
|
+
flagName: 'filter',
|
|
24
24
|
in: 'query',
|
|
25
|
+
type: 'object',
|
|
25
26
|
required: true,
|
|
26
27
|
},
|
|
27
28
|
],
|
|
@@ -120,7 +121,9 @@ export async function syncPortalConfigToRemote(options) {
|
|
|
120
121
|
cliVersion: options.cliVersion ?? '',
|
|
121
122
|
envName: options.envName,
|
|
122
123
|
flags: {
|
|
123
|
-
|
|
124
|
+
filter: {
|
|
125
|
+
portalName: options.portal,
|
|
126
|
+
},
|
|
124
127
|
body: JSON.stringify({
|
|
125
128
|
options: mergePortalConfigIntoOptions(options.config, options.currentOptions),
|
|
126
129
|
}),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { mkdir, stat } from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { translateCli } from './cli-locale.js';
|
|
12
|
-
import {
|
|
12
|
+
import { resolvePortalAppContext, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
13
13
|
import { buildPortalConfig, buildPortalConfigFromOptions, readPortalConfig, syncPortalConfigToRemote, writePortalConfig, } from './portal-config.js';
|
|
14
14
|
import { findPortalListItem } from './portal-info.js';
|
|
15
15
|
import { listPortalWorkspaces } from './portal-list.js';
|
|
@@ -64,9 +64,9 @@ export async function configurePortalWorkspace(options) {
|
|
|
64
64
|
throw new Error(portalConfigureText('errors.noChanges', undefined, 'No portal configuration changes were provided. Pass --source-storage or a --git-* flag.'));
|
|
65
65
|
}
|
|
66
66
|
const portal = validatePortalSlug(options.portal);
|
|
67
|
-
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
68
67
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
69
|
-
const
|
|
68
|
+
const appContext = await resolvePortalAppContext(options);
|
|
69
|
+
const { app } = appContext;
|
|
70
70
|
const portalDir = path.join(storagePath, 'portals', app, portal);
|
|
71
71
|
if (!(await pathExists(portalDir))) {
|
|
72
72
|
throw new Error(portalConfigureText('errors.workspaceMissing', { portalDir, portal }, `Portal does not exist: ${portalDir}\nRun \`nb portal create ${portal}\` or \`nb portal pull ${portal}\` first.`));
|
|
@@ -76,6 +76,7 @@ export async function configurePortalWorkspace(options) {
|
|
|
76
76
|
envName: options.envName,
|
|
77
77
|
cliVersion: options.cliVersion,
|
|
78
78
|
apiRequest: options.apiRequest,
|
|
79
|
+
appContext,
|
|
79
80
|
});
|
|
80
81
|
const remoteItem = findPortalListItem(list.items, portal);
|
|
81
82
|
const existingConfig = (await readExistingConfig(portalDir)) ??
|
|
@@ -19,9 +19,10 @@ import { appendAppPublicPath, resolveAppPublicPath } from './app-public-path.js'
|
|
|
19
19
|
import { executeApiRequest } from './api-client.js';
|
|
20
20
|
import { resolveEnvRelativePath } from './cli-home.js';
|
|
21
21
|
import { translateCli } from './cli-locale.js';
|
|
22
|
+
import { ensurePortalBuildHtmlReadsEnvOnly } from './portal-build-html.js';
|
|
22
23
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
23
24
|
import { buildPortalConfig, mergePortalConfigIntoOptions, writePortalConfig, } from './portal-config.js';
|
|
24
|
-
import { run,
|
|
25
|
+
import { resolvePnpmInstallCommand, run, runPnpmInstallCommand } from './run-npm.js';
|
|
25
26
|
const DEFAULT_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
26
27
|
const DEFAULT_PORTAL_APP_NAME = 'main';
|
|
27
28
|
const TEMPLATE_COPY_EXCLUDED_NAMES = new Set(['.git', 'node_modules', '.DS_Store']);
|
|
@@ -42,6 +43,11 @@ const FIRST_OR_CREATE_PORTAL_OPERATION = {
|
|
|
42
43
|
},
|
|
43
44
|
],
|
|
44
45
|
};
|
|
46
|
+
const APP_INFO_OPERATION = {
|
|
47
|
+
method: 'GET',
|
|
48
|
+
pathTemplate: '/app:getInfo',
|
|
49
|
+
parameters: [],
|
|
50
|
+
};
|
|
45
51
|
function trimValue(value) {
|
|
46
52
|
return String(value ?? '').trim();
|
|
47
53
|
}
|
|
@@ -319,6 +325,54 @@ export function resolvePortalAppFromApiBaseUrl(apiBaseUrl, appPublicPath) {
|
|
|
319
325
|
appPublicPath: resolveAppPublicPath(configuredPublicPath || inferredPublicPath),
|
|
320
326
|
};
|
|
321
327
|
}
|
|
328
|
+
function readAppNameFromInfo(data) {
|
|
329
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
const direct = data.name;
|
|
333
|
+
if (typeof direct === 'string' && direct.trim()) {
|
|
334
|
+
return direct.trim();
|
|
335
|
+
}
|
|
336
|
+
return readAppNameFromInfo(data.data);
|
|
337
|
+
}
|
|
338
|
+
function isValidPortalAppName(value) {
|
|
339
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value);
|
|
340
|
+
}
|
|
341
|
+
async function resolvePortalAppFromServer(options) {
|
|
342
|
+
const apiRequest = options.apiRequest ?? executeApiRequest;
|
|
343
|
+
try {
|
|
344
|
+
const response = await apiRequest({
|
|
345
|
+
cliVersion: options.cliVersion ?? '',
|
|
346
|
+
envName: options.envName,
|
|
347
|
+
flags: {},
|
|
348
|
+
operation: APP_INFO_OPERATION,
|
|
349
|
+
});
|
|
350
|
+
if (!response.ok) {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
const appName = readAppNameFromInfo(response.data);
|
|
354
|
+
return appName && isValidPortalAppName(appName) ? appName : undefined;
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
export async function resolvePortalAppContext(options) {
|
|
361
|
+
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
362
|
+
const resolvedApp = resolvePortalAppFromApiBaseUrl(apiBaseUrl, options.env.config.appPublicPath);
|
|
363
|
+
if (options.env.kind !== 'http') {
|
|
364
|
+
return {
|
|
365
|
+
...resolvedApp,
|
|
366
|
+
portalBaseApp: resolvedApp.app,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const serverApp = await resolvePortalAppFromServer(options);
|
|
370
|
+
return {
|
|
371
|
+
app: serverApp ?? resolvedApp.app,
|
|
372
|
+
appPublicPath: resolvedApp.appPublicPath,
|
|
373
|
+
portalBaseApp: resolvedApp.app,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
322
376
|
export function buildPortalBasePath(params) {
|
|
323
377
|
const segment = params.app === DEFAULT_PORTAL_APP_NAME
|
|
324
378
|
? `x/${params.portal}`
|
|
@@ -363,8 +417,8 @@ export async function createPortalWorkspace(options) {
|
|
|
363
417
|
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
364
418
|
const envApiUrl = resolvePortalEnvApiUrl(apiBaseUrl);
|
|
365
419
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
366
|
-
const { app, appPublicPath } =
|
|
367
|
-
const portalBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
420
|
+
const { app, appPublicPath, portalBaseApp } = await resolvePortalAppContext(options);
|
|
421
|
+
const portalBase = buildPortalBasePath({ app: portalBaseApp ?? app, appPublicPath, portal });
|
|
368
422
|
const portalParentDir = path.join(storagePath, 'portals', app);
|
|
369
423
|
const portalDir = path.join(portalParentDir, portal);
|
|
370
424
|
assertPortalDirIsInsideParent(portalParentDir, portalDir);
|
|
@@ -381,6 +435,7 @@ export async function createPortalWorkspace(options) {
|
|
|
381
435
|
let shouldCleanupTempDir = true;
|
|
382
436
|
try {
|
|
383
437
|
await copyTemplate(template.dir, tempDir);
|
|
438
|
+
await ensurePortalBuildHtmlReadsEnvOnly(tempDir);
|
|
384
439
|
await writeFile(path.join(tempDir, '.env'), [`NOCOBASE_API_URL=${envApiUrl}`, `NOCOBASE_PORTAL_BASE=${portalBase}`].join('\n') + '\n', 'utf-8');
|
|
385
440
|
await writeFile(path.join(tempDir, '.env.local'), [`NOCOBASE_API_URL=${apiBaseUrl}`, `NOCOBASE_PORTAL_BASE=${portalBase}`].join('\n') + '\n', 'utf-8');
|
|
386
441
|
await writePortalConfig(tempDir, portalConfig);
|
|
@@ -390,14 +445,23 @@ export async function createPortalWorkspace(options) {
|
|
|
390
445
|
await rename(tempDir, portalDir);
|
|
391
446
|
shouldCleanupTempDir = false;
|
|
392
447
|
const hasPackageJson = await pathExists(path.join(portalDir, 'package.json'));
|
|
448
|
+
let dependenciesInstalled = false;
|
|
449
|
+
let installFailed = false;
|
|
393
450
|
if (hasPackageJson) {
|
|
394
451
|
const runCommand = options.runCommand ?? run;
|
|
395
|
-
await
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
452
|
+
const installCommand = await resolvePnpmInstallCommand(portalDir);
|
|
453
|
+
try {
|
|
454
|
+
await runPnpmInstallCommand(runCommand, installCommand.args, {
|
|
455
|
+
cwd: portalDir,
|
|
456
|
+
env: buildPortalCommandEnv(),
|
|
457
|
+
envMode: 'replace',
|
|
458
|
+
errorName: installCommand.errorName,
|
|
459
|
+
});
|
|
460
|
+
dependenciesInstalled = true;
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
installFailed = true;
|
|
464
|
+
}
|
|
401
465
|
}
|
|
402
466
|
else {
|
|
403
467
|
options.onSkipInstall?.(portalCreateText('messages.skipInstall', { portalDir }, `Skipped pnpm install because package.json was not found in ${portalDir}.`));
|
|
@@ -421,6 +485,8 @@ export async function createPortalWorkspace(options) {
|
|
|
421
485
|
portalBase,
|
|
422
486
|
template,
|
|
423
487
|
installSkipped: !hasPackageJson,
|
|
488
|
+
dependenciesInstalled,
|
|
489
|
+
installFailed,
|
|
424
490
|
sourceStorage: portalConfig.sourceStorage,
|
|
425
491
|
};
|
|
426
492
|
}
|
|
@@ -12,11 +12,12 @@ import path from 'node:path';
|
|
|
12
12
|
import * as tar from 'tar';
|
|
13
13
|
import { executeApiRequest } from './api-client.js';
|
|
14
14
|
import { translateCli } from './cli-locale.js';
|
|
15
|
-
import {
|
|
15
|
+
import { ensurePortalBuildHtmlReadsEnvOnly } from './portal-build-html.js';
|
|
16
|
+
import { buildPortalBasePath, resolvePortalAppContext, resolvePortalEnvApiUrl, resolvePortalStoragePath, titleFromPortalSlug, validatePortalSlug, } from './portal-create.js';
|
|
16
17
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
17
18
|
import { updatePortalEnvFiles } from './portal-env-files.js';
|
|
18
19
|
import { mergePortalConfigIntoOptions, readPortalConfig } from './portal-config.js';
|
|
19
|
-
import { run, runPnpmCommand } from './run-npm.js';
|
|
20
|
+
import { resolvePnpmInstallCommand, run, runPnpmCommand, runPnpmInstallCommand } from './run-npm.js';
|
|
20
21
|
const portalDeployText = (key, values, fallback) => translateCli(`commands.portalDeploy.${key}`, values, { fallback });
|
|
21
22
|
const DEPLOY_OPERATION = {
|
|
22
23
|
method: 'POST',
|
|
@@ -192,9 +193,11 @@ async function syncMultiPortalRecord(params) {
|
|
|
192
193
|
export async function deployPortalWorkspace(options) {
|
|
193
194
|
const portal = validatePortalSlug(options.portal);
|
|
194
195
|
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
196
|
+
const envApiUrl = resolvePortalEnvApiUrl(apiBaseUrl);
|
|
195
197
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
196
|
-
const { app, appPublicPath } =
|
|
197
|
-
const portalBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
198
|
+
const { app, appPublicPath, portalBaseApp } = await resolvePortalAppContext(options);
|
|
199
|
+
const portalBase = buildPortalBasePath({ app: portalBaseApp ?? app, appPublicPath, portal });
|
|
200
|
+
const deployBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
198
201
|
const portalDir = path.join(storagePath, 'portals', app, portal);
|
|
199
202
|
const distDir = path.join(portalDir, 'dist');
|
|
200
203
|
if (!(await pathExists(portalDir))) {
|
|
@@ -207,12 +210,14 @@ export async function deployPortalWorkspace(options) {
|
|
|
207
210
|
apiBaseUrl,
|
|
208
211
|
portalBase,
|
|
209
212
|
});
|
|
213
|
+
await ensurePortalBuildHtmlReadsEnvOnly(portalDir);
|
|
210
214
|
const runCommand = options.runCommand ?? run;
|
|
211
|
-
await
|
|
215
|
+
const installCommand = await resolvePnpmInstallCommand(portalDir);
|
|
216
|
+
await runPnpmInstallCommand(runCommand, installCommand.args, {
|
|
212
217
|
cwd: portalDir,
|
|
213
218
|
env: buildPortalCommandEnv(),
|
|
214
219
|
envMode: 'replace',
|
|
215
|
-
errorName:
|
|
220
|
+
errorName: installCommand.errorName,
|
|
216
221
|
});
|
|
217
222
|
await runPnpmCommand(runCommand, ['build'], {
|
|
218
223
|
cwd: portalDir,
|
|
@@ -226,7 +231,7 @@ export async function deployPortalWorkspace(options) {
|
|
|
226
231
|
await runPnpmCommand(runCommand, ['build:html'], {
|
|
227
232
|
cwd: portalDir,
|
|
228
233
|
env: buildPortalCommandEnv({
|
|
229
|
-
NOCOBASE_API_URL:
|
|
234
|
+
NOCOBASE_API_URL: envApiUrl,
|
|
230
235
|
NOCOBASE_PORTAL_BASE: portalBase,
|
|
231
236
|
}),
|
|
232
237
|
envMode: 'replace',
|
|
@@ -268,7 +273,7 @@ export async function deployPortalWorkspace(options) {
|
|
|
268
273
|
archivePath: archive.archivePath,
|
|
269
274
|
app,
|
|
270
275
|
portal,
|
|
271
|
-
portalBase,
|
|
276
|
+
portalBase: deployBase,
|
|
272
277
|
envName: options.envName,
|
|
273
278
|
cliVersion: options.cliVersion,
|
|
274
279
|
apiRequest: options.apiRequest,
|
|
@@ -10,23 +10,21 @@ import { rm, stat } from 'node:fs/promises';
|
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { executeApiRequest } from './api-client.js';
|
|
12
12
|
import { translateCli } from './cli-locale.js';
|
|
13
|
-
import { buildPortalBasePath,
|
|
13
|
+
import { buildPortalBasePath, resolvePortalAppContext, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
14
14
|
const portalDestroyText = (key, values, fallback) => translateCli(`commands.portalDestroy.${key}`, values, { fallback });
|
|
15
15
|
const DESTROY_PORTAL_OPERATION = {
|
|
16
16
|
method: 'POST',
|
|
17
17
|
pathTemplate: '/multiPortals:destroy',
|
|
18
18
|
parameters: [
|
|
19
19
|
{
|
|
20
|
-
name: '
|
|
21
|
-
flagName: '
|
|
20
|
+
name: 'filter',
|
|
21
|
+
flagName: 'filter',
|
|
22
22
|
in: 'query',
|
|
23
|
+
type: 'object',
|
|
23
24
|
required: true,
|
|
24
25
|
},
|
|
25
26
|
],
|
|
26
27
|
};
|
|
27
|
-
function trimValue(value) {
|
|
28
|
-
return String(value ?? '').trim();
|
|
29
|
-
}
|
|
30
28
|
async function pathExists(target) {
|
|
31
29
|
try {
|
|
32
30
|
await stat(target);
|
|
@@ -48,7 +46,9 @@ async function destroyMultiPortalRecord(params) {
|
|
|
48
46
|
cliVersion: params.cliVersion ?? '',
|
|
49
47
|
envName: params.envName,
|
|
50
48
|
flags: {
|
|
51
|
-
|
|
49
|
+
filter: {
|
|
50
|
+
portalName: params.portal,
|
|
51
|
+
},
|
|
52
52
|
},
|
|
53
53
|
operation: DESTROY_PORTAL_OPERATION,
|
|
54
54
|
});
|
|
@@ -62,10 +62,9 @@ async function destroyMultiPortalRecord(params) {
|
|
|
62
62
|
}
|
|
63
63
|
export async function destroyPortalWorkspace(options) {
|
|
64
64
|
const portal = validatePortalSlug(options.portal);
|
|
65
|
-
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
66
65
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
67
|
-
const { app, appPublicPath } =
|
|
68
|
-
const portalBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
66
|
+
const { app, appPublicPath, portalBaseApp } = await resolvePortalAppContext(options);
|
|
67
|
+
const portalBase = buildPortalBasePath({ app: portalBaseApp ?? app, appPublicPath, portal });
|
|
69
68
|
const portalParentDir = path.join(storagePath, 'portals', app);
|
|
70
69
|
const portalDir = path.join(portalParentDir, portal);
|
|
71
70
|
const mode = options.env.kind;
|
|
@@ -75,9 +74,6 @@ export async function destroyPortalWorkspace(options) {
|
|
|
75
74
|
const destroyMode = mode;
|
|
76
75
|
assertPortalDirIsInsideParent(portalParentDir, portalDir);
|
|
77
76
|
const workspaceExists = await pathExists(portalDir);
|
|
78
|
-
if (!workspaceExists && !options.force) {
|
|
79
|
-
throw new Error(portalDestroyText('errors.workspaceMissing', { portalDir, portal }, `Portal does not exist: ${portalDir}\nPass --force to ignore missing local files.`));
|
|
80
|
-
}
|
|
81
77
|
const recordDeleted = await destroyMultiPortalRecord({
|
|
82
78
|
portal,
|
|
83
79
|
envName: options.envName,
|
package/dist/lib/portal-dev.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { stat } from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { translateCli } from './cli-locale.js';
|
|
12
|
-
import { buildPortalBasePath,
|
|
12
|
+
import { buildPortalBasePath, resolvePortalAppContext, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
13
13
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
14
14
|
import { updatePortalEnvFiles } from './portal-env-files.js';
|
|
15
15
|
import { run, runPnpmCommand } from './run-npm.js';
|
|
@@ -45,8 +45,8 @@ export async function devPortalWorkspace(options) {
|
|
|
45
45
|
throw new Error(portalDevText('errors.sshUnsupported', undefined, 'Cannot start a portal in dev mode for ssh envs in the first version.'));
|
|
46
46
|
}
|
|
47
47
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
48
|
-
const { app, appPublicPath } =
|
|
49
|
-
const portalBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
48
|
+
const { app, appPublicPath, portalBaseApp } = await resolvePortalAppContext(options);
|
|
49
|
+
const portalBase = buildPortalBasePath({ app: portalBaseApp ?? app, appPublicPath, portal });
|
|
50
50
|
const portalDir = path.join(storagePath, 'portals', app, portal);
|
|
51
51
|
if (!(await pathExists(portalDir))) {
|
|
52
52
|
throw new Error(portalDevText('errors.workspaceMissing', { portalDir, portal }, `Portal does not exist: ${portalDir}\nRun \`nb portal create ${portal}\` first.`));
|
|
@@ -42,8 +42,9 @@ export async function upsertPortalEnvFile(filePath, values) {
|
|
|
42
42
|
await writeFile(filePath, upsertEnvContent(content, values), 'utf-8');
|
|
43
43
|
}
|
|
44
44
|
export async function updatePortalEnvFiles(params) {
|
|
45
|
+
const envApiUrl = resolvePortalEnvApiUrl(params.apiBaseUrl);
|
|
45
46
|
await upsertPortalEnvFile(path.join(params.portalDir, '.env'), {
|
|
46
|
-
NOCOBASE_API_URL:
|
|
47
|
+
NOCOBASE_API_URL: envApiUrl,
|
|
47
48
|
NOCOBASE_PORTAL_BASE: params.portalBase,
|
|
48
49
|
});
|
|
49
50
|
await upsertPortalEnvFile(path.join(params.portalDir, '.env.local'), {
|
package/dist/lib/portal-list.js
CHANGED
|
@@ -11,7 +11,7 @@ import path from 'node:path';
|
|
|
11
11
|
import { appendAppPublicPath } from './app-public-path.js';
|
|
12
12
|
import { executeApiRequest } from './api-client.js';
|
|
13
13
|
import { translateCli } from './cli-locale.js';
|
|
14
|
-
import { buildPortalBasePath,
|
|
14
|
+
import { buildPortalBasePath, resolvePortalAppContext, resolvePortalStoragePath, } from './portal-create.js';
|
|
15
15
|
const portalListText = (key, values, fallback) => translateCli(`commands.portalList.${key}`, values, { fallback });
|
|
16
16
|
const LIST_PORTALS_OPERATION = {
|
|
17
17
|
method: 'GET',
|
|
@@ -159,8 +159,9 @@ async function listMultiPortalRecords(params) {
|
|
|
159
159
|
export async function listPortalWorkspaces(options) {
|
|
160
160
|
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
161
161
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
162
|
-
const { app, appPublicPath } =
|
|
162
|
+
const { app, appPublicPath, portalBaseApp } = options.appContext ?? (await resolvePortalAppContext(options));
|
|
163
163
|
const mode = options.env.kind;
|
|
164
|
+
const baseApp = portalBaseApp ?? app;
|
|
164
165
|
if (mode !== 'local' && mode !== 'docker' && mode !== 'http') {
|
|
165
166
|
throw new Error(portalListText('errors.unsupportedEnvKind', { kind: mode }, `Cannot list portals for ${mode} envs in the first version.`));
|
|
166
167
|
}
|
|
@@ -195,8 +196,8 @@ export async function listPortalWorkspaces(options) {
|
|
|
195
196
|
options,
|
|
196
197
|
portalUrl: enabled
|
|
197
198
|
? buildPortalAccessUrl(apiBaseUrl, isAi
|
|
198
|
-
? buildPortalBasePath({ app, appPublicPath, portal: portalName })
|
|
199
|
-
: buildNoCodePortalBasePath({ app, appPublicPath, routePath }))
|
|
199
|
+
? buildPortalBasePath({ app: baseApp, appPublicPath, portal: portalName })
|
|
200
|
+
: buildNoCodePortalBasePath({ app: baseApp, appPublicPath, routePath }))
|
|
200
201
|
: '',
|
|
201
202
|
portalDir,
|
|
202
203
|
localSynced: isAi ? await pathExists(portalDir) : null,
|
|
@@ -14,12 +14,13 @@ import path from 'node:path';
|
|
|
14
14
|
import * as tar from 'tar';
|
|
15
15
|
import { executeApiRequest } from './api-client.js';
|
|
16
16
|
import { translateCli } from './cli-locale.js';
|
|
17
|
+
import { ensurePortalBuildHtmlReadsEnvOnly } from './portal-build-html.js';
|
|
17
18
|
import { buildPortalConfig, buildPortalConfigFromOptions, DEFAULT_PORTAL_GIT_PATH, readPortalConfig, syncPortalConfigToRemote, writePortalConfig, } from './portal-config.js';
|
|
18
19
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
19
|
-
import { buildPortalBasePath,
|
|
20
|
+
import { buildPortalBasePath, resolvePortalAppContext, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
20
21
|
import { listPortalWorkspaces } from './portal-list.js';
|
|
21
22
|
import { findPortalListItem } from './portal-info.js';
|
|
22
|
-
import { run,
|
|
23
|
+
import { resolvePnpmInstallCommand, run, runPnpmInstallCommand } from './run-npm.js';
|
|
23
24
|
const execFileAsync = promisify(execFile);
|
|
24
25
|
const portalSourceText = (key, values, fallback) => translateCli(`commands.portalSource.${key}`, values, { fallback });
|
|
25
26
|
const PULL_SOURCE_OPERATION = {
|
|
@@ -128,6 +129,20 @@ async function packPortalSource(portalDir) {
|
|
|
128
129
|
},
|
|
129
130
|
};
|
|
130
131
|
}
|
|
132
|
+
async function replaceExistingPortalDirectory(params) {
|
|
133
|
+
if (!(await pathExists(path.join(params.portalDir, '.git')))) {
|
|
134
|
+
await rm(params.portalDir, { recursive: true, force: true });
|
|
135
|
+
await rename(params.sourceDir, params.portalDir);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const existingEntries = await readdir(params.portalDir);
|
|
139
|
+
await Promise.all(existingEntries
|
|
140
|
+
.filter((entry) => entry !== '.git')
|
|
141
|
+
.map((entry) => rm(path.join(params.portalDir, entry), { recursive: true, force: true })));
|
|
142
|
+
const sourceEntries = await readdir(params.sourceDir);
|
|
143
|
+
await Promise.all(sourceEntries.map((entry) => rename(path.join(params.sourceDir, entry), path.join(params.portalDir, entry))));
|
|
144
|
+
await rm(params.sourceDir, { recursive: true, force: true });
|
|
145
|
+
}
|
|
131
146
|
async function replacePortalSourceFromArchive(params) {
|
|
132
147
|
const targetExists = await pathExists(params.portalDir);
|
|
133
148
|
if (targetExists && !params.force) {
|
|
@@ -143,7 +158,11 @@ async function replacePortalSourceFromArchive(params) {
|
|
|
143
158
|
filter: validatePortalSourceTarEntry,
|
|
144
159
|
});
|
|
145
160
|
if (targetExists) {
|
|
146
|
-
await
|
|
161
|
+
await replaceExistingPortalDirectory({
|
|
162
|
+
sourceDir: tempDir,
|
|
163
|
+
portalDir: params.portalDir,
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
147
166
|
}
|
|
148
167
|
await rename(tempDir, params.portalDir);
|
|
149
168
|
}
|
|
@@ -165,7 +184,11 @@ async function replacePortalSourceFromDirectory(params) {
|
|
|
165
184
|
filter: (source) => shouldPackPortalSourceEntry(path.relative(params.sourceDir, source)),
|
|
166
185
|
});
|
|
167
186
|
if (targetExists) {
|
|
168
|
-
await
|
|
187
|
+
await replaceExistingPortalDirectory({
|
|
188
|
+
sourceDir: tempDir,
|
|
189
|
+
portalDir: params.portalDir,
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
169
192
|
}
|
|
170
193
|
await rename(tempDir, params.portalDir);
|
|
171
194
|
}
|
|
@@ -174,6 +197,13 @@ async function replacePortalSourceFromDirectory(params) {
|
|
|
174
197
|
throw error;
|
|
175
198
|
}
|
|
176
199
|
}
|
|
200
|
+
async function assertPortalDirectoryCanBeReplaced(params) {
|
|
201
|
+
const targetExists = await pathExists(params.portalDir);
|
|
202
|
+
if (targetExists && !params.force) {
|
|
203
|
+
throw new Error(portalSourceText('errors.workspaceExists', { portalDir: params.portalDir }, `Portal already exists: ${params.portalDir}\nPass --force to delete it and pull again.`));
|
|
204
|
+
}
|
|
205
|
+
return targetExists;
|
|
206
|
+
}
|
|
177
207
|
async function runGit(args, cwd) {
|
|
178
208
|
return await execFileAsync('git', args, {
|
|
179
209
|
cwd,
|
|
@@ -185,24 +215,37 @@ async function installPortalDependencies(params) {
|
|
|
185
215
|
return {
|
|
186
216
|
dependenciesInstalled: false,
|
|
187
217
|
installSkipped: true,
|
|
218
|
+
installFailed: false,
|
|
188
219
|
};
|
|
189
220
|
}
|
|
190
221
|
if (!(await isFile(path.join(params.portalDir, 'package.json')))) {
|
|
191
222
|
return {
|
|
192
223
|
dependenciesInstalled: false,
|
|
193
224
|
installSkipped: true,
|
|
225
|
+
installFailed: false,
|
|
194
226
|
};
|
|
195
227
|
}
|
|
196
228
|
const runCommand = params.runCommand ?? run;
|
|
197
|
-
await
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
229
|
+
const installCommand = await resolvePnpmInstallCommand(params.portalDir);
|
|
230
|
+
try {
|
|
231
|
+
await runPnpmInstallCommand(runCommand, installCommand.args, {
|
|
232
|
+
cwd: params.portalDir,
|
|
233
|
+
env: buildPortalCommandEnv(),
|
|
234
|
+
envMode: 'replace',
|
|
235
|
+
errorName: installCommand.errorName,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return {
|
|
240
|
+
dependenciesInstalled: false,
|
|
241
|
+
installSkipped: false,
|
|
242
|
+
installFailed: true,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
203
245
|
return {
|
|
204
246
|
dependenciesInstalled: true,
|
|
205
247
|
installSkipped: false,
|
|
248
|
+
installFailed: false,
|
|
206
249
|
};
|
|
207
250
|
}
|
|
208
251
|
function readSourceRevision(data) {
|
|
@@ -219,10 +262,11 @@ async function resolvePortalSourceContext(options) {
|
|
|
219
262
|
const portal = validatePortalSlug(options.portal);
|
|
220
263
|
const apiBaseUrl = trimValue(options.env.apiBaseUrl);
|
|
221
264
|
const storagePath = resolvePortalStoragePath(options.env);
|
|
222
|
-
const { app, appPublicPath } = resolvePortalAppFromApiBaseUrl(apiBaseUrl, options.env.config.appPublicPath);
|
|
223
|
-
const portalDir = path.join(storagePath, 'portals', app, portal);
|
|
224
|
-
const portalBase = buildPortalBasePath({ app, appPublicPath, portal });
|
|
225
265
|
const mode = options.env.kind;
|
|
266
|
+
const appContext = await resolvePortalAppContext(options);
|
|
267
|
+
const { app, appPublicPath, portalBaseApp } = appContext;
|
|
268
|
+
const portalDir = path.join(storagePath, 'portals', app, portal);
|
|
269
|
+
const portalBase = buildPortalBasePath({ app: portalBaseApp ?? app, appPublicPath, portal });
|
|
226
270
|
if (mode !== 'local' && mode !== 'docker' && mode !== 'http') {
|
|
227
271
|
throw new Error(portalSourceText('errors.unsupportedEnvKind', { kind: mode }, `Cannot sync portal source for ${mode} envs in the first version.`));
|
|
228
272
|
}
|
|
@@ -231,6 +275,7 @@ async function resolvePortalSourceContext(options) {
|
|
|
231
275
|
envName: options.envName,
|
|
232
276
|
cliVersion: options.cliVersion,
|
|
233
277
|
apiRequest: options.apiRequest,
|
|
278
|
+
appContext,
|
|
234
279
|
});
|
|
235
280
|
const item = findPortalListItem(list.items, portal);
|
|
236
281
|
if (!item) {
|
|
@@ -319,8 +364,76 @@ async function cloneGitSource(params) {
|
|
|
319
364
|
}
|
|
320
365
|
return repoDir;
|
|
321
366
|
}
|
|
367
|
+
async function setGitOriginRepository(params) {
|
|
368
|
+
try {
|
|
369
|
+
await runGit(['remote', 'get-url', 'origin'], params.repoDir);
|
|
370
|
+
await runGit(['remote', 'set-url', 'origin', params.repo], params.repoDir);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
await runGit(['remote', 'add', 'origin', params.repo], params.repoDir);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function checkoutExistingGitRepository(params) {
|
|
377
|
+
await setGitOriginRepository({
|
|
378
|
+
repoDir: params.repoDir,
|
|
379
|
+
repo: params.repo,
|
|
380
|
+
});
|
|
381
|
+
try {
|
|
382
|
+
await runGit(['fetch', 'origin', params.branch], params.repoDir);
|
|
383
|
+
await runGit(['checkout', '-f', '-B', params.branch, 'FETCH_HEAD'], params.repoDir);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
await runGit(['fetch', 'origin'], params.repoDir);
|
|
387
|
+
await runGit(['checkout', '-f', '-B', params.branch], params.repoDir);
|
|
388
|
+
}
|
|
389
|
+
await runGit(['clean', '-fdx'], params.repoDir);
|
|
390
|
+
}
|
|
391
|
+
async function pullGitRepositoryRootPortalSource(params) {
|
|
392
|
+
const targetExists = await assertPortalDirectoryCanBeReplaced({
|
|
393
|
+
portalDir: params.context.portalDir,
|
|
394
|
+
force: params.force,
|
|
395
|
+
});
|
|
396
|
+
await mkdir(path.dirname(params.context.portalDir), { recursive: true });
|
|
397
|
+
if (targetExists && (await pathExists(path.join(params.context.portalDir, '.git')))) {
|
|
398
|
+
await checkoutExistingGitRepository({
|
|
399
|
+
repoDir: params.context.portalDir,
|
|
400
|
+
repo: params.repo,
|
|
401
|
+
branch: params.branch,
|
|
402
|
+
});
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (targetExists) {
|
|
406
|
+
await rm(params.context.portalDir, { recursive: true, force: true });
|
|
407
|
+
}
|
|
408
|
+
const tempDir = await mkdtemp(path.join(path.dirname(params.context.portalDir), `.${path.basename(params.context.portalDir)}-git-pull-`));
|
|
409
|
+
try {
|
|
410
|
+
const repoDir = await cloneGitSource({
|
|
411
|
+
repo: params.repo,
|
|
412
|
+
branch: params.branch,
|
|
413
|
+
cwd: tempDir,
|
|
414
|
+
createBranch: true,
|
|
415
|
+
});
|
|
416
|
+
await rename(repoDir, params.context.portalDir);
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
await rm(params.context.portalDir, { recursive: true, force: true });
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
322
426
|
async function pullGitPortalSource(params) {
|
|
323
427
|
const git = assertGitSourceConfig(params.context);
|
|
428
|
+
if (isGitRepositoryRootPath(git.gitPath)) {
|
|
429
|
+
await pullGitRepositoryRootPortalSource({
|
|
430
|
+
context: params.context,
|
|
431
|
+
repo: git.repo,
|
|
432
|
+
branch: git.branch,
|
|
433
|
+
force: params.force,
|
|
434
|
+
});
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
324
437
|
const tempDir = await mkdtemp(path.join(os.tmpdir(), 'nocobase-cli-portal-git-pull-'));
|
|
325
438
|
try {
|
|
326
439
|
const repoDir = await cloneGitSource({
|
|
@@ -390,6 +503,7 @@ export async function pullPortalSource(options) {
|
|
|
390
503
|
context: sourceContext,
|
|
391
504
|
force: options.force,
|
|
392
505
|
});
|
|
506
|
+
await ensurePortalBuildHtmlReadsEnvOnly(sourceContext.portalDir);
|
|
393
507
|
await writePortalConfig(sourceContext.portalDir, portalConfig);
|
|
394
508
|
const installResult = await installPortalDependencies({
|
|
395
509
|
portalDir: sourceContext.portalDir,
|
|
@@ -437,6 +551,7 @@ export async function pullPortalSource(options) {
|
|
|
437
551
|
portalDir: sourceContext.portalDir,
|
|
438
552
|
force: options.force,
|
|
439
553
|
});
|
|
554
|
+
await ensurePortalBuildHtmlReadsEnvOnly(sourceContext.portalDir);
|
|
440
555
|
await writePortalConfig(sourceContext.portalDir, portalConfig);
|
|
441
556
|
const installResult = await installPortalDependencies({
|
|
442
557
|
portalDir: sourceContext.portalDir,
|
package/dist/lib/run-npm.js
CHANGED
|
@@ -105,6 +105,52 @@ export async function runPnpmCommand(runCommand, args, options) {
|
|
|
105
105
|
throw createMissingCommandError('pnpm', options.errorName ?? `pnpm ${args.join(' ')}`.trim(), error) ?? error;
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
+
function createInstallArgsWithoutTrustLockfile(args) {
|
|
109
|
+
if (!args.includes('install') || !args.includes('--trust-lockfile')) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return args.filter((arg) => arg !== '--trust-lockfile');
|
|
113
|
+
}
|
|
114
|
+
function isFriendlyMissingPnpmError(error) {
|
|
115
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
116
|
+
return message.includes('because the pnpm executable could not be found');
|
|
117
|
+
}
|
|
118
|
+
function formatPnpmLabel(args) {
|
|
119
|
+
return `pnpm ${args.join(' ')}`.trim();
|
|
120
|
+
}
|
|
121
|
+
export async function resolvePnpmInstallCommand(cwd) {
|
|
122
|
+
const hasLockfile = await fsp
|
|
123
|
+
.stat(path.join(cwd, 'pnpm-lock.yaml'))
|
|
124
|
+
.then((stats) => stats.isFile())
|
|
125
|
+
.catch(() => false);
|
|
126
|
+
if (hasLockfile) {
|
|
127
|
+
const args = ['install', '--frozen-lockfile', '--trust-lockfile'];
|
|
128
|
+
return {
|
|
129
|
+
args,
|
|
130
|
+
errorName: formatPnpmLabel(args),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const args = ['install'];
|
|
134
|
+
return {
|
|
135
|
+
args,
|
|
136
|
+
errorName: formatPnpmLabel(args),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export async function runPnpmInstallCommand(runCommand, args, options) {
|
|
140
|
+
try {
|
|
141
|
+
await runPnpmCommand(runCommand, args, options);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
const fallbackArgs = createInstallArgsWithoutTrustLockfile(args);
|
|
145
|
+
if (!fallbackArgs || isFriendlyMissingPnpmError(error)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
await runPnpmCommand(runCommand, fallbackArgs, {
|
|
149
|
+
...options,
|
|
150
|
+
errorName: formatPnpmLabel(fallbackArgs),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
108
154
|
function isDockerDaemonUnavailableError(error) {
|
|
109
155
|
const message = error instanceof Error ? error.message : String(error);
|
|
110
156
|
return DOCKER_DAEMON_UNAVAILABLE_PATTERNS.some((pattern) => pattern.test(message));
|
package/dist/locale/en-US.json
CHANGED
|
@@ -192,7 +192,8 @@
|
|
|
192
192
|
"created": "Portal \"{{portal}}\" created at {{portalDir}}.",
|
|
193
193
|
"app": "App: {{app}}",
|
|
194
194
|
"base": "Base: {{base}}",
|
|
195
|
-
"sourceStorage": "Source storage: {{sourceStorage}}"
|
|
195
|
+
"sourceStorage": "Source storage: {{sourceStorage}}",
|
|
196
|
+
"installFailed": "Dependency installation did not finish successfully. Run `pnpm install` manually in {{portalDir}}."
|
|
196
197
|
}
|
|
197
198
|
},
|
|
198
199
|
"portalConfig": {
|
|
@@ -259,6 +260,17 @@
|
|
|
259
260
|
"localSynced": "Local synced"
|
|
260
261
|
}
|
|
261
262
|
},
|
|
263
|
+
"portalPull": {
|
|
264
|
+
"errors": {
|
|
265
|
+
"envNotConfigured": "Env \"{{envName}}\" is not configured. Run `nb env add {{envName}} --api-base-url <url>` first.",
|
|
266
|
+
"noEnvConfigured": "No NocoBase env is configured yet. Run `nb init --ui` to create one first."
|
|
267
|
+
},
|
|
268
|
+
"messages": {
|
|
269
|
+
"noop": "No pull is needed.",
|
|
270
|
+
"pulled": "Pulled portal source \"{{portal}}\" into {{portalDir}}.",
|
|
271
|
+
"installFailed": "Dependency installation did not finish successfully. Run `pnpm install` manually in {{portalDir}}."
|
|
272
|
+
}
|
|
273
|
+
},
|
|
262
274
|
"swagger": {
|
|
263
275
|
"errors": {
|
|
264
276
|
"pluginDisabled": "The API documentation plugin is not enabled. Enable it before requesting Swagger documents.",
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -192,7 +192,8 @@
|
|
|
192
192
|
"created": "Portal \"{{portal}}\" 已创建:{{portalDir}}。",
|
|
193
193
|
"app": "App:{{app}}",
|
|
194
194
|
"base": "Base:{{base}}",
|
|
195
|
-
"sourceStorage": "源码存储:{{sourceStorage}}"
|
|
195
|
+
"sourceStorage": "源码存储:{{sourceStorage}}",
|
|
196
|
+
"installFailed": "依赖安装没有成功完成。请在 {{portalDir}} 中手动运行 `pnpm install`。"
|
|
196
197
|
}
|
|
197
198
|
},
|
|
198
199
|
"portalConfig": {
|
|
@@ -259,6 +260,17 @@
|
|
|
259
260
|
"localSynced": "本地已同步"
|
|
260
261
|
}
|
|
261
262
|
},
|
|
263
|
+
"portalPull": {
|
|
264
|
+
"errors": {
|
|
265
|
+
"envNotConfigured": "env \"{{envName}}\" 尚未配置。请先运行 `nb env add {{envName}} --api-base-url <url>`。",
|
|
266
|
+
"noEnvConfigured": "还没有配置 NocoBase env。请先运行 `nb init --ui` 创建一个。"
|
|
267
|
+
},
|
|
268
|
+
"messages": {
|
|
269
|
+
"noop": "无需执行 pull。",
|
|
270
|
+
"pulled": "Portal 源码 \"{{portal}}\" 已拉取到 {{portalDir}}。",
|
|
271
|
+
"installFailed": "依赖安装没有成功完成。请在 {{portalDir}} 中手动运行 `pnpm install`。"
|
|
272
|
+
}
|
|
273
|
+
},
|
|
262
274
|
"swagger": {
|
|
263
275
|
"errors": {
|
|
264
276
|
"pluginDisabled": "API 文档插件尚未启用。请先启用该插件,再获取 Swagger 文档。",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.5",
|
|
4
4
|
"description": "NocoBase Command Line Tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/generated/command-registry.js",
|
|
@@ -147,5 +147,5 @@
|
|
|
147
147
|
"type": "git",
|
|
148
148
|
"url": "git+https://github.com/nocobase/nocobase.git"
|
|
149
149
|
},
|
|
150
|
-
"gitHead": "
|
|
150
|
+
"gitHead": "82c7ed1cdb2f247c190582e34ee37f154cac0968"
|
|
151
151
|
}
|