@nocobase/cli 2.1.0-beta.35 → 2.1.0-beta.37
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/run.js +3 -2
- package/dist/commands/app/upgrade.js +38 -16
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/dist/commands/config/delete.js +4 -0
- package/dist/commands/config/get.js +4 -0
- package/dist/commands/config/set.js +5 -1
- package/dist/commands/env/add.js +129 -15
- package/dist/commands/env/auth.js +145 -12
- package/dist/commands/env/info.js +52 -8
- package/dist/commands/env/list.js +2 -2
- package/dist/commands/env/shared.js +41 -3
- package/dist/commands/init.js +254 -136
- package/dist/commands/install.js +447 -272
- package/dist/commands/license/activate.js +6 -4
- package/dist/commands/source/publish.js +17 -0
- package/dist/commands/v1.js +210 -0
- package/dist/lib/app-managed-resources.js +20 -1
- package/dist/lib/app-runtime.js +13 -4
- package/dist/lib/auth-store.js +69 -18
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +23 -13
- package/dist/lib/cli-config.js +99 -4
- package/dist/lib/cli-locale.js +19 -7
- package/dist/lib/db-connection-check.js +61 -0
- package/dist/lib/env-auth.js +79 -0
- package/dist/lib/env-config.js +8 -1
- package/dist/lib/prompt-validators.js +23 -5
- package/dist/lib/prompt-web-ui.js +143 -19
- package/dist/lib/run-npm.js +166 -30
- package/dist/lib/skills-manager.js +74 -4
- package/dist/lib/source-publish.js +20 -1
- package/dist/lib/source-registry.js +2 -2
- package/dist/locale/en-US.json +36 -5
- package/dist/locale/zh-CN.json +36 -5
- package/package.json +6 -3
package/dist/lib/run-npm.js
CHANGED
|
@@ -19,9 +19,39 @@ import fsp from 'node:fs/promises';
|
|
|
19
19
|
import os from 'node:os';
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import spawn from 'cross-spawn';
|
|
22
|
+
import { translateCli } from './cli-locale.js';
|
|
23
|
+
import { resolveConfiguredCommandName } from './cli-config.js';
|
|
22
24
|
const FORWARDED_SIGNALS = ['SIGINT', 'SIGTERM'];
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
git: {
|
|
32
|
+
displayName: 'Git',
|
|
33
|
+
configKey: 'bin.git',
|
|
34
|
+
},
|
|
35
|
+
yarn: {
|
|
36
|
+
displayName: 'Yarn',
|
|
37
|
+
configKey: 'bin.yarn',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
async function resolveCommandName(name) {
|
|
41
|
+
return await resolveConfiguredCommandName(name);
|
|
42
|
+
}
|
|
43
|
+
function createMissingCommandError(name, label, error) {
|
|
44
|
+
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined;
|
|
45
|
+
if (code !== 'ENOENT') {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(MISSING_COMMAND_SPECS, name)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const spec = MISSING_COMMAND_SPECS[name];
|
|
52
|
+
return new Error(translateCli('commands.shared.missingCommand', { action: label, displayName: spec.displayName, configKey: spec.configKey }, {
|
|
53
|
+
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.`,
|
|
54
|
+
}));
|
|
25
55
|
}
|
|
26
56
|
function pathExists(candidate) {
|
|
27
57
|
try {
|
|
@@ -31,9 +61,17 @@ function pathExists(candidate) {
|
|
|
31
61
|
return false;
|
|
32
62
|
}
|
|
33
63
|
}
|
|
64
|
+
function isDirectory(candidate) {
|
|
65
|
+
try {
|
|
66
|
+
return Boolean(candidate) && fs.statSync(candidate).isDirectory();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
34
72
|
function hasLocalNocoBaseBinary(candidate) {
|
|
35
|
-
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1'))
|
|
36
|
-
|
|
73
|
+
return (pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1')) ||
|
|
74
|
+
pathExists(path.join(candidate, 'node_modules', '.bin', 'nocobase-v1.cmd')));
|
|
37
75
|
}
|
|
38
76
|
export function resolveCwd(cwd) {
|
|
39
77
|
const next = cwd ?? process.cwd();
|
|
@@ -45,29 +83,31 @@ export function resolveCwd(cwd) {
|
|
|
45
83
|
export function resolveProjectCwd(cwd) {
|
|
46
84
|
const normalizedCwd = typeof cwd === 'string' && cwd.trim() === '' ? undefined : cwd;
|
|
47
85
|
const fallback = resolveCwd(normalizedCwd);
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return candidate;
|
|
58
|
-
}
|
|
86
|
+
const hasExplicitInput = normalizedCwd !== undefined;
|
|
87
|
+
if (hasExplicitInput && !pathExists(fallback)) {
|
|
88
|
+
throw new Error(`The specified --cwd does not exist: ${fallback}`);
|
|
89
|
+
}
|
|
90
|
+
if (hasExplicitInput && !isDirectory(fallback)) {
|
|
91
|
+
throw new Error(`The specified --cwd is not a directory: ${fallback}`);
|
|
92
|
+
}
|
|
93
|
+
let current = hasExplicitInput ? fallback : process.cwd();
|
|
94
|
+
while (!hasLocalNocoBaseBinary(current)) {
|
|
59
95
|
const parent = path.dirname(current);
|
|
60
96
|
if (parent === current) {
|
|
97
|
+
if (hasExplicitInput) {
|
|
98
|
+
throw new Error(`Couldn't find a NocoBase source project from --cwd: ${fallback}`);
|
|
99
|
+
}
|
|
61
100
|
return fallback;
|
|
62
101
|
}
|
|
63
102
|
current = parent;
|
|
64
103
|
}
|
|
104
|
+
return current;
|
|
65
105
|
}
|
|
66
|
-
export function run(name, args, options) {
|
|
106
|
+
export async function run(name, args, options) {
|
|
67
107
|
const cwd = resolveCwd(options?.cwd);
|
|
68
108
|
const label = options?.errorName ?? name;
|
|
69
|
-
const command = resolveCommandName(name);
|
|
70
|
-
return new Promise((resolve, reject) => {
|
|
109
|
+
const command = await resolveCommandName(name);
|
|
110
|
+
return await new Promise((resolve, reject) => {
|
|
71
111
|
const child = spawn(command, [...args], {
|
|
72
112
|
stdio: options?.stdio ?? 'inherit',
|
|
73
113
|
cwd,
|
|
@@ -77,13 +117,34 @@ export function run(name, args, options) {
|
|
|
77
117
|
},
|
|
78
118
|
windowsHide: process.platform === 'win32',
|
|
79
119
|
});
|
|
120
|
+
if (options?.stdio === 'pipe') {
|
|
121
|
+
child.stdout?.setEncoding('utf8');
|
|
122
|
+
child.stderr?.setEncoding('utf8');
|
|
123
|
+
if (options.onStdout) {
|
|
124
|
+
child.stdout?.on('data', (chunk) => {
|
|
125
|
+
options.onStdout?.(String(chunk));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (options.onStderr) {
|
|
129
|
+
child.stderr?.on('data', (chunk) => {
|
|
130
|
+
options.onStderr?.(String(chunk));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
80
134
|
const cleanupSignalForwarding = forwardSignalsToChild(child);
|
|
135
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
81
136
|
child.once('error', (error) => {
|
|
137
|
+
timeoutController.cleanup();
|
|
82
138
|
cleanupSignalForwarding();
|
|
83
|
-
reject(error);
|
|
139
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
84
140
|
});
|
|
85
141
|
child.once('close', (code, signal) => {
|
|
142
|
+
timeoutController.cleanup();
|
|
86
143
|
cleanupSignalForwarding();
|
|
144
|
+
if (timeoutController.didTimeout()) {
|
|
145
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
87
148
|
if (code === 0) {
|
|
88
149
|
resolve();
|
|
89
150
|
return;
|
|
@@ -96,6 +157,46 @@ export function run(name, args, options) {
|
|
|
96
157
|
});
|
|
97
158
|
});
|
|
98
159
|
}
|
|
160
|
+
function attachProcessTimeout(child, timeoutMs) {
|
|
161
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
162
|
+
return {
|
|
163
|
+
cleanup: () => undefined,
|
|
164
|
+
didTimeout: () => false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
let didTimeout = false;
|
|
168
|
+
let forceKillTimer;
|
|
169
|
+
const isChildRunning = () => child.exitCode === null && child.signalCode === null;
|
|
170
|
+
const terminateChild = (signal) => {
|
|
171
|
+
if (!isChildRunning()) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
child.kill(signal);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Ignore kill errors here and let the child close/error handlers surface the failure.
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const timeoutTimer = setTimeout(() => {
|
|
182
|
+
didTimeout = true;
|
|
183
|
+
terminateChild('SIGTERM');
|
|
184
|
+
forceKillTimer = setTimeout(() => {
|
|
185
|
+
terminateChild('SIGKILL');
|
|
186
|
+
}, PROCESS_TIMEOUT_FORCE_KILL_DELAY_MS);
|
|
187
|
+
forceKillTimer.unref?.();
|
|
188
|
+
}, timeoutMs);
|
|
189
|
+
timeoutTimer.unref?.();
|
|
190
|
+
return {
|
|
191
|
+
cleanup: () => {
|
|
192
|
+
clearTimeout(timeoutTimer);
|
|
193
|
+
if (forceKillTimer) {
|
|
194
|
+
clearTimeout(forceKillTimer);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
didTimeout: () => didTimeout,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
99
200
|
function forwardSignalsToChild(child) {
|
|
100
201
|
let forwardedSignalCount = 0;
|
|
101
202
|
const listeners = new Map();
|
|
@@ -123,10 +224,11 @@ function forwardSignalsToChild(child) {
|
|
|
123
224
|
}
|
|
124
225
|
};
|
|
125
226
|
}
|
|
126
|
-
export function commandSucceeds(name, args, options) {
|
|
227
|
+
export async function commandSucceeds(name, args, options) {
|
|
127
228
|
const cwd = resolveCwd(options?.cwd);
|
|
128
|
-
const
|
|
129
|
-
|
|
229
|
+
const label = options?.errorName ?? name;
|
|
230
|
+
const command = await resolveCommandName(name);
|
|
231
|
+
return await new Promise((resolve, reject) => {
|
|
130
232
|
const child = spawn(command, [...args], {
|
|
131
233
|
cwd,
|
|
132
234
|
env: {
|
|
@@ -136,15 +238,31 @@ export function commandSucceeds(name, args, options) {
|
|
|
136
238
|
stdio: 'ignore',
|
|
137
239
|
windowsHide: process.platform === 'win32',
|
|
138
240
|
});
|
|
139
|
-
|
|
140
|
-
child.once('
|
|
241
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
242
|
+
child.once('error', (error) => {
|
|
243
|
+
timeoutController.cleanup();
|
|
244
|
+
const missingCommandError = createMissingCommandError(name, label, error);
|
|
245
|
+
if (missingCommandError) {
|
|
246
|
+
reject(missingCommandError);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
resolve(false);
|
|
250
|
+
});
|
|
251
|
+
child.once('close', (code) => {
|
|
252
|
+
timeoutController.cleanup();
|
|
253
|
+
if (timeoutController.didTimeout()) {
|
|
254
|
+
resolve(false);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
resolve(code === 0);
|
|
258
|
+
});
|
|
141
259
|
});
|
|
142
260
|
}
|
|
143
|
-
export function commandOutput(name, args, options) {
|
|
261
|
+
export async function commandOutput(name, args, options) {
|
|
144
262
|
const cwd = resolveCwd(options?.cwd);
|
|
145
263
|
const label = options?.errorName ?? name;
|
|
146
|
-
const command = resolveCommandName(name);
|
|
147
|
-
return new Promise((resolve, reject) => {
|
|
264
|
+
const command = await resolveCommandName(name);
|
|
265
|
+
return await new Promise((resolve, reject) => {
|
|
148
266
|
const child = spawn(command, [...args], {
|
|
149
267
|
cwd,
|
|
150
268
|
env: {
|
|
@@ -164,8 +282,17 @@ export function commandOutput(name, args, options) {
|
|
|
164
282
|
child.stderr.on('data', (chunk) => {
|
|
165
283
|
stderr += chunk;
|
|
166
284
|
});
|
|
167
|
-
child
|
|
285
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
286
|
+
child.once('error', (error) => {
|
|
287
|
+
timeoutController.cleanup();
|
|
288
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
289
|
+
});
|
|
168
290
|
child.once('close', (code, signal) => {
|
|
291
|
+
timeoutController.cleanup();
|
|
292
|
+
if (timeoutController.didTimeout()) {
|
|
293
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
169
296
|
if (code === 0) {
|
|
170
297
|
resolve(stdout.trim());
|
|
171
298
|
return;
|
|
@@ -190,7 +317,7 @@ async function readCommandOutputFile(filePath) {
|
|
|
190
317
|
export async function commandOutputViaFile(name, args, options) {
|
|
191
318
|
const cwd = resolveCwd(options?.cwd);
|
|
192
319
|
const label = options?.errorName ?? name;
|
|
193
|
-
const command = resolveCommandName(name);
|
|
320
|
+
const command = await resolveCommandName(name);
|
|
194
321
|
const captureDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'nocobase-cli-output-'));
|
|
195
322
|
const stdoutPath = path.join(captureDir, 'stdout.log');
|
|
196
323
|
const stderrPath = path.join(captureDir, 'stderr.log');
|
|
@@ -207,8 +334,17 @@ export async function commandOutputViaFile(name, args, options) {
|
|
|
207
334
|
stdio: ['ignore', stdoutHandle.fd, stderrHandle.fd],
|
|
208
335
|
windowsHide: process.platform === 'win32',
|
|
209
336
|
});
|
|
210
|
-
child
|
|
337
|
+
const timeoutController = attachProcessTimeout(child, options?.timeoutMs);
|
|
338
|
+
child.once('error', (error) => {
|
|
339
|
+
timeoutController.cleanup();
|
|
340
|
+
reject(createMissingCommandError(name, label, error) ?? error);
|
|
341
|
+
});
|
|
211
342
|
child.once('close', (code, signal) => {
|
|
343
|
+
timeoutController.cleanup();
|
|
344
|
+
if (timeoutController.didTimeout()) {
|
|
345
|
+
reject(new Error(`${label} timed out after ${options?.timeoutMs}ms`));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
212
348
|
resolve({ code, signal });
|
|
213
349
|
});
|
|
214
350
|
});
|
|
@@ -17,6 +17,72 @@ import { commandOutput, commandOutputViaFile, run } from './run-npm.js';
|
|
|
17
17
|
export const NOCOBASE_SKILLS_SOURCE = 'nocobase/skills';
|
|
18
18
|
export const NOCOBASE_SKILLS_PACKAGE_NAME = '@nocobase/skills';
|
|
19
19
|
const NOCOBASE_SKILLS_NAME_PREFIX = 'nocobase-';
|
|
20
|
+
const SKILLS_LIST_TIMEOUT_MS = 5000;
|
|
21
|
+
const SKILLS_NPM_VIEW_TIMEOUT_MS = 3000;
|
|
22
|
+
const SKILLS_PACK_TIMEOUT_MS = 30000;
|
|
23
|
+
const SKILLS_ADD_TIMEOUT_MS = 20000;
|
|
24
|
+
const NPM_REGISTRY_UNAVAILABLE_PATTERNS = [
|
|
25
|
+
'enotfound',
|
|
26
|
+
'eai_again',
|
|
27
|
+
'etimedout',
|
|
28
|
+
'esockettimedout',
|
|
29
|
+
'econnreset',
|
|
30
|
+
'econnrefused',
|
|
31
|
+
'ehostunreach',
|
|
32
|
+
'enetunreach',
|
|
33
|
+
'socket hang up',
|
|
34
|
+
'getaddrinfo',
|
|
35
|
+
'fetch failed',
|
|
36
|
+
'network request to',
|
|
37
|
+
'self_signed_cert',
|
|
38
|
+
'depth_zero_self_signed_cert',
|
|
39
|
+
'unable_to_verify_leaf_signature',
|
|
40
|
+
'cert_has_expired',
|
|
41
|
+
'timed out after',
|
|
42
|
+
];
|
|
43
|
+
function collectErrorMessages(error) {
|
|
44
|
+
const messages = [];
|
|
45
|
+
const queue = [error];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
while (queue.length > 0) {
|
|
48
|
+
const current = queue.shift();
|
|
49
|
+
if (current === undefined || current === null || seen.has(current)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
seen.add(current);
|
|
53
|
+
if (current instanceof Error) {
|
|
54
|
+
if (current.message) {
|
|
55
|
+
messages.push(current.message);
|
|
56
|
+
}
|
|
57
|
+
const cause = current.cause;
|
|
58
|
+
if (cause !== undefined) {
|
|
59
|
+
queue.push(cause);
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (typeof current === 'string') {
|
|
64
|
+
messages.push(current);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (typeof current === 'object') {
|
|
68
|
+
if ('message' in current && typeof current.message === 'string') {
|
|
69
|
+
messages.push(current.message);
|
|
70
|
+
}
|
|
71
|
+
if ('cause' in current) {
|
|
72
|
+
queue.push(current.cause);
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
messages.push(String(current));
|
|
77
|
+
}
|
|
78
|
+
return messages;
|
|
79
|
+
}
|
|
80
|
+
export function isNpmRegistryUnavailable(error) {
|
|
81
|
+
return collectErrorMessages(error).some((message) => {
|
|
82
|
+
const normalized = message.toLowerCase();
|
|
83
|
+
return NPM_REGISTRY_UNAVAILABLE_PATTERNS.some((pattern) => normalized.includes(pattern));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
20
86
|
function normalizePath(value) {
|
|
21
87
|
return path.resolve(value);
|
|
22
88
|
}
|
|
@@ -72,6 +138,7 @@ export async function listGlobalSkills(options = {}) {
|
|
|
72
138
|
const output = await (options.commandOutputFn ?? commandOutputViaFile)('npx', ['-y', 'skills', 'list', '-g', '--json'], {
|
|
73
139
|
cwd: globalRoot,
|
|
74
140
|
errorName: 'skills list',
|
|
141
|
+
timeoutMs: SKILLS_LIST_TIMEOUT_MS,
|
|
75
142
|
});
|
|
76
143
|
const parsed = JSON.parse(output);
|
|
77
144
|
return Array.isArray(parsed) ? parsed : [];
|
|
@@ -95,6 +162,7 @@ async function readPublishedSkillsVersion(options = {}) {
|
|
|
95
162
|
const output = await (options.commandOutputFn ?? commandOutput)('npm', ['view', NOCOBASE_SKILLS_PACKAGE_NAME, 'version', '--json'], {
|
|
96
163
|
cwd: globalRoot,
|
|
97
164
|
errorName: 'npm view',
|
|
165
|
+
timeoutMs: SKILLS_NPM_VIEW_TIMEOUT_MS,
|
|
98
166
|
});
|
|
99
167
|
const parsed = JSON.parse(output);
|
|
100
168
|
const version = String(parsed ?? '').trim();
|
|
@@ -187,6 +255,7 @@ async function prepareLocalSkillsPackage(globalRoot, options = {}, targetVersion
|
|
|
187
255
|
cwd: packRoot,
|
|
188
256
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
189
257
|
errorName: 'npm pack',
|
|
258
|
+
timeoutMs: SKILLS_PACK_TIMEOUT_MS,
|
|
190
259
|
});
|
|
191
260
|
const tarballPath = await resolvePackedSkillsTarball(packRoot);
|
|
192
261
|
await extractPackedSkillsTarball(tarballPath, cacheRoot, targetVersion);
|
|
@@ -276,6 +345,7 @@ async function reinstallManagedSkills(globalRoot, options = {}, targetVersion) {
|
|
|
276
345
|
cwd: globalRoot,
|
|
277
346
|
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
278
347
|
errorName: 'skills add',
|
|
348
|
+
timeoutMs: SKILLS_ADD_TIMEOUT_MS,
|
|
279
349
|
});
|
|
280
350
|
}
|
|
281
351
|
finally {
|
|
@@ -314,10 +384,10 @@ export async function updateNocoBaseSkills(options = {}) {
|
|
|
314
384
|
status,
|
|
315
385
|
};
|
|
316
386
|
}
|
|
317
|
-
if (status.managedByNb
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
387
|
+
if (status.managedByNb &&
|
|
388
|
+
status.latestVersion &&
|
|
389
|
+
status.installedVersion &&
|
|
390
|
+
compareVersions(status.latestVersion, status.installedVersion) <= 0) {
|
|
321
391
|
return {
|
|
322
392
|
action: 'noop',
|
|
323
393
|
reason: 'up-to-date',
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { commandOutput, resolveProjectCwd, run } from './run-npm.js';
|
|
10
|
+
import { commandOutput, resolveProjectCwd, run, runNocoBaseCommand } from './run-npm.js';
|
|
11
11
|
import { DEFAULT_SOURCE_REGISTRY_PORT, parseSourceRegistryUrl, resolveSourceRegistryInfo } from './source-registry.js';
|
|
12
12
|
function trimValue(value) {
|
|
13
13
|
return String(value ?? '').trim();
|
|
@@ -98,6 +98,16 @@ async function commitSourceSnapshotVersion(params) {
|
|
|
98
98
|
errorName: 'git commit',
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
|
+
async function buildSourceSnapshot(params) {
|
|
102
|
+
const args = ['build'];
|
|
103
|
+
if (!params.buildDts) {
|
|
104
|
+
args.push('--no-dts');
|
|
105
|
+
}
|
|
106
|
+
await runNocoBaseCommand(args, {
|
|
107
|
+
cwd: params.cwd,
|
|
108
|
+
stdio: params.stdio,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
101
111
|
async function createSourcePublishStash(params) {
|
|
102
112
|
if (!(await hasLocalGitChanges(params.cwd))) {
|
|
103
113
|
return undefined;
|
|
@@ -158,6 +168,8 @@ export async function publishSourceSnapshot(params) {
|
|
|
158
168
|
const version = buildSnapshotVersion(baseVersion, gitSha, params.now);
|
|
159
169
|
const temporaryBranch = buildSourcePublishBranchName(gitSha, params.now);
|
|
160
170
|
const stdio = params.verbose ? 'inherit' : 'ignore';
|
|
171
|
+
const shouldBuild = params.build !== false;
|
|
172
|
+
const shouldBuildDts = params.buildDts !== false;
|
|
161
173
|
let stash;
|
|
162
174
|
let onTemporaryBranch = false;
|
|
163
175
|
let branchCreated = false;
|
|
@@ -186,6 +198,13 @@ export async function publishSourceSnapshot(params) {
|
|
|
186
198
|
errorName: 'git stash apply',
|
|
187
199
|
});
|
|
188
200
|
}
|
|
201
|
+
if (shouldBuild) {
|
|
202
|
+
await buildSourceSnapshot({
|
|
203
|
+
cwd: projectRoot,
|
|
204
|
+
buildDts: shouldBuildDts,
|
|
205
|
+
stdio,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
189
208
|
await run('yarn', ['lerna', 'version', version, '--force-publish=*', '--no-git-tag-version', '-y'], {
|
|
190
209
|
cwd: projectRoot,
|
|
191
210
|
errorName: 'lerna version',
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import fsp from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { commandOutput, commandSucceeds, resolveCwd, resolveProjectCwd, run } from './run-npm.js';
|
|
12
|
-
import {
|
|
12
|
+
import { resolveCliHomeDir } from './cli-home.js';
|
|
13
13
|
export const DEFAULT_SOURCE_REGISTRY_HOST = '127.0.0.1';
|
|
14
14
|
export const DEFAULT_SOURCE_REGISTRY_PORT = 4873;
|
|
15
15
|
export const DEFAULT_SOURCE_REGISTRY_CONTAINER_NAME = 'nb-source-registry';
|
|
@@ -31,7 +31,7 @@ function asPosixPathForDockerMount(value) {
|
|
|
31
31
|
return resolveCwd(value).replace(/\\/g, '/');
|
|
32
32
|
}
|
|
33
33
|
export function resolveSourceRegistryRootDir() {
|
|
34
|
-
return path.join(
|
|
34
|
+
return path.join(resolveCliHomeDir(), 'verdaccio');
|
|
35
35
|
}
|
|
36
36
|
export function resolveSourceRegistryConfigPath() {
|
|
37
37
|
return path.join(resolveSourceRegistryRootDir(), 'config.yaml');
|
package/dist/locale/en-US.json
CHANGED
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
"back": "Back",
|
|
35
35
|
"next": "Next",
|
|
36
36
|
"submit": "Submit & continue in terminal",
|
|
37
|
+
"showPassword": "Show password",
|
|
38
|
+
"hidePassword": "Hide password",
|
|
37
39
|
"checking": "Checking...",
|
|
38
40
|
"sending": "Sending...",
|
|
39
41
|
"successTitle": "Success",
|
|
@@ -73,7 +75,8 @@
|
|
|
73
75
|
"timeout": "Timed out connecting to the database at {{host}}:{{port}} after about {{seconds}} seconds.",
|
|
74
76
|
"authenticationFailed": "Failed to sign in to database \"{{database}}\" with user \"{{user}}\". Check the username and password.",
|
|
75
77
|
"databaseNotFound": "Database \"{{database}}\" does not exist or is not accessible with the current connection settings.",
|
|
76
|
-
"connectionFailed": "Database connection check failed. Details: {{details}}"
|
|
78
|
+
"connectionFailed": "Database connection check failed. Details: {{details}}",
|
|
79
|
+
"lowerCaseTableNamesRequiresUnderscored": "MySQL lower_case_table_names=1 requires DB_UNDERSCORED=true."
|
|
77
80
|
}
|
|
78
81
|
},
|
|
79
82
|
"commands": {
|
|
@@ -94,16 +97,28 @@
|
|
|
94
97
|
},
|
|
95
98
|
"authType": {
|
|
96
99
|
"message": "How would you like to sign in?",
|
|
100
|
+
"basicLabel": "Basic login (username + password)",
|
|
101
|
+
"basicHint": "uses your credentials to fetch a token after save",
|
|
97
102
|
"oauthLabel": "OAuth (browser login)",
|
|
98
103
|
"oauthHint": "runs nb env auth after save",
|
|
99
104
|
"tokenLabel": "API token / API key"
|
|
100
105
|
},
|
|
106
|
+
"username": {
|
|
107
|
+
"message": "Enter the username for basic login",
|
|
108
|
+
"placeholder": "admin"
|
|
109
|
+
},
|
|
110
|
+
"password": {
|
|
111
|
+
"message": "Enter the password for basic login"
|
|
112
|
+
},
|
|
101
113
|
"accessToken": {
|
|
102
114
|
"message": "Enter an API token or API key",
|
|
103
115
|
"placeholder": "Enter your API token / API key"
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
118
|
},
|
|
119
|
+
"shared": {
|
|
120
|
+
"missingCommand": "Couldn't run `{{action}}` because the {{displayName}} executable could not be found. Install {{displayName}} or update `nb config set {{configKey}} <path>` and try again."
|
|
121
|
+
},
|
|
107
122
|
"download": {
|
|
108
123
|
"failures": {
|
|
109
124
|
"dependencyInstall": {
|
|
@@ -206,6 +221,10 @@
|
|
|
206
221
|
}
|
|
207
222
|
},
|
|
208
223
|
"install": {
|
|
224
|
+
"messages": {
|
|
225
|
+
"skipDownloadDockerImageMissing": "Cannot continue with `--skip-download` because Docker image \"{{imageRef}}\" is not available locally. Load or pull the image first, or run the command again without `--skip-download`.",
|
|
226
|
+
"skipDownloadLocalAppMissing": "Cannot continue with `--skip-download` because \"{{projectRoot}}\" is missing or does not contain a package.json file. Point `--app-root-path` to an existing NocoBase app, or run the command again without `--skip-download`."
|
|
227
|
+
},
|
|
209
228
|
"validation": {
|
|
210
229
|
"builtinDbUnsupported": "Built-in database does not support \"{{dialect}}\" yet. Choose PostgreSQL, MySQL, or MariaDB, or turn off built-in database."
|
|
211
230
|
},
|
|
@@ -229,9 +248,6 @@
|
|
|
229
248
|
"message": "Where should uploads and local files be stored?",
|
|
230
249
|
"placeholder": "./<env>/storage/"
|
|
231
250
|
},
|
|
232
|
-
"fetchSource": {
|
|
233
|
-
"message": "Download NocoBase automatically if the app directory is empty?"
|
|
234
|
-
},
|
|
235
251
|
"dbDialect": {
|
|
236
252
|
"message": "Which database would you like to use?"
|
|
237
253
|
},
|
|
@@ -259,6 +275,17 @@
|
|
|
259
275
|
"dbPassword": {
|
|
260
276
|
"message": "What is the database password?"
|
|
261
277
|
},
|
|
278
|
+
"dbSchema": {
|
|
279
|
+
"message": "What is the database schema? (PostgreSQL only, optional)",
|
|
280
|
+
"placeholder": "Leave empty to use the default schema"
|
|
281
|
+
},
|
|
282
|
+
"dbTablePrefix": {
|
|
283
|
+
"message": "What table prefix should be used? (optional)",
|
|
284
|
+
"placeholder": "For example: nb_"
|
|
285
|
+
},
|
|
286
|
+
"dbUnderscored": {
|
|
287
|
+
"message": "Use underscored names for database tables and columns?"
|
|
288
|
+
},
|
|
262
289
|
"rootUsername": {
|
|
263
290
|
"message": "Choose the initial admin username",
|
|
264
291
|
"placeholder": "nocobase"
|
|
@@ -290,7 +317,8 @@
|
|
|
290
317
|
"uiOpening": "A local setup form will open in your browser. That form needs a person to fill it in. If you are using an AI agent, do not stop this process while the CLI waits for the submission.",
|
|
291
318
|
"uiReady": "Local setup form is ready.",
|
|
292
319
|
"uiReadyHelp": "If your browser does not open automatically, copy the URL below into your browser to continue. Keep this terminal session running while the CLI waits for the submission.",
|
|
293
|
-
"uiOpenBrowserFallback": "We could not open your browser automatically. Copy the URL above into your browser to continue setup, and keep this terminal session running. If you are using an AI agent, do not stop the current process."
|
|
320
|
+
"uiOpenBrowserFallback": "We could not open your browser automatically. Copy the URL above into your browser to continue setup, and keep this terminal session running. If you are using an AI agent, do not stop the current process.",
|
|
321
|
+
"skillsSyncRegistryUnavailable": "Couldn't reach the npm registry to sync NocoBase AI coding skills. Skipping skills install and continuing init. Run `nb skills install` later when registry access is available."
|
|
294
322
|
},
|
|
295
323
|
"prompts": {
|
|
296
324
|
"appName": {
|
|
@@ -308,6 +336,9 @@
|
|
|
308
336
|
"apiBaseUrl": {
|
|
309
337
|
"message": "API base URL",
|
|
310
338
|
"placeholder": "https://demo.example.com/api or https://demo.example.com/api/__app/<subapp>"
|
|
339
|
+
},
|
|
340
|
+
"skipDownload": {
|
|
341
|
+
"message": "Skip downloading NocoBase and reuse existing local app files or Docker images?"
|
|
311
342
|
}
|
|
312
343
|
},
|
|
313
344
|
"webUi": {
|