@nocobase/app-installer 0.0.0-stage → 0.1.0-beta.3
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 +217 -2
- package/bin/run.js +43 -0
- package/dist/cli.js +171 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/install.js +383 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/rollback.js +287 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/status.js +155 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.js +709 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/lib/app-cli.js +89 -0
- package/dist/lib/app-cli.js.map +1 -0
- package/dist/lib/backup.js +167 -0
- package/dist/lib/backup.js.map +1 -0
- package/dist/lib/confirm.js +36 -0
- package/dist/lib/confirm.js.map +1 -0
- package/dist/lib/current-link.js +22 -0
- package/dist/lib/current-link.js.map +1 -0
- package/dist/lib/ecosystem.js +86 -0
- package/dist/lib/ecosystem.js.map +1 -0
- package/dist/lib/env-file.js +87 -0
- package/dist/lib/env-file.js.map +1 -0
- package/dist/lib/errors.js +32 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/health.js +55 -0
- package/dist/lib/health.js.map +1 -0
- package/dist/lib/initial-admin.js +39 -0
- package/dist/lib/initial-admin.js.map +1 -0
- package/dist/lib/interrupt.js +50 -0
- package/dist/lib/interrupt.js.map +1 -0
- package/dist/lib/invocation.js +33 -0
- package/dist/lib/invocation.js.map +1 -0
- package/dist/lib/layout.js +61 -0
- package/dist/lib/layout.js.map +1 -0
- package/dist/lib/lock.js +59 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/mount-path.js +34 -0
- package/dist/lib/mount-path.js.map +1 -0
- package/dist/lib/output.js +61 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/pm2.js +92 -0
- package/dist/lib/pm2.js.map +1 -0
- package/dist/lib/prechecks.js +236 -0
- package/dist/lib/prechecks.js.map +1 -0
- package/dist/lib/registry.js +59 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/release.js +328 -0
- package/dist/lib/release.js.map +1 -0
- package/dist/lib/run-command.js +108 -0
- package/dist/lib/run-command.js.map +1 -0
- package/dist/lib/service.js +100 -0
- package/dist/lib/service.js.map +1 -0
- package/dist/lib/source.js +100 -0
- package/dist/lib/source.js.map +1 -0
- package/dist/lib/state.js +73 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/version.js +51 -0
- package/dist/lib/version.js.map +1 -0
- package/package.json +51 -3
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
import { EXIT_INVALID, InstallerError, } from "./errors.js";
|
|
4
|
+
import { runCommand } from "./run-command.js";
|
|
5
|
+
import { compareVersions } from "./version.js";
|
|
6
|
+
export const MINIMUM_NODE_MAJOR = 24;
|
|
7
|
+
export const MINIMUM_PNPM_MAJOR = 11;
|
|
8
|
+
/**
|
|
9
|
+
* pm2 recognises `ecosystem.config.cjs` as a configuration from 4.3 on; an older one runs the file as the application
|
|
10
|
+
* instead, which exits at once and looks like an application that cannot start.
|
|
11
|
+
*/
|
|
12
|
+
export const MINIMUM_PM2_VERSION = '4.3.0';
|
|
13
|
+
export function majorOf(version) {
|
|
14
|
+
const match = version.trim().match(/^v?(\d+)/u);
|
|
15
|
+
return match ? Number.parseInt(match[1], 10) : Number.NaN;
|
|
16
|
+
}
|
|
17
|
+
export function currentNodeMajor() {
|
|
18
|
+
return majorOf(process.versions.node);
|
|
19
|
+
}
|
|
20
|
+
export function checkPlatform(platform = process.platform) {
|
|
21
|
+
if (platform === 'win32') {
|
|
22
|
+
throw new InstallerError('PLATFORM_UNSUPPORTED', 'app-installer does not run on Windows: the release switch relies on symbolic links and atomic renames.', {
|
|
23
|
+
exitCode: EXIT_INVALID,
|
|
24
|
+
suggestions: [{ message: 'Run it inside WSL instead.' }],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function checkPnpm(run = runCommand) {
|
|
29
|
+
let version;
|
|
30
|
+
try {
|
|
31
|
+
version = (await run('pnpm', ['--version'])).stdout.trim();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new InstallerError('PNPM_MISSING', 'pnpm was not found on PATH.', {
|
|
35
|
+
exitCode: EXIT_INVALID,
|
|
36
|
+
suggestions: installPnpmAdvice(),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (!(majorOf(version) >= MINIMUM_PNPM_MAJOR)) {
|
|
40
|
+
throw new InstallerError('PNPM_UNSUPPORTED', `pnpm ${MINIMUM_PNPM_MAJOR} or later is required; found ${version}.`, {
|
|
41
|
+
exitCode: EXIT_INVALID,
|
|
42
|
+
suggestions: installPnpmAdvice(),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return version;
|
|
46
|
+
}
|
|
47
|
+
/** Two steps, since a suggestion runs one command: corepack is enabled, then pnpm is activated through it. */
|
|
48
|
+
function installPnpmAdvice() {
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
message: `Enable corepack, which provides pnpm ${MINIMUM_PNPM_MAJOR}:`,
|
|
52
|
+
run: { command: 'corepack', args: ['enable'] },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
message: `Then activate pnpm ${MINIMUM_PNPM_MAJOR}, and open a new shell:`,
|
|
56
|
+
run: {
|
|
57
|
+
command: 'corepack',
|
|
58
|
+
args: ['prepare', `pnpm@${MINIMUM_PNPM_MAJOR}`, '--activate'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
/** The C library Node runs on, which a Linux build's native modules are compiled against. */
|
|
64
|
+
export function currentLibc() {
|
|
65
|
+
if (process.platform !== 'linux')
|
|
66
|
+
return undefined;
|
|
67
|
+
const report = process.report.getReport();
|
|
68
|
+
return report.header?.glibcVersionRuntime ? 'glibc' : 'musl';
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* This machine as `pnpm build --target` names a platform, such as `linux-x64` or `linux-arm64-musl`, so a suggestion
|
|
72
|
+
* to rebuild for it runs as printed.
|
|
73
|
+
*/
|
|
74
|
+
export function machineBuildTarget() {
|
|
75
|
+
return `${process.platform}-${process.arch}${currentLibc() === 'musl' ? '-musl' : ''}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The build command that produces an archive this machine runs, run in the application project. A current build is not
|
|
79
|
+
* tied to a mount path, so the command names none.
|
|
80
|
+
*/
|
|
81
|
+
export function rebuildCommandLine() {
|
|
82
|
+
return {
|
|
83
|
+
command: 'pnpm',
|
|
84
|
+
args: [
|
|
85
|
+
'build',
|
|
86
|
+
'--target',
|
|
87
|
+
machineBuildTarget(),
|
|
88
|
+
'--node-version',
|
|
89
|
+
String(currentNodeMajor()),
|
|
90
|
+
'--tar',
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* pm2 must be installed globally: `pm2 startup` writes a boot service that names pm2's own path, so a copy fetched
|
|
96
|
+
* through `npx` into a cache would vanish from under it.
|
|
97
|
+
*/
|
|
98
|
+
export async function checkPm2(pm2) {
|
|
99
|
+
let version;
|
|
100
|
+
try {
|
|
101
|
+
version = await pm2.version();
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new InstallerError('PM2_MISSING', 'pm2 was not found on PATH.', {
|
|
105
|
+
exitCode: EXIT_INVALID,
|
|
106
|
+
suggestions: [
|
|
107
|
+
{
|
|
108
|
+
message: 'Install pm2 globally:',
|
|
109
|
+
run: { command: 'npm', args: ['install', '-g', 'pm2'] },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
message: 'Or install without starting the application, with --no-start.',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const comparison = compareVersions(version, MINIMUM_PM2_VERSION);
|
|
118
|
+
if (comparison === undefined || comparison < 0) {
|
|
119
|
+
throw new InstallerError('PM2_UNSUPPORTED', comparison === undefined
|
|
120
|
+
? `pm2 ${MINIMUM_PM2_VERSION} or later is required, and \`pm2 --version\` printed "${version}", which is not a version.`
|
|
121
|
+
: `pm2 ${MINIMUM_PM2_VERSION} or later is required; found ${version}.`, {
|
|
122
|
+
exitCode: EXIT_INVALID,
|
|
123
|
+
suggestions: [
|
|
124
|
+
{
|
|
125
|
+
message: 'Update pm2 globally:',
|
|
126
|
+
run: { command: 'npm', args: ['install', '-g', 'pm2@latest'] },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
message: 'Then swap the running daemon for the new version:',
|
|
130
|
+
run: { command: 'pm2', args: ['update'] },
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return version;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The pm2 process name must be free. `pm2 start` on a name that is already registered does not start a second process:
|
|
139
|
+
* it restarts the existing one — its own script and arguments — with the new environment, so another application on this
|
|
140
|
+
* machine would come back serving this one's configuration and database.
|
|
141
|
+
*/
|
|
142
|
+
export async function checkPm2NameFree(pm2, name) {
|
|
143
|
+
const existing = await pm2.describe(name);
|
|
144
|
+
if (existing) {
|
|
145
|
+
throw new InstallerError('PM2_NAME_IN_USE', `pm2 already runs a process named ${name}${existing.cwd ? ` from ${existing.cwd}` : ''}.`, {
|
|
146
|
+
exitCode: EXIT_INVALID,
|
|
147
|
+
suggestions: [
|
|
148
|
+
{
|
|
149
|
+
message: `Give this installation its own process name with --name, such as --name ${name}-2.`,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function tryListen(host, port) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
const server = createServer();
|
|
158
|
+
server.once('error', (error) => resolve(error));
|
|
159
|
+
server.listen(port, host, () => server.close(() => resolve(undefined)));
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/** The first port above `after` that can be listened on right now, for a suggestion; `undefined` if none is near. */
|
|
163
|
+
export async function nextFreePort(host, after, attempts = 50) {
|
|
164
|
+
for (let port = after + 1; port <= Math.min(after + attempts, 65535); port += 1) {
|
|
165
|
+
if (!(await tryListen(host, port)))
|
|
166
|
+
return port;
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The application's port must be free. Otherwise whatever already listens there answers the health check, and the
|
|
172
|
+
* install reports success while its own process crash-loops on EADDRINUSE. With `suggestPort`, as for an install that
|
|
173
|
+
* chooses its port, the error names a free one; each installation on a machine needs its own.
|
|
174
|
+
*/
|
|
175
|
+
export async function checkPortFree(host, port, options = {}) {
|
|
176
|
+
const error = await tryListen(host, port);
|
|
177
|
+
if (!error)
|
|
178
|
+
return;
|
|
179
|
+
const free = options.suggestPort && error.code === 'EADDRINUSE'
|
|
180
|
+
? await nextFreePort(host, port)
|
|
181
|
+
: undefined;
|
|
182
|
+
throw new InstallerError('PORT_IN_USE', error.code === 'EADDRINUSE'
|
|
183
|
+
? `${host}:${port} is already in use.`
|
|
184
|
+
: `Cannot listen on ${host}:${port}: ${error.message}`, {
|
|
185
|
+
exitCode: EXIT_INVALID,
|
|
186
|
+
details: free === undefined ? undefined : { freePort: free },
|
|
187
|
+
suggestions: [
|
|
188
|
+
options.suggestPort
|
|
189
|
+
? {
|
|
190
|
+
message: free === undefined
|
|
191
|
+
? 'Choose another port with --port.'
|
|
192
|
+
: `Port ${free} is free: pass --port ${free}, and point the reverse proxy at it.`,
|
|
193
|
+
}
|
|
194
|
+
: { message: 'Stop what holds the port, then run this again.' },
|
|
195
|
+
],
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** `--set-from-env` names variables; a typo should fail here, not after the build. */
|
|
199
|
+
export function checkEnvVariables(variables, env = process.env) {
|
|
200
|
+
const missing = variables.filter((name) => env[name] === undefined);
|
|
201
|
+
if (missing.length > 0) {
|
|
202
|
+
throw new InstallerError('ENV_MISSING', `--set-from-env names ${missing.length === 1 ? 'a variable that is' : 'variables that are'} not set: ${missing.join(', ')}.`, { exitCode: EXIT_INVALID });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** The target must be new or empty; the installer never overwrites files it did not write. */
|
|
206
|
+
export async function checkTargetEmpty(root) {
|
|
207
|
+
let entries;
|
|
208
|
+
try {
|
|
209
|
+
entries = await readdir(root);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const code = error.code;
|
|
213
|
+
if (code === 'ENOENT')
|
|
214
|
+
return false;
|
|
215
|
+
if (code === 'ENOTDIR') {
|
|
216
|
+
throw new InstallerError('TARGET_NOT_EMPTY', `${root} is a file.`, {
|
|
217
|
+
exitCode: EXIT_INVALID,
|
|
218
|
+
suggestions: [{ message: 'Install into a new or empty directory.' }],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
if (entries.length > 0) {
|
|
224
|
+
throw new InstallerError('TARGET_NOT_EMPTY', `${root} is not empty.`, {
|
|
225
|
+
exitCode: EXIT_INVALID,
|
|
226
|
+
suggestions: [
|
|
227
|
+
{ message: 'Install into a new or empty directory.' },
|
|
228
|
+
{
|
|
229
|
+
message: 'To manage an application this installer already set up there, use status, upgrade or rollback instead.',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=prechecks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prechecks.js","sourceRoot":"","sources":["../../src/lib/prechecks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EXIT_INVALID, InstallerError } from "./errors.js";
|
|
2
|
+
/** The public npm registry is the default; `--registry` and `NOCOBASE_REGISTRY` may select a private registry. */
|
|
3
|
+
export const FALLBACK_REGISTRY = 'https://registry.npmjs.org';
|
|
4
|
+
export function defaultRegistry(env = process.env) {
|
|
5
|
+
return env.NOCOBASE_REGISTRY?.trim() || FALLBACK_REGISTRY;
|
|
6
|
+
}
|
|
7
|
+
export function normalizeRegistry(registry) {
|
|
8
|
+
return registry.replace(/\/+$/u, '');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a dist-tag such as `latest`, or checks an exact version, against the registry. Every later step uses the
|
|
12
|
+
* exact version, so `latest` moving halfway through an install cannot mix two releases.
|
|
13
|
+
*/
|
|
14
|
+
export async function resolveTemplateVersion(registry, packageName, requested, fetchImpl = fetch) {
|
|
15
|
+
const url = `${normalizeRegistry(registry)}/${packageName.replace('/', '%2f')}`;
|
|
16
|
+
let packument;
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetchImpl(url, {
|
|
19
|
+
headers: { accept: 'application/vnd.npm.install-v1+json' },
|
|
20
|
+
signal: AbortSignal.timeout(30_000),
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`HTTP ${response.status}`);
|
|
24
|
+
}
|
|
25
|
+
packument = (await response.json());
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new InstallerError('REGISTRY_UNREACHABLE', `Could not read ${packageName} from ${normalizeRegistry(registry)}: ${error instanceof Error ? error.message : String(error)}.`, {
|
|
29
|
+
exitCode: EXIT_INVALID,
|
|
30
|
+
cause: error,
|
|
31
|
+
suggestions: [
|
|
32
|
+
{
|
|
33
|
+
message: 'Check the network, or name another registry with --registry.',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// `hasOwn`, not a plain lookup: `constructor` and friends are on every parsed object's prototype.
|
|
39
|
+
const tags = packument['dist-tags'] ?? {};
|
|
40
|
+
if (Object.hasOwn(tags, requested))
|
|
41
|
+
return tags[requested];
|
|
42
|
+
if (packument.versions && Object.hasOwn(packument.versions, requested)) {
|
|
43
|
+
return requested;
|
|
44
|
+
}
|
|
45
|
+
const recent = Object.keys(packument.versions ?? {}).slice(-5);
|
|
46
|
+
throw new InstallerError('VERSION_NOT_FOUND', `${packageName} has no version or tag "${requested}".`, {
|
|
47
|
+
exitCode: EXIT_INVALID,
|
|
48
|
+
details: {
|
|
49
|
+
distTags: packument['dist-tags'] ?? {},
|
|
50
|
+
recentVersions: recent,
|
|
51
|
+
},
|
|
52
|
+
suggestions: [
|
|
53
|
+
{
|
|
54
|
+
message: `Use a tag (${Object.keys(packument['dist-tags'] ?? {}).join(', ') || 'none'}) or one of the recent versions: ${recent.join(', ') || 'none'}.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3D,kHAAkH;AAClH,MAAM,CAAC,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAE9D,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,OAAO,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,iBAAiB,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAgBD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAAgB,EAChB,WAAmB,EACnB,SAAiB,EACjB,YAAuB,KAAK;IAE5B,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;IAChF,IAAI,SAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;YACpC,OAAO,EAAE,EAAE,MAAM,EAAE,qCAAqC,EAAE;YAC1D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CACtB,sBAAsB,EACtB,kBAAkB,WAAW,SAAS,iBAAiB,CAAC,QAAQ,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAC/H;YACE,QAAQ,EAAE,YAAY;YACtB,KAAK,EAAE,KAAK;YACZ,WAAW,EAAE;gBACX;oBACE,OAAO,EACL,8DAA8D;iBACjE;aACF;SACF,CACF,CAAC;IACJ,CAAC;IAED,kGAAkG;IAClG,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,SAAS,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,cAAc,CACtB,mBAAmB,EACnB,GAAG,WAAW,2BAA2B,SAAS,IAAI,EACtD;QACE,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE;YACP,QAAQ,EAAE,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE;YACtC,cAAc,EAAE,MAAM;SACvB;QACD,WAAW,EAAE;YACX;gBACE,OAAO,EAAE,cAAc,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,oCAAoC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG;aACxJ;SACF;KACF,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile, rename, rm, rmdir, writeFile, } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { extract } from 'tar';
|
|
5
|
+
import { EXIT_INVALID, InstallerError, isInstallerError } from "./errors.js";
|
|
6
|
+
import { RELEASE_APP_DIR, buildRoot, releaseDir, releaseId, stagingDir, } from "./layout.js";
|
|
7
|
+
import { currentNodeMajor, rebuildCommandLine } from "./prechecks.js";
|
|
8
|
+
import { normalizeRegistry } from "./registry.js";
|
|
9
|
+
import { CommandFailedError, runCommand, tail, } from "./run-command.js";
|
|
10
|
+
/** Every official database dialect; each has its driver in `@nocobase/db-<dialect>`. */
|
|
11
|
+
export const DIALECTS = [
|
|
12
|
+
'sqlite',
|
|
13
|
+
'postgres',
|
|
14
|
+
'mysql',
|
|
15
|
+
'mssql',
|
|
16
|
+
'oracle',
|
|
17
|
+
'dameng',
|
|
18
|
+
'kingbase',
|
|
19
|
+
'oceanbase',
|
|
20
|
+
];
|
|
21
|
+
/** SQLite ships with every template; any other dialect's driver has to be added before the build. */
|
|
22
|
+
export function driversFor(dialect) {
|
|
23
|
+
return dialect === 'sqlite' ? [] : [`@nocobase/db-${dialect}`];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The environment every install step runs with. pnpm 11 holds back versions younger than a day by default, which would
|
|
27
|
+
* make a template released today uninstallable until tomorrow; the NocoBase documentation clears it the same way.
|
|
28
|
+
*/
|
|
29
|
+
export function installEnv(base = process.env) {
|
|
30
|
+
return { ...base, PNPM_CONFIG_MINIMUM_RELEASE_AGE: '0' };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `pnpm create` prints pnpm's own notices around create-app's result; the result is the last line that parses. It is
|
|
34
|
+
* the application CLI's envelope, with the stage under `error.details`. `pnpm create` runs whichever create-app the
|
|
35
|
+
* registry serves as latest, so the flat `status`, `stage` and `message` a create-app before that envelope printed are
|
|
36
|
+
* read too.
|
|
37
|
+
*/
|
|
38
|
+
export function parseCreateResult(stdout) {
|
|
39
|
+
for (const line of stdout.trim().split('\n').reverse()) {
|
|
40
|
+
if (!line.startsWith('{'))
|
|
41
|
+
continue;
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(line);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const stage = parsed.error?.details?.stage;
|
|
50
|
+
return {
|
|
51
|
+
ok: parsed.ok ?? parsed.status === 'success',
|
|
52
|
+
stage: typeof stage === 'string' ? stage : parsed.stage,
|
|
53
|
+
message: parsed.error?.message ?? parsed.message,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { ok: false };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The range the installed runtime accepts for a driver, read from `@nocobase/app-server`'s peer dependencies the same
|
|
60
|
+
* way `nocobase config init` suggests it. A bare `pnpm add` would take the newest driver, which during a prerelease can
|
|
61
|
+
* be one the runtime was never built against.
|
|
62
|
+
*/
|
|
63
|
+
export async function driverSpecifier(projectDir, driver) {
|
|
64
|
+
try {
|
|
65
|
+
const manifest = JSON.parse(await readFile(path.join(projectDir, 'node_modules/@nocobase/app-server/package.json'), 'utf8'));
|
|
66
|
+
const range = manifest.peerDependencies?.[driver]?.trim();
|
|
67
|
+
return range && !range.startsWith('workspace:')
|
|
68
|
+
? `${driver}@${range}`
|
|
69
|
+
: driver;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return driver;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function verifyBuildTarget(target, expected = {
|
|
76
|
+
platform: process.platform,
|
|
77
|
+
arch: process.arch,
|
|
78
|
+
nodeMajor: currentNodeMajor(),
|
|
79
|
+
}) {
|
|
80
|
+
if (!target ||
|
|
81
|
+
target.platform !== expected.platform ||
|
|
82
|
+
target.arch !== expected.arch ||
|
|
83
|
+
target.nodeMajor !== expected.nodeMajor) {
|
|
84
|
+
throw new InstallerError('BUILD_TARGET_MISMATCH', `The build targets ${target ? `${target.platform}-${target.arch} on Node ${target.nodeMajor}` : 'an unknown platform'}, but this machine is ${expected.platform}-${expected.arch} on Node ${expected.nodeMajor}.`);
|
|
85
|
+
}
|
|
86
|
+
return target;
|
|
87
|
+
}
|
|
88
|
+
export async function readReleaseManifest(dir) {
|
|
89
|
+
return JSON.parse(await readFile(path.join(dir, 'dist/package.json'), 'utf8'));
|
|
90
|
+
}
|
|
91
|
+
function stepFailure(code, step, error) {
|
|
92
|
+
// An interrupt, or a failure that already says what went wrong, passes through as it is.
|
|
93
|
+
if (isInstallerError(error))
|
|
94
|
+
return error;
|
|
95
|
+
const output = error instanceof CommandFailedError
|
|
96
|
+
? tail(`${error.stdout}\n${error.stderr}`)
|
|
97
|
+
: undefined;
|
|
98
|
+
return new InstallerError(code, `${step} failed${error instanceof Error ? `: ${error.message}` : '.'}`, { cause: error, ...(output ? { details: { output } } : {}) });
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Unpacks a deployment archive into a staging directory, reads which release it is from its manifest, checks that it
|
|
102
|
+
* was built for this machine, and moves it to `releases/<id>/app`. A release already on disk under the same id is the
|
|
103
|
+
* same build, so the staged copy is dropped and the existing one is used. Nothing here touches the running application,
|
|
104
|
+
* and on failure the staging directory is removed.
|
|
105
|
+
*/
|
|
106
|
+
export async function unpackRelease(options) {
|
|
107
|
+
const staging = stagingDir(options.layout);
|
|
108
|
+
const stagedApp = path.join(staging, RELEASE_APP_DIR);
|
|
109
|
+
const fromArchive = options.fallback === undefined;
|
|
110
|
+
await rm(staging, { recursive: true, force: true });
|
|
111
|
+
try {
|
|
112
|
+
await mkdir(stagedApp, { recursive: true });
|
|
113
|
+
try {
|
|
114
|
+
// The library `pnpm build --tar` packs with, so a deployment needs no `tar` of its own. Synchronous for the same
|
|
115
|
+
// reason the packing is: on a tree this size the promise form can leave the event loop with nothing pending.
|
|
116
|
+
extract({
|
|
117
|
+
file: options.archive,
|
|
118
|
+
cwd: stagedApp,
|
|
119
|
+
strict: true,
|
|
120
|
+
sync: true,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw stepFailure('UNPACK_FAILED', 'Unpacking the deployment archive', error);
|
|
125
|
+
}
|
|
126
|
+
let manifest;
|
|
127
|
+
try {
|
|
128
|
+
manifest = await readReleaseManifest(stagedApp);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw new InstallerError('ARCHIVE_INVALID', `${options.archive} holds no dist/package.json; it is not a deployment archive from \`pnpm build --tar\`.`, { exitCode: EXIT_INVALID, cause: error });
|
|
132
|
+
}
|
|
133
|
+
let buildTarget;
|
|
134
|
+
try {
|
|
135
|
+
buildTarget = verifyBuildTarget(manifest.nocobase?.buildTarget);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (!fromArchive || !isInstallerError(error))
|
|
139
|
+
throw error;
|
|
140
|
+
throw new InstallerError(error.code, error.message, {
|
|
141
|
+
exitCode: EXIT_INVALID,
|
|
142
|
+
suggestions: [
|
|
143
|
+
{
|
|
144
|
+
message: 'Build the archive for this machine in the application project, then run this again:',
|
|
145
|
+
run: rebuildCommandLine(),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const version = manifest.version ?? '0.0.0';
|
|
151
|
+
const builtAt = manifest.nocobase?.builtAt ?? options.fallback?.builtAt;
|
|
152
|
+
const relocatable = manifest.nocobase?.relocatable === true;
|
|
153
|
+
const basePath = relocatable
|
|
154
|
+
? undefined
|
|
155
|
+
: (manifest.nocobase?.basePath ?? options.fallback?.basePath);
|
|
156
|
+
if (!builtAt || (!relocatable && !basePath) || !manifest.name) {
|
|
157
|
+
throw new InstallerError('ARCHIVE_TOO_OLD', `${options.archive} does not record where its client can be mounted and when it was built; it comes from a \`pnpm build\` older than app-installer needs.`, {
|
|
158
|
+
exitCode: EXIT_INVALID,
|
|
159
|
+
suggestions: [
|
|
160
|
+
{
|
|
161
|
+
message: 'Upgrade @nocobase/app-cli in the application project, build the archive again, then run this again:',
|
|
162
|
+
run: rebuildCommandLine(),
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const id = releaseId(version, builtAt);
|
|
168
|
+
const dir = releaseDir(options.layout, id);
|
|
169
|
+
const reused = existsSync(dir);
|
|
170
|
+
if (!reused) {
|
|
171
|
+
await mkdir(options.layout.releasesDir, { recursive: true });
|
|
172
|
+
await rename(staging, path.dirname(dir));
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
id,
|
|
176
|
+
dir,
|
|
177
|
+
appName: manifest.name,
|
|
178
|
+
version,
|
|
179
|
+
builtAt,
|
|
180
|
+
relocatable,
|
|
181
|
+
...(basePath === undefined ? {} : { basePath }),
|
|
182
|
+
buildTarget,
|
|
183
|
+
templateKind: manifest.nocobase?.templateKind,
|
|
184
|
+
reused,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
await rm(staging, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Builds one release from a published template: the project is generated and built in `.build/<version>/`, and only
|
|
193
|
+
* the deployment archive it produces is unpacked into `releases/<id>/app`. The build directory, with the sources and
|
|
194
|
+
* development dependencies, is removed afterwards unless `keepSource` is set. Nothing here touches the running
|
|
195
|
+
* application; on failure every directory this call created is removed again, so a retry starts clean.
|
|
196
|
+
*/
|
|
197
|
+
export async function buildFromTemplate(options) {
|
|
198
|
+
const { layout, template, version, reporter } = options;
|
|
199
|
+
const run = options.run ?? runCommand;
|
|
200
|
+
const build = buildRoot(layout, version);
|
|
201
|
+
const projectDir = path.join(build, template.projectName);
|
|
202
|
+
const env = installEnv();
|
|
203
|
+
const startedAt = new Date().toISOString();
|
|
204
|
+
try {
|
|
205
|
+
await rm(build, { recursive: true, force: true });
|
|
206
|
+
await mkdir(build, { recursive: true });
|
|
207
|
+
// `pnpm create` looks up @nocobase/create-app with the configuration of the directory it runs in, not with
|
|
208
|
+
// --registry, so the registry has to be named here too while NocoBase packages live outside the public npm.
|
|
209
|
+
await writeFile(path.join(build, '.npmrc'), `@nocobase:registry=${normalizeRegistry(options.registry)}/\n`);
|
|
210
|
+
reporter.progress(`Generating the ${template.title} ${version} project`);
|
|
211
|
+
let createStdout;
|
|
212
|
+
try {
|
|
213
|
+
({ stdout: createStdout } = await run('pnpm', [
|
|
214
|
+
'create',
|
|
215
|
+
'@nocobase/app',
|
|
216
|
+
template.projectName,
|
|
217
|
+
`--template=${template.package}@${version}`,
|
|
218
|
+
`--registry=${normalizeRegistry(options.registry)}`,
|
|
219
|
+
'--json',
|
|
220
|
+
], { cwd: build, env, timeoutMs: 20 * 60_000 }));
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (isInstallerError(error))
|
|
224
|
+
throw error;
|
|
225
|
+
const result = error instanceof CommandFailedError
|
|
226
|
+
? parseCreateResult(error.stdout)
|
|
227
|
+
: { ok: false };
|
|
228
|
+
throw new InstallerError('CREATE_FAILED', `create-app failed${result.stage ? ` at its ${result.stage} stage` : ''}: ${result.message ?? (error instanceof Error ? error.message : String(error))}`, {
|
|
229
|
+
cause: error,
|
|
230
|
+
...(error instanceof CommandFailedError
|
|
231
|
+
? { details: { output: tail(error.stderr) } }
|
|
232
|
+
: {}),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const created = parseCreateResult(createStdout);
|
|
236
|
+
if (!created.ok) {
|
|
237
|
+
throw new InstallerError('CREATE_FAILED', `create-app did not succeed: ${created.message ?? 'no result'}`);
|
|
238
|
+
}
|
|
239
|
+
for (const driver of options.drivers) {
|
|
240
|
+
const specifier = await driverSpecifier(projectDir, driver);
|
|
241
|
+
reporter.progress(`Adding ${specifier}`);
|
|
242
|
+
try {
|
|
243
|
+
await run('pnpm', ['add', specifier], {
|
|
244
|
+
cwd: projectDir,
|
|
245
|
+
env,
|
|
246
|
+
timeoutMs: 10 * 60_000,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
throw stepFailure('DRIVER_INSTALL_FAILED', `pnpm add ${specifier}`, error);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
reporter.progress(`Building the ${template.title} ${version}`);
|
|
254
|
+
try {
|
|
255
|
+
// A template version from before relocatable builds compiles the base path into its client, so an APP_BASE_PATH
|
|
256
|
+
// left in the caller's shell must not leak in. A current version ignores it.
|
|
257
|
+
await run('pnpm', ['build', '--tar'], {
|
|
258
|
+
cwd: projectDir,
|
|
259
|
+
env: { ...env, APP_BASE_PATH: template.basePath },
|
|
260
|
+
timeoutMs: 30 * 60_000,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
throw stepFailure('BUILD_FAILED', 'pnpm build', error);
|
|
265
|
+
}
|
|
266
|
+
const prepared = await unpackRelease({
|
|
267
|
+
layout,
|
|
268
|
+
archive: path.join(projectDir, 'storage/exports/dist.tar.gz'),
|
|
269
|
+
// Published templates built before `pnpm build` recorded these have neither; the installer knows both.
|
|
270
|
+
fallback: { builtAt: startedAt, basePath: template.basePath },
|
|
271
|
+
});
|
|
272
|
+
if (!options.keepSource) {
|
|
273
|
+
await rm(build, { recursive: true, force: true });
|
|
274
|
+
// Only removes `.build/` once it is empty; another version's kept build stays.
|
|
275
|
+
await rmdir(layout.buildDir).catch(() => undefined);
|
|
276
|
+
}
|
|
277
|
+
return prepared;
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (!options.keepSource) {
|
|
281
|
+
await rm(build, { recursive: true, force: true });
|
|
282
|
+
await rmdir(layout.buildDir).catch(() => undefined);
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* A deployment archive carries its database drivers in `dist/node_modules`, installed when it was built; nothing adds
|
|
289
|
+
* one afterwards. A dialect whose driver is absent would fail only when the application first connects.
|
|
290
|
+
*/
|
|
291
|
+
export function checkArchiveDriver(dir, dialect) {
|
|
292
|
+
if (dialect === 'sqlite')
|
|
293
|
+
return;
|
|
294
|
+
const driver = `@nocobase/db-${dialect}`;
|
|
295
|
+
if (existsSync(path.join(dir, 'dist/node_modules', driver, 'package.json'))) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
throw new InstallerError('DRIVER_MISSING', `The archive holds no ${driver}, which the ${dialect} dialect needs; a deployment archive carries the drivers it was built with.`, {
|
|
299
|
+
exitCode: EXIT_INVALID,
|
|
300
|
+
suggestions: [
|
|
301
|
+
{
|
|
302
|
+
message: 'Add the driver in the application project:',
|
|
303
|
+
run: { command: 'pnpm', args: ['add', driver] },
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
message: 'Then build the archive again:',
|
|
307
|
+
run: rebuildCommandLine(),
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* A release older than `APP_STORAGE_DIR` ignores it and keeps its data in `storage/` beside `dist/`, inside the release
|
|
314
|
+
* directory, where the next upgrade would leave it behind. Its first database write shows it.
|
|
315
|
+
*/
|
|
316
|
+
export function assertStorageOutsideRelease(dir) {
|
|
317
|
+
if (!existsSync(path.join(dir, 'storage')))
|
|
318
|
+
return;
|
|
319
|
+
throw new InstallerError('STORAGE_IN_RELEASE', `The release wrote its data into ${path.join(dir, 'storage')} instead of the installation's storage directory: its @nocobase/app-server predates APP_STORAGE_DIR.`, {
|
|
320
|
+
suggestions: [
|
|
321
|
+
{
|
|
322
|
+
message: 'Upgrade @nocobase/app-server and @nocobase/app-cli in the application project, then build the archive again:',
|
|
323
|
+
run: rebuildCommandLine(),
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=release.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release.js","sourceRoot":"","sources":["../../src/lib/release.ts"],"names":[],"mappings":"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"}
|