@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
|
@@ -13,6 +13,9 @@ import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
|
13
13
|
import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
|
|
14
14
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, resolveCliLocale, translateCli, } from "../../lib/cli-locale.js";
|
|
15
15
|
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_REGISTRY_ZH_CN, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
16
|
+
import { getEnv } from '../../lib/auth-store.js';
|
|
17
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
18
|
+
import { buildBeforeDependencyInstallHookContext, runBeforeDependencyInstallHook, } from '../../lib/hook-script.js';
|
|
16
19
|
import { run } from "../../lib/run-npm.js";
|
|
17
20
|
import { printVerbose, setVerboseMode, startTask, stopTask, updateTask } from '../../lib/ui.js';
|
|
18
21
|
const DEFAULT_DOCKER_PLATFORM = 'auto';
|
|
@@ -231,6 +234,26 @@ export default class SourceDownload extends Command {
|
|
|
231
234
|
'npm-registry': Flags.string({
|
|
232
235
|
description: 'npm registry for npm/git downloads and dependency installation.',
|
|
233
236
|
}),
|
|
237
|
+
'hook-script': Flags.string({
|
|
238
|
+
description: 'Hook module to run after npm scaffold or git clone and before dependency installation.',
|
|
239
|
+
}),
|
|
240
|
+
'hook-phase': Flags.string({
|
|
241
|
+
hidden: true,
|
|
242
|
+
options: ['init', 'upgrade', 'restore', 'source-download', 'app-start'],
|
|
243
|
+
}),
|
|
244
|
+
'hook-command': Flags.string({
|
|
245
|
+
hidden: true,
|
|
246
|
+
options: ['init', 'source:download', 'app:start', 'app:restart', 'app:upgrade'],
|
|
247
|
+
}),
|
|
248
|
+
'hook-env-name': Flags.string({
|
|
249
|
+
hidden: true,
|
|
250
|
+
}),
|
|
251
|
+
'hook-app-path': Flags.string({
|
|
252
|
+
hidden: true,
|
|
253
|
+
}),
|
|
254
|
+
'hook-storage-path': Flags.string({
|
|
255
|
+
hidden: true,
|
|
256
|
+
}),
|
|
234
257
|
build: Flags.boolean({
|
|
235
258
|
allowNo: true,
|
|
236
259
|
description: 'Build npm/git source after dependencies are installed.',
|
|
@@ -573,6 +596,9 @@ export default class SourceDownload extends Command {
|
|
|
573
596
|
const npmRegistryRaw = results.npmRegistry !== undefined ? String(results.npmRegistry) : flags['npm-registry'] ?? '';
|
|
574
597
|
const npmRegistry = npmRegistryRaw.trim() || undefined;
|
|
575
598
|
const npmDevDependencies = devDependencies ?? false;
|
|
599
|
+
const hookScript = flags['hook-script']?.trim()
|
|
600
|
+
? path.resolve(process.cwd(), flags['hook-script'].trim())
|
|
601
|
+
: undefined;
|
|
576
602
|
return {
|
|
577
603
|
source,
|
|
578
604
|
version,
|
|
@@ -586,6 +612,14 @@ export default class SourceDownload extends Command {
|
|
|
586
612
|
...(source === 'docker' ? { 'docker-platform': dockerPlatform } : {}),
|
|
587
613
|
...(source === 'docker' ? { 'docker-save': dockerSave } : {}),
|
|
588
614
|
...(npmRegistry ? { 'npm-registry': npmRegistry } : {}),
|
|
615
|
+
...(hookScript ? { 'hook-script': hookScript } : {}),
|
|
616
|
+
...(flags['hook-phase'] ? { 'hook-phase': flags['hook-phase'] } : {}),
|
|
617
|
+
...(flags['hook-command'] ? { 'hook-command': flags['hook-command'] } : {}),
|
|
618
|
+
...(flags['hook-env-name'] ? { 'hook-env-name': flags['hook-env-name'].trim() } : {}),
|
|
619
|
+
...(flags['hook-app-path'] ? { 'hook-app-path': path.resolve(process.cwd(), flags['hook-app-path']) } : {}),
|
|
620
|
+
...(flags['hook-storage-path']
|
|
621
|
+
? { 'hook-storage-path': path.resolve(process.cwd(), flags['hook-storage-path']) }
|
|
622
|
+
: {}),
|
|
589
623
|
};
|
|
590
624
|
}
|
|
591
625
|
async resolveDownloadFlags(flags) {
|
|
@@ -718,6 +752,36 @@ export default class SourceDownload extends Command {
|
|
|
718
752
|
}
|
|
719
753
|
return argv;
|
|
720
754
|
}
|
|
755
|
+
async runBeforeDependencyInstallHookIfNeeded(flags, projectRoot) {
|
|
756
|
+
const hookScript = flags['hook-script']?.trim();
|
|
757
|
+
if (!hookScript || (flags.source !== 'npm' && flags.source !== 'git')) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const envName = flags['hook-env-name']?.trim() || '';
|
|
761
|
+
const env = envName ? await getEnv(envName, { scope: resolveDefaultConfigScope() }) : undefined;
|
|
762
|
+
const appPath = flags['hook-app-path']?.trim() || projectRoot;
|
|
763
|
+
const storagePath = flags['hook-storage-path']?.trim() || path.join(appPath, 'storage');
|
|
764
|
+
const context = buildBeforeDependencyInstallHookContext({
|
|
765
|
+
phase: flags['hook-phase'] ?? 'source-download',
|
|
766
|
+
command: flags['hook-command'] ?? 'source:download',
|
|
767
|
+
envName,
|
|
768
|
+
source: flags.source,
|
|
769
|
+
version: flags.version,
|
|
770
|
+
appPath,
|
|
771
|
+
sourcePath: projectRoot,
|
|
772
|
+
storagePath,
|
|
773
|
+
hookScript,
|
|
774
|
+
envConfig: env?.config ?? {},
|
|
775
|
+
});
|
|
776
|
+
if (!context) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
this.log(`Running hook before dependency install: ${hookScript}`);
|
|
780
|
+
await runBeforeDependencyInstallHook({
|
|
781
|
+
hookScriptPath: hookScript,
|
|
782
|
+
context,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
721
785
|
async downloadFromDocker(flags) {
|
|
722
786
|
const imageRef = resolveDockerImageRef(flags['docker-registry'], flags.version, {
|
|
723
787
|
defaultRegistry: defaultDockerRegistryForLang(process.env.NB_LOCALE),
|
|
@@ -777,6 +841,7 @@ export default class SourceDownload extends Command {
|
|
|
777
841
|
errorName: 'npx create-nocobase-app',
|
|
778
842
|
loadingMessage: 'Creating the app scaffold',
|
|
779
843
|
});
|
|
844
|
+
await this.runBeforeDependencyInstallHookIfNeeded(flags, projectRoot);
|
|
780
845
|
const installArgs = ['install'];
|
|
781
846
|
if (!flags['dev-dependencies']) {
|
|
782
847
|
installArgs.push('--production');
|
|
@@ -820,6 +885,7 @@ export default class SourceDownload extends Command {
|
|
|
820
885
|
});
|
|
821
886
|
const projectRoot = path.resolve(process.cwd(), outputDir);
|
|
822
887
|
const registryEnv = this.npmRegistryEnv(flags);
|
|
888
|
+
await this.runBeforeDependencyInstallHookIfNeeded(flags, projectRoot);
|
|
823
889
|
this.log(`Installing dependencies in ${projectRoot}`);
|
|
824
890
|
await this.runExternalCommand('yarn', ['install'], {
|
|
825
891
|
...this.runOptionsWithCwd(projectRoot, registryEnv),
|
|
@@ -6,13 +6,16 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
10
12
|
import { resolveAppPublicPath } from './app-public-path.js';
|
|
11
13
|
import { dockerContainerExists, managedAppLifecycleEnvVars, runLocalNocoBaseCommand, startDockerContainer, } from './app-runtime.js';
|
|
12
14
|
import { deriveBuiltinDbConnection, resolveBuiltinDbConnection } from './builtin-db.js';
|
|
13
15
|
import { resolveConfiguredStoragePath } from './env-paths.js';
|
|
14
16
|
import { resolveDockerEnvFileArg } from "./docker-env-file.js";
|
|
15
17
|
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "./docker-image.js";
|
|
18
|
+
import { resolveHookScriptPath } from './hook-script.js';
|
|
16
19
|
import { commandSucceeds, ensureDockerDaemonRunning, run } from './run-npm.js';
|
|
17
20
|
import Install from '../commands/install.js';
|
|
18
21
|
const DOCKER_APP_STORAGE_DESTINATION = '/app/nocobase/storage';
|
|
@@ -90,6 +93,15 @@ function formatLocalPostinstallFailure(envName, message) {
|
|
|
90
93
|
`Details: ${message}`,
|
|
91
94
|
].join('\n');
|
|
92
95
|
}
|
|
96
|
+
function formatNpmSourceDevDependenciesFailure(envName, projectRoot, message) {
|
|
97
|
+
return [
|
|
98
|
+
`Couldn't prepare source dev dependencies for "${envName}".`,
|
|
99
|
+
'`nb source dev` requires @nocobase/devtools in npm source envs.',
|
|
100
|
+
`Source directory: ${projectRoot}`,
|
|
101
|
+
`Run \`cd ${projectRoot} && yarn install\` after fixing package.json, then try again.`,
|
|
102
|
+
`Details: ${message}`,
|
|
103
|
+
].join('\n');
|
|
104
|
+
}
|
|
93
105
|
function formatSavedDockerSettingsIncomplete(envName, missing) {
|
|
94
106
|
return [
|
|
95
107
|
`Can't start NocoBase for "${envName}" yet.`,
|
|
@@ -114,6 +126,45 @@ async function localProjectHasFiles(projectRoot) {
|
|
|
114
126
|
return false;
|
|
115
127
|
}
|
|
116
128
|
}
|
|
129
|
+
function isRecord(value) {
|
|
130
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
131
|
+
}
|
|
132
|
+
async function readPackageJson(projectRoot) {
|
|
133
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
134
|
+
const content = await readFile(packageJsonPath, 'utf-8');
|
|
135
|
+
const parsed = JSON.parse(content);
|
|
136
|
+
if (!isRecord(parsed)) {
|
|
137
|
+
throw new Error(`${packageJsonPath} must contain a JSON object.`);
|
|
138
|
+
}
|
|
139
|
+
return parsed;
|
|
140
|
+
}
|
|
141
|
+
function getStringDependency(packageJson, section, packageName) {
|
|
142
|
+
const dependencies = packageJson[section];
|
|
143
|
+
if (!isRecord(dependencies)) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const version = dependencies[packageName];
|
|
147
|
+
return typeof version === 'string' && version.trim() ? version.trim() : undefined;
|
|
148
|
+
}
|
|
149
|
+
function ensureDevDependencies(packageJson) {
|
|
150
|
+
const devDependencies = packageJson.devDependencies;
|
|
151
|
+
if (devDependencies === undefined) {
|
|
152
|
+
const next = {};
|
|
153
|
+
packageJson.devDependencies = next;
|
|
154
|
+
return next;
|
|
155
|
+
}
|
|
156
|
+
if (!isRecord(devDependencies)) {
|
|
157
|
+
throw new Error('package.json devDependencies must be an object.');
|
|
158
|
+
}
|
|
159
|
+
return devDependencies;
|
|
160
|
+
}
|
|
161
|
+
function hasNpmSourceDevtools(projectRoot) {
|
|
162
|
+
return existsSync(path.join(projectRoot, 'node_modules', '@nocobase', 'devtools', 'package.json'));
|
|
163
|
+
}
|
|
164
|
+
function npmRegistryEnv(runtime) {
|
|
165
|
+
const npmRegistry = String(runtime.env.config?.npmRegistry ?? '').trim();
|
|
166
|
+
return npmRegistry ? { npm_config_registry: npmRegistry } : undefined;
|
|
167
|
+
}
|
|
117
168
|
export async function buildSavedDockerRunArgs(runtime, options) {
|
|
118
169
|
const config = runtime.env.config ?? {};
|
|
119
170
|
const storagePath = trimValue(resolveConfiguredStoragePath(config));
|
|
@@ -302,6 +353,13 @@ export function buildSavedLocalDownloadArgv(runtime, options) {
|
|
|
302
353
|
if (config.buildDts === true) {
|
|
303
354
|
argv.push('--build-dts');
|
|
304
355
|
}
|
|
356
|
+
const hookScriptPath = resolveHookScriptPath({
|
|
357
|
+
appPath: runtime.env.appPath,
|
|
358
|
+
hookScript: config.hookScript,
|
|
359
|
+
});
|
|
360
|
+
if (hookScriptPath) {
|
|
361
|
+
argv.push('--hook-script', hookScriptPath, '--hook-phase', options?.hookPhase ?? 'restore', '--hook-command', options?.hookCommand ?? 'app:start', '--hook-env-name', runtime.envName, '--hook-app-path', runtime.env.appPath, '--hook-storage-path', runtime.env.storagePath);
|
|
362
|
+
}
|
|
305
363
|
return argv;
|
|
306
364
|
}
|
|
307
365
|
export async function ensureSavedLocalSource(runtime, runCommand, options) {
|
|
@@ -313,6 +371,8 @@ export async function ensureSavedLocalSource(runtime, runCommand, options) {
|
|
|
313
371
|
try {
|
|
314
372
|
await runCommand('source:download', buildSavedLocalDownloadArgv(runtime, {
|
|
315
373
|
verbose: options?.verbose,
|
|
374
|
+
hookPhase: options?.hookPhase,
|
|
375
|
+
hookCommand: options?.hookCommand,
|
|
316
376
|
}));
|
|
317
377
|
options?.onSucceedTask?.(`NocoBase files are ready for "${runtime.envName}".`);
|
|
318
378
|
}
|
|
@@ -335,3 +395,43 @@ export async function ensureLocalPostinstall(runtime, options) {
|
|
|
335
395
|
throw new Error(formatLocalPostinstallFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
|
|
336
396
|
}
|
|
337
397
|
}
|
|
398
|
+
export async function ensureNpmSourceDevDependencies(runtime, options) {
|
|
399
|
+
if (runtime.source !== 'npm') {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
let taskStarted = false;
|
|
403
|
+
try {
|
|
404
|
+
const packageJson = await readPackageJson(runtime.projectRoot);
|
|
405
|
+
const appVersion = getStringDependency(packageJson, 'dependencies', '@nocobase/app');
|
|
406
|
+
const devtoolsVersion = getStringDependency(packageJson, 'devDependencies', '@nocobase/devtools');
|
|
407
|
+
let updatedPackageJson = false;
|
|
408
|
+
if (!devtoolsVersion) {
|
|
409
|
+
if (!appVersion) {
|
|
410
|
+
throw new Error('Cannot determine @nocobase/devtools version because dependencies["@nocobase/app"] is missing.');
|
|
411
|
+
}
|
|
412
|
+
const devDependencies = ensureDevDependencies(packageJson);
|
|
413
|
+
devDependencies['@nocobase/devtools'] = appVersion;
|
|
414
|
+
updatedPackageJson = true;
|
|
415
|
+
await writeFile(path.join(runtime.projectRoot, 'package.json'), `${JSON.stringify(packageJson, null, 2)}\n`, 'utf-8');
|
|
416
|
+
}
|
|
417
|
+
const needsInstall = updatedPackageJson || !hasNpmSourceDevtools(runtime.projectRoot);
|
|
418
|
+
if (!needsInstall) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
options?.onStartTask?.(`Preparing source dev dependencies for "${runtime.envName}"...`);
|
|
422
|
+
taskStarted = true;
|
|
423
|
+
await run('yarn', ['install'], {
|
|
424
|
+
cwd: runtime.projectRoot,
|
|
425
|
+
env: npmRegistryEnv(runtime),
|
|
426
|
+
errorName: 'yarn install',
|
|
427
|
+
stdio: commandStdio(options?.verbose),
|
|
428
|
+
});
|
|
429
|
+
options?.onSucceedTask?.(`Source dev dependencies are ready for "${runtime.envName}".`);
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
if (taskStarted) {
|
|
433
|
+
options?.onFailTask?.(`Failed to prepare source dev dependencies for "${runtime.envName}".`);
|
|
434
|
+
}
|
|
435
|
+
throw new Error(formatNpmSourceDevDependenciesFailure(runtime.envName, runtime.projectRoot, error instanceof Error ? error.message : String(error)));
|
|
436
|
+
}
|
|
437
|
+
}
|
package/dist/lib/auth-store.js
CHANGED
|
@@ -123,13 +123,19 @@ function normalizeAuthConfig(config) {
|
|
|
123
123
|
},
|
|
124
124
|
}
|
|
125
125
|
: {}),
|
|
126
|
-
...(settings.bin?.docker ||
|
|
126
|
+
...(settings.bin?.docker ||
|
|
127
|
+
settings.bin?.caddy ||
|
|
128
|
+
settings.bin?.git ||
|
|
129
|
+
settings.bin?.nginx ||
|
|
130
|
+
settings.bin?.pnpm ||
|
|
131
|
+
settings.bin?.yarn
|
|
127
132
|
? {
|
|
128
133
|
bin: {
|
|
129
134
|
...(settings.bin?.docker ? { docker: normalizeOptionalString(settings.bin.docker) } : {}),
|
|
130
135
|
...(settings.bin?.caddy ? { caddy: normalizeOptionalString(settings.bin.caddy) } : {}),
|
|
131
136
|
...(settings.bin?.git ? { git: normalizeOptionalString(settings.bin.git) } : {}),
|
|
132
137
|
...(settings.bin?.nginx ? { nginx: normalizeOptionalString(settings.bin.nginx) } : {}),
|
|
138
|
+
...(settings.bin?.pnpm ? { pnpm: normalizeOptionalString(settings.bin.pnpm) } : {}),
|
|
133
139
|
...(settings.bin?.yarn ? { yarn: normalizeOptionalString(settings.bin.yarn) } : {}),
|
|
134
140
|
},
|
|
135
141
|
}
|
package/dist/lib/cli-config.js
CHANGED
|
@@ -16,6 +16,7 @@ export const DEFAULT_DOCKER_BIN = 'docker';
|
|
|
16
16
|
export const DEFAULT_CADDY_BIN = 'caddy';
|
|
17
17
|
export const DEFAULT_GIT_BIN = 'git';
|
|
18
18
|
export const DEFAULT_NGINX_BIN = 'nginx';
|
|
19
|
+
export const DEFAULT_PNPM_BIN = 'pnpm';
|
|
19
20
|
export const PROXY_PROVIDER_OPTIONS = ['nginx', 'caddy'];
|
|
20
21
|
export const DEFAULT_PROXY_PROVIDER = 'nginx';
|
|
21
22
|
export const NGINX_PROXY_DRIVER_OPTIONS = ['local', 'docker'];
|
|
@@ -40,6 +41,7 @@ export const SUPPORTED_CLI_CONFIG_KEYS = [
|
|
|
40
41
|
'bin.caddy',
|
|
41
42
|
'bin.git',
|
|
42
43
|
'bin.nginx',
|
|
44
|
+
'bin.pnpm',
|
|
43
45
|
'proxy.nb-cli-root',
|
|
44
46
|
'proxy.caddy-driver',
|
|
45
47
|
'proxy.nginx-driver',
|
|
@@ -136,6 +138,7 @@ function pruneSettings(config) {
|
|
|
136
138
|
!trimValue(bin.caddy) &&
|
|
137
139
|
!trimValue(bin.git) &&
|
|
138
140
|
!trimValue(bin.nginx) &&
|
|
141
|
+
!trimValue(bin.pnpm) &&
|
|
139
142
|
!trimValue(bin.yarn)) {
|
|
140
143
|
delete config.settings?.bin;
|
|
141
144
|
}
|
|
@@ -187,6 +190,8 @@ export function getExplicitCliConfigValue(config, key) {
|
|
|
187
190
|
return trimValue(config.settings?.bin?.git);
|
|
188
191
|
case 'bin.nginx':
|
|
189
192
|
return trimValue(config.settings?.bin?.nginx);
|
|
193
|
+
case 'bin.pnpm':
|
|
194
|
+
return trimValue(config.settings?.bin?.pnpm);
|
|
190
195
|
case 'proxy.nb-cli-root':
|
|
191
196
|
return trimValue(config.settings?.proxy?.nbCliRoot);
|
|
192
197
|
case 'proxy.caddy-driver':
|
|
@@ -233,6 +238,8 @@ export function getEffectiveCliConfigValue(config, key) {
|
|
|
233
238
|
return DEFAULT_GIT_BIN;
|
|
234
239
|
case 'bin.nginx':
|
|
235
240
|
return DEFAULT_NGINX_BIN;
|
|
241
|
+
case 'bin.pnpm':
|
|
242
|
+
return DEFAULT_PNPM_BIN;
|
|
236
243
|
case 'proxy.nb-cli-root':
|
|
237
244
|
return explicit ?? resolveCliHomeRoot();
|
|
238
245
|
case 'proxy.caddy-driver':
|
|
@@ -387,6 +394,12 @@ export async function setCliConfigValue(key, value, options = {}) {
|
|
|
387
394
|
nginx: normalized,
|
|
388
395
|
};
|
|
389
396
|
break;
|
|
397
|
+
case 'bin.pnpm':
|
|
398
|
+
config.settings.bin = {
|
|
399
|
+
...(config.settings.bin ?? {}),
|
|
400
|
+
pnpm: normalized,
|
|
401
|
+
};
|
|
402
|
+
break;
|
|
390
403
|
case 'proxy.nb-cli-root':
|
|
391
404
|
config.settings.proxy = {
|
|
392
405
|
...(config.settings.proxy ?? {}),
|
|
@@ -496,6 +509,11 @@ export async function deleteCliConfigValue(key, options = {}) {
|
|
|
496
509
|
delete config.settings.bin.nginx;
|
|
497
510
|
}
|
|
498
511
|
break;
|
|
512
|
+
case 'bin.pnpm':
|
|
513
|
+
if (config.settings.bin) {
|
|
514
|
+
delete config.settings.bin.pnpm;
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
499
517
|
case 'proxy.nb-cli-root':
|
|
500
518
|
if (config.settings.proxy) {
|
|
501
519
|
delete config.settings.proxy.nbCliRoot;
|
|
@@ -556,6 +574,7 @@ const CONFIGURABLE_COMMAND_KEYS = {
|
|
|
556
574
|
caddy: 'bin.caddy',
|
|
557
575
|
git: 'bin.git',
|
|
558
576
|
nginx: 'bin.nginx',
|
|
577
|
+
pnpm: 'bin.pnpm',
|
|
559
578
|
yarn: 'bin.yarn',
|
|
560
579
|
};
|
|
561
580
|
export function isConfigurableCommandName(value) {
|
package/dist/lib/env-config.js
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
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 { copyFile, mkdir } from 'node:fs/promises';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
export const ENV_HOOK_SCRIPT_CONFIG_PATH = '.nb/hooks.mjs';
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const { spawn } = require('node:child_process');
|
|
15
|
+
function trimValue(value) {
|
|
16
|
+
return String(value ?? '').trim();
|
|
17
|
+
}
|
|
18
|
+
function normalizeHookPhase(value) {
|
|
19
|
+
const text = trimValue(value);
|
|
20
|
+
if (text === 'init' || text === 'upgrade' || text === 'restore' || text === 'source-download' || text === 'app-start') {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
return 'init';
|
|
24
|
+
}
|
|
25
|
+
function normalizeHookCommand(value) {
|
|
26
|
+
const text = trimValue(value);
|
|
27
|
+
if (text === 'source:download' || text === 'app:start' || text === 'app:restart' || text === 'app:upgrade') {
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
return 'init';
|
|
31
|
+
}
|
|
32
|
+
function normalizeHookSource(value) {
|
|
33
|
+
const text = trimValue(value);
|
|
34
|
+
if (text === 'npm' || text === 'git' || text === 'docker') {
|
|
35
|
+
return text;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
function isDependencyHookSource(source) {
|
|
40
|
+
return source === 'npm' || source === 'git';
|
|
41
|
+
}
|
|
42
|
+
function isRecord(value) {
|
|
43
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
export function resolveHookScriptPath(params) {
|
|
46
|
+
const hookScript = trimValue(params.hookScript);
|
|
47
|
+
if (!hookScript) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (path.isAbsolute(hookScript)) {
|
|
51
|
+
return hookScript;
|
|
52
|
+
}
|
|
53
|
+
const appPath = trimValue(params.appPath);
|
|
54
|
+
if (!appPath) {
|
|
55
|
+
return hookScript;
|
|
56
|
+
}
|
|
57
|
+
const usesWindowsSeparators = appPath.includes('\\') || /^[a-zA-Z]:([\\/]|$)/.test(appPath) || appPath.startsWith('\\\\');
|
|
58
|
+
return usesWindowsSeparators ? path.win32.join(appPath, hookScript) : path.posix.join(appPath, hookScript);
|
|
59
|
+
}
|
|
60
|
+
export async function persistHookScript(params) {
|
|
61
|
+
const sourcePath = path.resolve(params.sourcePath);
|
|
62
|
+
const targetPath = path.join(params.appPath, ENV_HOOK_SCRIPT_CONFIG_PATH);
|
|
63
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
64
|
+
if (path.resolve(sourcePath) !== path.resolve(targetPath)) {
|
|
65
|
+
await copyFile(sourcePath, targetPath);
|
|
66
|
+
}
|
|
67
|
+
return ENV_HOOK_SCRIPT_CONFIG_PATH;
|
|
68
|
+
}
|
|
69
|
+
const hookRunnerScript = `
|
|
70
|
+
import { pathToFileURL } from 'node:url';
|
|
71
|
+
|
|
72
|
+
const knownHookNames = ['beforeDependencyInstall', 'beforeAppInstall', 'afterAppStart'];
|
|
73
|
+
const [, hookScriptPath, hookName, contextJson] = process.argv;
|
|
74
|
+
const url = pathToFileURL(hookScriptPath);
|
|
75
|
+
url.searchParams.set('t', String(Date.now()));
|
|
76
|
+
|
|
77
|
+
const imported = await import(url.href);
|
|
78
|
+
const hooks = imported.default ?? imported;
|
|
79
|
+
|
|
80
|
+
if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) {
|
|
81
|
+
throw new Error('Hook script must export an object.');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const knownHookName of knownHookNames) {
|
|
85
|
+
if (Object.prototype.hasOwnProperty.call(hooks, knownHookName) && typeof hooks[knownHookName] !== 'function') {
|
|
86
|
+
throw new Error(\`Hook "\${knownHookName}" must be a function.\`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const hook = hooks[hookName];
|
|
91
|
+
if (typeof hook === 'function') {
|
|
92
|
+
await hook(JSON.parse(contextJson));
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
async function runHookInSubprocess(params) {
|
|
96
|
+
await new Promise((resolve, reject) => {
|
|
97
|
+
const child = spawn(process.execPath, ['--input-type=module', '--eval', hookRunnerScript, params.hookScriptPath, params.hookName, JSON.stringify(params.context)], {
|
|
98
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
99
|
+
});
|
|
100
|
+
let stdout = '';
|
|
101
|
+
let stderr = '';
|
|
102
|
+
child.stdout?.on?.('data', (chunk) => {
|
|
103
|
+
stdout += String(chunk);
|
|
104
|
+
});
|
|
105
|
+
child.stderr?.on?.('data', (chunk) => {
|
|
106
|
+
stderr += String(chunk);
|
|
107
|
+
});
|
|
108
|
+
child.once('error', reject);
|
|
109
|
+
child.once('close', (code) => {
|
|
110
|
+
if (code === 0) {
|
|
111
|
+
resolve();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const output = stderr.trim() || stdout.trim();
|
|
115
|
+
reject(new Error(output || `Hook process exited with code ${code ?? 'unknown'}.`));
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export function buildHookContext(params) {
|
|
120
|
+
const source = normalizeHookSource(params.source);
|
|
121
|
+
if (!source) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const version = trimValue(params.version);
|
|
125
|
+
return {
|
|
126
|
+
phase: normalizeHookPhase(params.phase),
|
|
127
|
+
command: normalizeHookCommand(params.command),
|
|
128
|
+
envName: trimValue(params.envName),
|
|
129
|
+
source,
|
|
130
|
+
...(version ? { version } : {}),
|
|
131
|
+
appPath: params.appPath,
|
|
132
|
+
sourcePath: params.sourcePath,
|
|
133
|
+
storagePath: params.storagePath,
|
|
134
|
+
hookScript: params.hookScript,
|
|
135
|
+
envConfig: { ...(params.envConfig ?? {}) },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function buildBeforeDependencyInstallHookContext(params) {
|
|
139
|
+
const context = buildHookContext(params);
|
|
140
|
+
if (!context || !isDependencyHookSource(context.source)) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return context;
|
|
144
|
+
}
|
|
145
|
+
export async function runHookScriptHook(params) {
|
|
146
|
+
try {
|
|
147
|
+
await runHookInSubprocess(params);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
151
|
+
throw new Error([`Hook script failed: ${params.hookScriptPath}`, `Hook stage: ${params.hookName}`, `Details: ${message}`].join('\n'));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export async function runBeforeDependencyInstallHook(params) {
|
|
155
|
+
await runHookScriptHook({
|
|
156
|
+
hookScriptPath: params.hookScriptPath,
|
|
157
|
+
hookName: 'beforeDependencyInstall',
|
|
158
|
+
context: params.context,
|
|
159
|
+
});
|
|
160
|
+
}
|
package/dist/lib/run-npm.js
CHANGED