@nocobase/cli 2.1.0-alpha.20 → 2.1.0-alpha.21

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.
@@ -0,0 +1,142 @@
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 path from 'node:path';
10
+ import { getEnv, loadAuthConfig } from './auth-store.js';
11
+ import { commandOutput, commandSucceeds, run, runNocoBaseCommand } from './run-npm.js';
12
+ const DOCKER_APP_WORKDIR = '/app/nocobase';
13
+ function sanitizeDockerResourceName(value) {
14
+ const normalized = value
15
+ .trim()
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9_.-]+/g, '-')
18
+ .replace(/-+/g, '-')
19
+ .replace(/^-+|-+$/g, '');
20
+ return normalized || 'nocobase';
21
+ }
22
+ export function defaultWorkspaceName(cwd = process.cwd()) {
23
+ return sanitizeDockerResourceName(`nb-${path.basename(cwd)}`);
24
+ }
25
+ export function buildDockerAppContainerName(envName, workspaceName) {
26
+ const workspace = workspaceName?.trim() || defaultWorkspaceName();
27
+ return sanitizeDockerResourceName(`${workspace}-${envName}-app`);
28
+ }
29
+ export function buildDockerDbContainerName(envName, dbDialect, workspaceName) {
30
+ const workspace = workspaceName?.trim() || defaultWorkspaceName();
31
+ const dialect = dbDialect.trim() || 'postgres';
32
+ return sanitizeDockerResourceName(`${workspace}-${envName}-${dialect}`);
33
+ }
34
+ function normalizeEnvSource(env) {
35
+ const source = String(env.config.source ?? '').trim();
36
+ if (source === 'docker' || source === 'npm' || source === 'git') {
37
+ return source;
38
+ }
39
+ if (env.config.appRootPath) {
40
+ return 'local';
41
+ }
42
+ return undefined;
43
+ }
44
+ export async function resolveManagedAppRuntime(envName) {
45
+ const config = await loadAuthConfig();
46
+ const env = await getEnv(envName, { config });
47
+ if (!env) {
48
+ return undefined;
49
+ }
50
+ const resolvedName = env.name || envName?.trim() || config.currentEnv || 'default';
51
+ const source = normalizeEnvSource(env);
52
+ const workspaceName = config.name?.trim() || defaultWorkspaceName();
53
+ if (source === 'docker') {
54
+ return {
55
+ kind: 'docker',
56
+ env,
57
+ envName: resolvedName,
58
+ source,
59
+ workspaceName,
60
+ containerName: buildDockerAppContainerName(resolvedName, workspaceName),
61
+ };
62
+ }
63
+ if (env.config.appRootPath) {
64
+ return {
65
+ kind: 'local',
66
+ env,
67
+ envName: resolvedName,
68
+ source: source === 'git' ? 'git' : source === 'npm' ? 'npm' : 'local',
69
+ projectRoot: env.appRootPath,
70
+ workspaceName,
71
+ };
72
+ }
73
+ return {
74
+ kind: 'remote',
75
+ env,
76
+ envName: resolvedName,
77
+ source,
78
+ };
79
+ }
80
+ export function formatMissingManagedAppEnvMessage(envName) {
81
+ const requested = String(envName ?? '').trim();
82
+ if (requested) {
83
+ return [
84
+ `Env "${requested}" is not configured in this workspace.`,
85
+ `If you want to create a new NocoBase AI environment, run \`nb init --env ${requested}\` first.`,
86
+ ].join('\n');
87
+ }
88
+ return 'No NocoBase env is configured yet. Run `nb init` to create one first.';
89
+ }
90
+ export async function runLocalNocoBaseCommand(runtime, args, options) {
91
+ await runNocoBaseCommand(args, {
92
+ cwd: runtime.projectRoot,
93
+ env: runtime.env.envVars,
94
+ stdio: options?.stdio,
95
+ });
96
+ }
97
+ export async function dockerContainerExists(containerName) {
98
+ return await commandSucceeds('docker', ['container', 'inspect', containerName]);
99
+ }
100
+ export async function dockerContainerIsRunning(containerName) {
101
+ try {
102
+ const output = await commandOutput('docker', ['inspect', '--format', '{{.State.Running}}', containerName], { errorName: 'docker inspect' });
103
+ return output.trim() === 'true';
104
+ }
105
+ catch (_error) {
106
+ return false;
107
+ }
108
+ }
109
+ export async function startDockerContainer(containerName, options) {
110
+ const exists = await dockerContainerExists(containerName);
111
+ if (!exists) {
112
+ throw new Error(`Docker app container "${containerName}" does not exist.`);
113
+ }
114
+ if (await dockerContainerIsRunning(containerName)) {
115
+ return 'already-running';
116
+ }
117
+ await run('docker', ['start', containerName], {
118
+ errorName: 'docker start',
119
+ stdio: options?.stdio,
120
+ });
121
+ return 'started';
122
+ }
123
+ export async function stopDockerContainer(containerName, options) {
124
+ const exists = await dockerContainerExists(containerName);
125
+ if (!exists) {
126
+ throw new Error(`Docker app container "${containerName}" does not exist.`);
127
+ }
128
+ if (!(await dockerContainerIsRunning(containerName))) {
129
+ return 'already-stopped';
130
+ }
131
+ await run('docker', ['stop', containerName], {
132
+ errorName: 'docker stop',
133
+ stdio: options?.stdio,
134
+ });
135
+ return 'stopped';
136
+ }
137
+ export async function runDockerNocoBaseCommand(containerName, args) {
138
+ await startDockerContainer(containerName);
139
+ await run('docker', ['exec', '-w', DOCKER_APP_WORKDIR, containerName, 'yarn', 'nocobase', ...args], {
140
+ errorName: 'docker exec',
141
+ });
142
+ }
@@ -21,6 +21,7 @@ export async function loadAuthConfig(options = {}) {
21
21
  const content = await fs.readFile(getConfigFile(options), 'utf8');
22
22
  const parsed = JSON.parse(content);
23
23
  return {
24
+ name: parsed.name || parsed.dockerResourcePrefix,
24
25
  currentEnv: parsed.currentEnv || 'default',
25
26
  envs: parsed.envs || {},
26
27
  };
@@ -53,6 +54,17 @@ export async function setCurrentEnv(envName, options = {}) {
53
54
  config.currentEnv = envName;
54
55
  await saveAuthConfig(config, options);
55
56
  }
57
+ export async function ensureWorkspaceName(defaultName, options = {}) {
58
+ const config = await loadAuthConfig(options);
59
+ const existing = config.name?.trim();
60
+ if (existing) {
61
+ return existing;
62
+ }
63
+ const next = defaultName.trim();
64
+ config.name = next;
65
+ await saveAuthConfig(config, options);
66
+ return next;
67
+ }
56
68
  export class Env {
57
69
  config;
58
70
  constructor(config = {}) {
@@ -87,11 +99,40 @@ export class Env {
87
99
  }
88
100
  return path.resolve(process.cwd(), storagePath);
89
101
  }
102
+ get appPort() {
103
+ return this.config.appPort;
104
+ }
105
+ get envVars() {
106
+ const out = {
107
+ STORAGE_PATH: this.storagePath,
108
+ };
109
+ const put = (key, value) => {
110
+ if (value === undefined || value === null) {
111
+ return;
112
+ }
113
+ out[key] = String(value);
114
+ };
115
+ put('APP_PORT', this.appPort);
116
+ put('APP_KEY', this.config.appKey);
117
+ put('TZ', this.config.timezone);
118
+ put('DB_DIALECT', this.config.dbDialect);
119
+ put('DB_HOST', this.config.dbHost);
120
+ put('DB_PORT', this.config.dbPort);
121
+ put('DB_DATABASE', this.config.dbDatabase);
122
+ put('DB_USER', this.config.dbUser);
123
+ put('DB_PASSWORD', this.config.dbPassword);
124
+ return out;
125
+ }
90
126
  }
91
127
  export async function getEnv(envName, options = {}) {
92
- const config = await loadAuthConfig(options);
93
- const resolved = envName || config.currentEnv || 'default';
94
- return new Env({ ...config.envs[resolved], name: resolved });
128
+ const { config: snapshot, ...loadOptions } = options;
129
+ const config = snapshot ?? (await loadAuthConfig(loadOptions));
130
+ const resolved = envName?.trim() || config.currentEnv || 'default';
131
+ const envConfig = config.envs[resolved];
132
+ if (!envConfig) {
133
+ return undefined;
134
+ }
135
+ return new Env({ ...envConfig, name: resolved });
95
136
  }
96
137
  function areAuthConfigsEquivalent(left, right) {
97
138
  if (!left && !right) {
@@ -354,9 +354,13 @@ export async function updateEnvRuntime(options) {
354
354
  });
355
355
  if (!baseUrl) {
356
356
  throw new Error([
357
- `Env "${envName}" is missing a base URL.`,
358
- 'Update it with `nb env add <name> --base-url <url>` first.',
359
- ].join('\n'));
357
+ env
358
+ ? `Env "${envName}" is missing a base URL.`
359
+ : `Env "${envName}" is not configured. Run \`nb env add ${envName}\` first.`,
360
+ env ? 'Update it with `nb env add <name> --base-url <url>` first.' : '',
361
+ ]
362
+ .filter(Boolean)
363
+ .join('\n'));
360
364
  }
361
365
  updateTask('Loading command runtime...');
362
366
  try {