@nocobase/cli 2.1.0-beta.9 → 2.1.0
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/assets/env-proxy/nginx/app.conf.tpl +23 -0
- package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
- package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
- package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
- package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
- package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
- package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +145 -0
- package/bin/session-env.js +39 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/app/autostart/disable.js +55 -0
- package/dist/commands/app/autostart/enable.js +55 -0
- package/dist/commands/app/autostart/list.js +37 -0
- package/dist/commands/app/autostart/run.js +84 -0
- package/dist/commands/app/autostart/shared.js +49 -0
- package/dist/commands/app/destroy.js +234 -0
- package/dist/commands/app/down.js +71 -0
- package/dist/commands/app/logs.js +115 -0
- package/dist/commands/app/restart.js +229 -0
- package/dist/commands/app/shared.js +123 -0
- package/dist/commands/app/start.js +416 -0
- package/dist/commands/app/stop.js +183 -0
- package/dist/commands/app/upgrade.js +523 -0
- 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/{src/cli.js → dist/commands/build.js} +4 -11
- package/dist/commands/config/delete.js +42 -0
- package/dist/commands/config/get.js +39 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +49 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +47 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +86 -0
- package/dist/commands/db/stop.js +71 -0
- package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
- package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
- package/dist/commands/download.js +13 -0
- package/dist/commands/env/add.js +406 -0
- package/dist/commands/env/auth.js +189 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +202 -0
- package/dist/commands/env/list.js +43 -0
- package/dist/commands/env/remove.js +174 -0
- package/dist/commands/env/shared.js +204 -0
- package/dist/commands/env/status.js +93 -0
- package/dist/commands/env/update.js +448 -0
- package/dist/commands/env/use.js +38 -0
- package/dist/commands/examples/prompts-stages.js +150 -0
- package/dist/commands/examples/prompts-test.js +181 -0
- package/dist/commands/init.js +1390 -0
- package/dist/commands/install.js +2609 -0
- package/dist/commands/license/activate.js +179 -0
- package/dist/commands/license/env.js +94 -0
- package/dist/commands/license/generate-id.js +108 -0
- package/dist/commands/license/id.js +70 -0
- package/dist/commands/license/index.js +20 -0
- package/dist/commands/license/plugins/clean.js +115 -0
- package/dist/commands/license/plugins/index.js +20 -0
- package/dist/commands/license/plugins/list.js +64 -0
- package/dist/commands/license/plugins/shared.js +382 -0
- package/dist/commands/license/plugins/sync.js +314 -0
- package/dist/commands/license/shared.js +423 -0
- package/dist/commands/license/status.js +64 -0
- package/dist/commands/logs.js +12 -0
- package/dist/commands/plugin/disable.js +86 -0
- package/dist/commands/plugin/enable.js +86 -0
- package/dist/commands/plugin/import.js +108 -0
- package/dist/commands/plugin/list.js +82 -0
- package/dist/commands/pm/disable.js +12 -0
- package/dist/commands/pm/enable.js +12 -0
- package/dist/commands/pm/list.js +12 -0
- package/dist/commands/proxy/caddy/current.js +17 -0
- package/dist/commands/proxy/caddy/generate.js +69 -0
- package/dist/commands/proxy/caddy/index.js +28 -0
- package/dist/commands/proxy/caddy/info.js +31 -0
- package/dist/commands/proxy/caddy/reload.js +30 -0
- package/dist/commands/proxy/caddy/restart.js +28 -0
- package/dist/commands/proxy/caddy/start.js +30 -0
- package/dist/commands/proxy/caddy/status.js +19 -0
- package/dist/commands/proxy/caddy/stop.js +30 -0
- package/dist/commands/proxy/caddy/use.js +26 -0
- package/dist/commands/proxy/index.js +28 -0
- package/dist/commands/proxy/nginx/current.js +18 -0
- package/dist/commands/proxy/nginx/generate.js +68 -0
- package/dist/commands/proxy/nginx/index.js +28 -0
- package/dist/commands/proxy/nginx/info.js +34 -0
- package/dist/commands/proxy/nginx/reload.js +30 -0
- package/dist/commands/proxy/nginx/restart.js +28 -0
- package/dist/commands/proxy/nginx/start.js +30 -0
- package/dist/commands/proxy/nginx/status.js +19 -0
- package/dist/commands/proxy/nginx/stop.js +30 -0
- package/dist/commands/proxy/nginx/use.js +31 -0
- package/dist/commands/restart.js +12 -0
- package/dist/commands/revision/create.js +118 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +152 -0
- package/dist/commands/session/id.js +24 -0
- package/dist/commands/session/remove.js +57 -0
- package/dist/commands/session/setup.js +62 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +80 -0
- package/dist/commands/skills/remove.js +80 -0
- package/dist/commands/skills/update.js +87 -0
- package/dist/commands/source/build.js +58 -0
- package/dist/commands/source/dev.js +182 -0
- package/dist/commands/source/download.js +884 -0
- 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/source/test.js +476 -0
- package/dist/commands/start.js +12 -0
- package/dist/commands/stop.js +12 -0
- package/dist/commands/test.js +12 -0
- package/dist/commands/upgrade.js +12 -0
- package/dist/commands/v1.js +210 -0
- package/dist/generated/command-registry.js +134 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +335 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +139 -0
- package/dist/lib/app-managed-resources.js +337 -0
- package/dist/lib/app-public-path.js +80 -0
- package/dist/lib/app-runtime.js +189 -0
- package/dist/lib/auth-store.js +528 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +409 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +569 -0
- package/dist/lib/cli-entry-error.js +52 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +141 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/command-log.js +284 -0
- package/dist/lib/db-connection-check.js +219 -0
- package/dist/lib/docker-env-file.js +60 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +963 -0
- package/dist/lib/env-command-config.js +45 -0
- package/dist/lib/env-config.js +108 -0
- package/dist/lib/env-guard.js +61 -0
- package/dist/lib/env-paths.js +101 -0
- package/dist/lib/env-proxy.js +1325 -0
- package/dist/lib/generated-command.js +203 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +243 -0
- package/dist/lib/managed-env-file.js +101 -0
- package/dist/lib/managed-init-env.js +32 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/plugin-import.js +279 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +186 -0
- package/dist/lib/prompt-catalog-terminal.js +374 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +278 -0
- package/dist/lib/prompt-web-ui.js +2234 -0
- package/dist/lib/proxy-caddy.js +274 -0
- package/dist/lib/proxy-nginx.js +330 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +429 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +498 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +301 -0
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/dist/lib/skills-manager.js +438 -0
- package/dist/lib/source-publish.js +326 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +309 -0
- package/dist/lib/ui.js +159 -0
- package/dist/locale/en-US.json +526 -0
- package/dist/locale/zh-CN.json +526 -0
- package/dist/post-processors/data-modeling.js +84 -0
- package/dist/post-processors/data-source-manager.js +138 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +388 -0
- package/package.json +128 -24
- package/scripts/build.mjs +34 -0
- package/scripts/clean.mjs +9 -0
- package/tsconfig.json +19 -0
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,429 @@
|
|
|
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
|
+
/**
|
|
10
|
+
* This file is part of the NocoBase (R) project.
|
|
11
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
12
|
+
* Authors: NocoBase Team.
|
|
13
|
+
*
|
|
14
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
15
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
16
|
+
*/
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import fsp from 'node:fs/promises';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import spawn from 'cross-spawn';
|
|
22
|
+
import { translateCli } from './cli-locale.js';
|
|
23
|
+
import { resolveConfiguredCommandName } from './cli-config.js';
|
|
24
|
+
const FORWARDED_SIGNALS = ['SIGINT', 'SIGTERM'];
|
|
25
|
+
const PROCESS_TIMEOUT_FORCE_KILL_DELAY_MS = 1000;
|
|
26
|
+
const MISSING_COMMAND_SPECS = {
|
|
27
|
+
docker: {
|
|
28
|
+
displayName: 'Docker',
|
|
29
|
+
configKey: 'bin.docker',
|
|
30
|
+
},
|
|
31
|
+
caddy: {
|
|
32
|
+
displayName: 'Caddy',
|
|
33
|
+
configKey: 'bin.caddy',
|
|
34
|
+
},
|
|
35
|
+
git: {
|
|
36
|
+
displayName: 'Git',
|
|
37
|
+
configKey: 'bin.git',
|
|
38
|
+
},
|
|
39
|
+
nginx: {
|
|
40
|
+
displayName: 'Nginx',
|
|
41
|
+
configKey: 'bin.nginx',
|
|
42
|
+
},
|
|
43
|
+
yarn: {
|
|
44
|
+
displayName: 'Yarn',
|
|
45
|
+
configKey: 'bin.yarn',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const DOCKER_DAEMON_UNAVAILABLE_PATTERNS = [
|
|
49
|
+
/cannot connect to the docker daemon/i,
|
|
50
|
+
/is the docker daemon running\?/i,
|
|
51
|
+
/error during connect/i,
|
|
52
|
+
/docker daemon is not running/i,
|
|
53
|
+
];
|
|
54
|
+
async function resolveCommandName(name) {
|
|
55
|
+
return await resolveConfiguredCommandName(name);
|
|
56
|
+
}
|
|
57
|
+
function shouldTeeInheritedOutput(options) {
|
|
58
|
+
return options?.stdio === 'inherit' && Boolean(String(process.env.NB_CLI_ACTIVE_LOG_FILE ?? '').trim());
|
|
59
|
+
}
|
|
60
|
+
function createMissingCommandError(name, label, error) {
|
|
61
|
+
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined;
|
|
62
|
+
if (code !== 'ENOENT') {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
if (!Object.prototype.hasOwnProperty.call(MISSING_COMMAND_SPECS, name)) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const spec = MISSING_COMMAND_SPECS[name];
|
|
69
|
+
return new Error(translateCli('commands.shared.missingCommand', { action: label, displayName: spec.displayName, configKey: spec.configKey }, {
|
|
70
|
+
fallback: `Couldn't run \`${label}\` because the ${spec.displayName} executable could not be found. Install ${spec.displayName} or update \`nb config set ${spec.configKey} <path>\` and try again.`,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function isDockerDaemonUnavailableError(error) {
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
+
return DOCKER_DAEMON_UNAVAILABLE_PATTERNS.some((pattern) => pattern.test(message));
|
|
76
|
+
}
|
|
77
|
+
function createDockerDaemonUnavailableError(action, error) {
|
|
78
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
79
|
+
return new Error(translateCli('commands.shared.dockerDaemonUnavailable', { action, details }, {
|
|
80
|
+
fallback: `Couldn't run \`${action}\` because Docker is installed but the Docker daemon is not running.` +
|
|
81
|
+
` Start Docker Desktop or the Docker daemon service, then try again. Details: ${details}`,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
function pathExists(candidate) {
|
|
85
|
+
try {
|
|
86
|
+
return Boolean(candidate) && fs.statSync(candidate) !== undefined;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function isDirectory(candidate) {
|
|
93
|
+
try {
|
|
94
|
+
return Boolean(candidate) && fs.statSync(candidate).isDirectory();
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function hasLocalNocoBaseBinary(candidate) {
|
|
101
|
+
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1')) ||
|
|
102
|
+
pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1.cmd')));
|
|
103
|
+
}
|
|
104
|
+
export function resolveCwd(cwd) {
|
|
105
|
+
const next = cwd ?? process.cwd();
|
|
106
|
+
if (path.isAbsolute(next)) {
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
return path.resolve(process.cwd(), next);
|
|
110
|
+
}
|
|
111
|
+
export function resolveProjectCwd(cwd) {
|
|
112
|
+
const normalizedCwd = typeof cwd === 'string' && cwd.trim() === '' ? undefined : cwd;
|
|
113
|
+
const fallback = resolveCwd(normalizedCwd);
|
|
114
|
+
const hasExplicitInput = normalizedCwd !== undefined;
|
|
115
|
+
if (hasExplicitInput && !pathExists(fallback)) {
|
|
116
|
+
throw new Error(`The specified --cwd does not exist: ${fallback}`);
|
|
117
|
+
}
|
|
118
|
+
if (hasExplicitInput && !isDirectory(fallback)) {
|
|
119
|
+
throw new Error(`The specified --cwd is not a directory: ${fallback}`);
|
|
120
|
+
}
|
|
121
|
+
let current = hasExplicitInput ? fallback : process.cwd();
|
|
122
|
+
while (!hasLocalNocoBaseBinary(current)) {
|
|
123
|
+
const parent = path.dirname(current);
|
|
124
|
+
if (parent === current) {
|
|
125
|
+
if (hasExplicitInput) {
|
|
126
|
+
throw new Error(`Couldn't find a NocoBase source project from --cwd: ${fallback}`);
|
|
127
|
+
}
|
|
128
|
+
return fallback;
|
|
129
|
+
}
|
|
130
|
+
current = parent;
|
|
131
|
+
}
|
|
132
|
+
return current;
|
|
133
|
+
}
|
|
134
|
+
export async function run(name, args, options) {
|
|
135
|
+
const cwd = resolveCwd(options?.cwd);
|
|
136
|
+
const label = options?.errorName ?? name;
|
|
137
|
+
const command = await resolveCommandName(name);
|
|
138
|
+
const stdio = shouldTeeInheritedOutput(options)
|
|
139
|
+
? ['inherit', 'pipe', 'pipe']
|
|
140
|
+
: (options?.stdio ?? 'inherit');
|
|
141
|
+
return await new Promise((resolve, reject) => {
|
|
142
|
+
const child = spawn(command, [...args], {
|
|
143
|
+
stdio,
|
|
144
|
+
cwd,
|
|
145
|
+
env: {
|
|
146
|
+
...process.env,
|
|
147
|
+
...options?.env,
|
|
148
|
+
},
|
|
149
|
+
windowsHide: process.platform === 'win32',
|
|
150
|
+
});
|
|
151
|
+
if (options?.stdio === 'pipe' || shouldTeeInheritedOutput(options)) {
|
|
152
|
+
child.stdout?.setEncoding('utf8');
|
|
153
|
+
child.stderr?.setEncoding('utf8');
|
|
154
|
+
child.stdout?.on('data', (chunk) => {
|
|
155
|
+
if (shouldTeeInheritedOutput(options)) {
|
|
156
|
+
process.stdout.write(chunk);
|
|
157
|
+
}
|
|
158
|
+
options.onStdout?.(String(chunk));
|
|
159
|
+
});
|
|
160
|
+
child.stderr?.on('data', (chunk) => {
|
|
161
|
+
if (shouldTeeInheritedOutput(options)) {
|
|
162
|
+
process.stderr.write(chunk);
|
|
163
|
+
}
|
|
164
|
+
options.onStderr?.(String(chunk));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const cleanupSignalForwarding = forwardSignalsToChild(child);
|
|
168
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
169
|
+
child.once('error', (error) => {
|
|
170
|
+
timeoutController.cleanup();
|
|
171
|
+
cleanupSignalForwarding();
|
|
172
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
173
|
+
});
|
|
174
|
+
child.once('close', (code, signal) => {
|
|
175
|
+
timeoutController.cleanup();
|
|
176
|
+
cleanupSignalForwarding();
|
|
177
|
+
if (timeoutController.didTimeout()) {
|
|
178
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (code === 0) {
|
|
182
|
+
resolve();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (signal) {
|
|
186
|
+
reject(new Error(`${label} exited due to signal ${signal}`));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
reject(new Error(`${label} exited with code ${code}`));
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function attachProcessTimeout(child, timeoutMs) {
|
|
194
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
195
|
+
return {
|
|
196
|
+
cleanup: () => undefined,
|
|
197
|
+
didTimeout: () => false,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
let didTimeout = false;
|
|
201
|
+
let forceKillTimer;
|
|
202
|
+
const isChildRunning = () => child.exitCode === null && child.signalCode === null;
|
|
203
|
+
const terminateChild = (signal) => {
|
|
204
|
+
if (!isChildRunning()) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
child.kill(signal);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Ignore kill errors here and let the child close/error handlers surface the failure.
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const timeoutTimer = setTimeout(() => {
|
|
215
|
+
didTimeout = true;
|
|
216
|
+
terminateChild('SIGTERM');
|
|
217
|
+
forceKillTimer = setTimeout(() => {
|
|
218
|
+
terminateChild('SIGKILL');
|
|
219
|
+
}, PROCESS_TIMEOUT_FORCE_KILL_DELAY_MS);
|
|
220
|
+
forceKillTimer.unref?.();
|
|
221
|
+
}, timeoutMs);
|
|
222
|
+
timeoutTimer.unref?.();
|
|
223
|
+
return {
|
|
224
|
+
cleanup: () => {
|
|
225
|
+
clearTimeout(timeoutTimer);
|
|
226
|
+
if (forceKillTimer) {
|
|
227
|
+
clearTimeout(forceKillTimer);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
didTimeout: () => didTimeout,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function forwardSignalsToChild(child) {
|
|
234
|
+
let forwardedSignalCount = 0;
|
|
235
|
+
const listeners = new Map();
|
|
236
|
+
const isChildRunning = () => child.exitCode === null && child.signalCode === null;
|
|
237
|
+
for (const signal of FORWARDED_SIGNALS) {
|
|
238
|
+
const listener = () => {
|
|
239
|
+
if (!isChildRunning()) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const nextSignal = forwardedSignalCount > 0 ? 'SIGKILL' : signal;
|
|
243
|
+
forwardedSignalCount += 1;
|
|
244
|
+
try {
|
|
245
|
+
child.kill(nextSignal);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Ignore kill errors here and let the child close/error handlers surface the failure.
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
listeners.set(signal, listener);
|
|
252
|
+
process.on(signal, listener);
|
|
253
|
+
}
|
|
254
|
+
return () => {
|
|
255
|
+
for (const [signal, listener] of listeners) {
|
|
256
|
+
process.off(signal, listener);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export async function commandSucceeds(name, args, options) {
|
|
261
|
+
const cwd = resolveCwd(options?.cwd);
|
|
262
|
+
const label = options?.errorName ?? name;
|
|
263
|
+
const command = await resolveCommandName(name);
|
|
264
|
+
return await new Promise((resolve, reject) => {
|
|
265
|
+
const child = spawn(command, [...args], {
|
|
266
|
+
cwd,
|
|
267
|
+
env: {
|
|
268
|
+
...process.env,
|
|
269
|
+
...options?.env,
|
|
270
|
+
},
|
|
271
|
+
stdio: 'ignore',
|
|
272
|
+
windowsHide: process.platform === 'win32',
|
|
273
|
+
});
|
|
274
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
275
|
+
child.once('error', (error) => {
|
|
276
|
+
timeoutController.cleanup();
|
|
277
|
+
const missingCommandError = createMissingCommandError(name, label, error);
|
|
278
|
+
if (missingCommandError) {
|
|
279
|
+
reject(missingCommandError);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
resolve(false);
|
|
283
|
+
});
|
|
284
|
+
child.once('close', (code) => {
|
|
285
|
+
timeoutController.cleanup();
|
|
286
|
+
if (timeoutController.didTimeout()) {
|
|
287
|
+
resolve(false);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
resolve(code === 0);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
export async function commandOutput(name, args, options) {
|
|
295
|
+
const cwd = resolveCwd(options?.cwd);
|
|
296
|
+
const label = options?.errorName ?? name;
|
|
297
|
+
const command = await resolveCommandName(name);
|
|
298
|
+
return await new Promise((resolve, reject) => {
|
|
299
|
+
const child = spawn(command, [...args], {
|
|
300
|
+
cwd,
|
|
301
|
+
env: {
|
|
302
|
+
...process.env,
|
|
303
|
+
...options?.env,
|
|
304
|
+
},
|
|
305
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
306
|
+
windowsHide: process.platform === 'win32',
|
|
307
|
+
});
|
|
308
|
+
let stdout = '';
|
|
309
|
+
let stderr = '';
|
|
310
|
+
child.stdout.setEncoding('utf8');
|
|
311
|
+
child.stderr.setEncoding('utf8');
|
|
312
|
+
child.stdout.on('data', (chunk) => {
|
|
313
|
+
stdout += chunk;
|
|
314
|
+
});
|
|
315
|
+
child.stderr.on('data', (chunk) => {
|
|
316
|
+
stderr += chunk;
|
|
317
|
+
});
|
|
318
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
319
|
+
child.once('error', (error) => {
|
|
320
|
+
timeoutController.cleanup();
|
|
321
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
322
|
+
});
|
|
323
|
+
child.once('close', (code, signal) => {
|
|
324
|
+
timeoutController.cleanup();
|
|
325
|
+
if (timeoutController.didTimeout()) {
|
|
326
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (code === 0) {
|
|
330
|
+
resolve(stdout.trim());
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (signal) {
|
|
334
|
+
reject(new Error(`${label} exited due to signal ${signal}`));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const details = stderr.trim() || stdout.trim();
|
|
338
|
+
reject(new Error(details ? `${label} exited with code ${code}: ${details}` : `${label} exited with code ${code}`));
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async function readCommandOutputFile(filePath) {
|
|
343
|
+
try {
|
|
344
|
+
return await fsp.readFile(filePath, 'utf8');
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return '';
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
export async function commandOutputViaFile(name, args, options) {
|
|
351
|
+
const cwd = resolveCwd(options?.cwd);
|
|
352
|
+
const label = options?.errorName ?? name;
|
|
353
|
+
const command = await resolveCommandName(name);
|
|
354
|
+
const captureDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'nocobase-cli-output-'));
|
|
355
|
+
const stdoutPath = path.join(captureDir, 'stdout.log');
|
|
356
|
+
const stderrPath = path.join(captureDir, 'stderr.log');
|
|
357
|
+
const stdoutHandle = await fsp.open(stdoutPath, 'w');
|
|
358
|
+
const stderrHandle = await fsp.open(stderrPath, 'w');
|
|
359
|
+
try {
|
|
360
|
+
const result = await new Promise((resolve, reject) => {
|
|
361
|
+
const child = spawn(command, [...args], {
|
|
362
|
+
cwd,
|
|
363
|
+
env: {
|
|
364
|
+
...process.env,
|
|
365
|
+
...options?.env,
|
|
366
|
+
},
|
|
367
|
+
stdio: ['ignore', stdoutHandle.fd, stderrHandle.fd],
|
|
368
|
+
windowsHide: process.platform === 'win32',
|
|
369
|
+
});
|
|
370
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
371
|
+
child.once('error', (error) => {
|
|
372
|
+
timeoutController.cleanup();
|
|
373
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
374
|
+
});
|
|
375
|
+
child.once('close', (code, signal) => {
|
|
376
|
+
timeoutController.cleanup();
|
|
377
|
+
if (timeoutController.didTimeout()) {
|
|
378
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
resolve({ code, signal });
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
await stdoutHandle.close();
|
|
385
|
+
await stderrHandle.close();
|
|
386
|
+
const stdout = await readCommandOutputFile(stdoutPath);
|
|
387
|
+
const stderr = await readCommandOutputFile(stderrPath);
|
|
388
|
+
if (result.code === 0) {
|
|
389
|
+
return stdout.trim();
|
|
390
|
+
}
|
|
391
|
+
if (result.signal) {
|
|
392
|
+
throw new Error(`${label} exited due to signal ${result.signal}`);
|
|
393
|
+
}
|
|
394
|
+
const details = stderr.trim() || stdout.trim();
|
|
395
|
+
throw new Error(details ? `${label} exited with code ${result.code}: ${details}` : `${label} exited with code ${result.code}`);
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
await Promise.allSettled([stdoutHandle.close(), stderrHandle.close()]);
|
|
399
|
+
await fsp.rm(captureDir, { recursive: true, force: true });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/** Run `yarn` with the given argument list, inheriting stdio (errors label as `npm` for compatibility). */
|
|
403
|
+
export function runNpm(args, options) {
|
|
404
|
+
return run('yarn', [...args], { ...options, errorName: 'npm' });
|
|
405
|
+
}
|
|
406
|
+
export async function ensureDockerDaemonRunning(action = 'use Docker') {
|
|
407
|
+
try {
|
|
408
|
+
await commandOutput('docker', ['info'], { errorName: 'docker info', timeoutMs: 5000 });
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
if (isDockerDaemonUnavailableError(error)) {
|
|
412
|
+
throw createDockerDaemonUnavailableError(action, error);
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
export function runNocoBaseCommand(args, options) {
|
|
418
|
+
const cwd = resolveProjectCwd(options?.cwd);
|
|
419
|
+
const localBin = path.join(cwd, 'node_modules', '.bin');
|
|
420
|
+
return run('nocobase-v1', [...args], {
|
|
421
|
+
...options,
|
|
422
|
+
cwd,
|
|
423
|
+
errorName: 'nocobase command',
|
|
424
|
+
env: {
|
|
425
|
+
PATH: `${localBin}${path.delimiter}${process.env.PATH ?? ''}`,
|
|
426
|
+
...options?.env,
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { resolveBuiltinDbConnection } from './builtin-db.js';
|
|
10
|
+
function put(out, key, value) {
|
|
11
|
+
if (value === undefined || value === null) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
out[key] = String(value);
|
|
15
|
+
}
|
|
16
|
+
export async function buildRuntimeEnvVars(runtime) {
|
|
17
|
+
const config = runtime.env.config ?? {};
|
|
18
|
+
const out = {
|
|
19
|
+
...runtime.env.envVars,
|
|
20
|
+
};
|
|
21
|
+
if (runtime.kind !== 'local' && runtime.kind !== 'docker') {
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
if (!config.builtinDb) {
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
const connection = await resolveBuiltinDbConnection(runtime);
|
|
28
|
+
put(out, 'DB_DIALECT', connection.dbDialect);
|
|
29
|
+
put(out, 'DB_HOST', connection.dbHost);
|
|
30
|
+
put(out, 'DB_PORT', connection.dbPort);
|
|
31
|
+
return out;
|
|
32
|
+
}
|