@nocobase/app-installer 0.0.0-stage → 0.1.0-beta.10
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 +263 -2
- package/bin/run.js +43 -0
- package/dist/cli.js +212 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/install.js +333 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/rollback.js +289 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/status.js +134 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.js +598 -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/app-source.js +163 -0
- package/dist/lib/app-source.js.map +1 -0
- package/dist/lib/backup.js +167 -0
- package/dist/lib/backup.js.map +1 -0
- package/dist/lib/checksum-repair.js +76 -0
- package/dist/lib/checksum-repair.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 +82 -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 +48 -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/offline-bundle.js +91 -0
- package/dist/lib/offline-bundle.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 +199 -0
- package/dist/lib/prechecks.js.map +1 -0
- package/dist/lib/registry.js +9 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/release.js +173 -0
- package/dist/lib/release.js.map +1 -0
- package/dist/lib/remote.js +331 -0
- package/dist/lib/remote.js.map +1 -0
- package/dist/lib/run-command.js +198 -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 +54 -0
- package/dist/lib/source.js.map +1 -0
- package/dist/lib/state.js +87 -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/dist/remote-entry.js +98 -0
- package/dist/remote-entry.js.map +1 -0
- package/package.json +53 -3
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { execFile, spawn } from 'node:child_process';
|
|
2
|
+
import { interruptError, onInterrupt, takeInterrupt } from "./interrupt.js";
|
|
3
|
+
export class CommandFailedError extends Error {
|
|
4
|
+
command;
|
|
5
|
+
exitCode;
|
|
6
|
+
stdout;
|
|
7
|
+
stderr;
|
|
8
|
+
constructor(command, exitCode, stdout, stderr, reason) {
|
|
9
|
+
super(reason ??
|
|
10
|
+
`${command} failed${exitCode === null ? '' : ` with exit code ${exitCode}`}.`);
|
|
11
|
+
this.name = 'CommandFailedError';
|
|
12
|
+
this.command = command;
|
|
13
|
+
this.exitCode = exitCode;
|
|
14
|
+
this.stdout = stdout;
|
|
15
|
+
this.stderr = stderr;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** How long a stopped command's process group gets to exit after SIGTERM before it is killed. */
|
|
19
|
+
const KILL_GRACE_MS = 10_000;
|
|
20
|
+
/** A bounded confirmation period after SIGKILL, including slow process-state checks. */
|
|
21
|
+
const STOP_CHECK_MS = 2000;
|
|
22
|
+
const POLL_MS = 50;
|
|
23
|
+
/** Linux and macOS ps expose zombies as Z; they cannot execute or write, even before their parent reaps them. */
|
|
24
|
+
async function groupIsRunning(pid) {
|
|
25
|
+
try {
|
|
26
|
+
process.kill(-pid, 0);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code === 'ESRCH')
|
|
30
|
+
return false;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
const states = await new Promise((resolve, reject) => {
|
|
34
|
+
// Read only group ids and states, never command lines or environment values. BSD and Linux ps support these fields.
|
|
35
|
+
execFile('ps', ['-A', '-o', 'pgid=,stat='], {
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
timeout: 1000,
|
|
38
|
+
killSignal: 'SIGKILL',
|
|
39
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
40
|
+
}, (error, stdout) => error
|
|
41
|
+
? reject(new Error('Cannot read command process states.', {
|
|
42
|
+
cause: error,
|
|
43
|
+
}))
|
|
44
|
+
: resolve(stdout));
|
|
45
|
+
});
|
|
46
|
+
return states
|
|
47
|
+
.trim()
|
|
48
|
+
.split('\n')
|
|
49
|
+
.some((line) => {
|
|
50
|
+
const [group, state] = line.trim().split(/\s+/);
|
|
51
|
+
return Number(group) === pid && !/^[ZX]/.test(state ?? '');
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Runs a child process, collecting its output, and rejects with `CommandFailedError` on a non-zero exit. The child leads
|
|
56
|
+
* its own process group, so a timeout or an interrupt stops everything it started, and the promise settles only once
|
|
57
|
+
* the group has no runnable members. If that cannot be confirmed within the stop deadline, the failure says so.
|
|
58
|
+
*/
|
|
59
|
+
export const runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
60
|
+
const label = [command, ...args].join(' ');
|
|
61
|
+
if (takeInterrupt()) {
|
|
62
|
+
reject(interruptError());
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const child = spawn(command, args, {
|
|
66
|
+
cwd: options.cwd,
|
|
67
|
+
env: options.env,
|
|
68
|
+
stdio: [options.input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'],
|
|
69
|
+
detached: true,
|
|
70
|
+
});
|
|
71
|
+
child.stdin?.on('error', () => {
|
|
72
|
+
/* A child may exit before reading stdin; close reports its status. */
|
|
73
|
+
});
|
|
74
|
+
child.stdin?.end(options.input);
|
|
75
|
+
let stdout = '';
|
|
76
|
+
let stderr = '';
|
|
77
|
+
let stopReason;
|
|
78
|
+
let settled = false;
|
|
79
|
+
let closed = false;
|
|
80
|
+
let groupStopped = false;
|
|
81
|
+
let exitCode = null;
|
|
82
|
+
let killTimer;
|
|
83
|
+
let pollTimer;
|
|
84
|
+
let deadlineTimer;
|
|
85
|
+
let stopError;
|
|
86
|
+
const signalGroup = (signal) => {
|
|
87
|
+
if (child.pid === undefined)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
process.kill(-child.pid, signal);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
// The group may disappear between observing it and signalling it. Other errors must not imply success.
|
|
94
|
+
if (error.code !== 'ESRCH')
|
|
95
|
+
stopError = error;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const checkStopped = async () => {
|
|
99
|
+
try {
|
|
100
|
+
const running = child.pid !== undefined && (await groupIsRunning(child.pid));
|
|
101
|
+
if (settled)
|
|
102
|
+
return;
|
|
103
|
+
if (!running) {
|
|
104
|
+
// Once every member is dead, none can create another member. Do not signal this group id again.
|
|
105
|
+
groupStopped = true;
|
|
106
|
+
clearTimeout(killTimer);
|
|
107
|
+
if (closed)
|
|
108
|
+
finish(exitCode);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
stopError = error;
|
|
114
|
+
}
|
|
115
|
+
if (!settled)
|
|
116
|
+
pollTimer = setTimeout(() => void checkStopped(), POLL_MS);
|
|
117
|
+
};
|
|
118
|
+
const stop = (reason) => {
|
|
119
|
+
if (stopReason || settled)
|
|
120
|
+
return;
|
|
121
|
+
stopReason = reason;
|
|
122
|
+
signalGroup('SIGTERM');
|
|
123
|
+
// These timers stay referenced even when the direct child closes its pipes and leaves no other Node handles.
|
|
124
|
+
killTimer = setTimeout(() => signalGroup('SIGKILL'), KILL_GRACE_MS);
|
|
125
|
+
deadlineTimer = setTimeout(() => {
|
|
126
|
+
const detail = stopError instanceof Error ? ` ${stopError.message}` : '';
|
|
127
|
+
finish(exitCode, `Could not confirm the command process group stopped.${detail}`);
|
|
128
|
+
child.stdin?.destroy();
|
|
129
|
+
child.stdout?.destroy();
|
|
130
|
+
child.stderr?.destroy();
|
|
131
|
+
child.unref();
|
|
132
|
+
}, KILL_GRACE_MS + STOP_CHECK_MS);
|
|
133
|
+
void checkStopped();
|
|
134
|
+
};
|
|
135
|
+
const timer = options.timeoutMs
|
|
136
|
+
? setTimeout(() => stop('timeout'), options.timeoutMs)
|
|
137
|
+
: undefined;
|
|
138
|
+
timer?.unref();
|
|
139
|
+
const stopListening = onInterrupt(() => stop('interrupt'));
|
|
140
|
+
child.stdout?.on('data', (chunk) => {
|
|
141
|
+
stdout += chunk.toString();
|
|
142
|
+
options.echo?.write(chunk);
|
|
143
|
+
});
|
|
144
|
+
child.stderr?.on('data', (chunk) => {
|
|
145
|
+
stderr += chunk.toString();
|
|
146
|
+
options.echo?.write(chunk);
|
|
147
|
+
});
|
|
148
|
+
const finish = (code, cleanupFailure, spawnFailure) => {
|
|
149
|
+
if (settled)
|
|
150
|
+
return;
|
|
151
|
+
settled = true;
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
clearTimeout(killTimer);
|
|
154
|
+
clearTimeout(pollTimer);
|
|
155
|
+
clearTimeout(deadlineTimer);
|
|
156
|
+
stopListening();
|
|
157
|
+
if (spawnFailure) {
|
|
158
|
+
reject(spawnFailure);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (stopReason === 'interrupt') {
|
|
162
|
+
// The interrupt is this step's failure; later recovery steps must not see it again.
|
|
163
|
+
takeInterrupt();
|
|
164
|
+
const error = interruptError();
|
|
165
|
+
if (cleanupFailure)
|
|
166
|
+
error.message = `Interrupted. ${cleanupFailure}`;
|
|
167
|
+
reject(error);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (stopReason === 'timeout') {
|
|
171
|
+
reject(new CommandFailedError(label, code, stdout, stderr, `${label} timed out after ${Math.round(options.timeoutMs / 1000)}s.${cleanupFailure ? ` ${cleanupFailure}` : ''}`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (code === 0) {
|
|
175
|
+
resolve({ stdout, stderr });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
reject(new CommandFailedError(label, code, stdout, stderr));
|
|
179
|
+
};
|
|
180
|
+
child.once('error', (error) => {
|
|
181
|
+
// A spawn failure has no process group. Errors after stopping begins must not bypass group confirmation.
|
|
182
|
+
if (stopReason && child.pid !== undefined)
|
|
183
|
+
stopError = error;
|
|
184
|
+
else
|
|
185
|
+
finish(null, undefined, error);
|
|
186
|
+
});
|
|
187
|
+
child.once('close', (code) => {
|
|
188
|
+
closed = true;
|
|
189
|
+
exitCode = code;
|
|
190
|
+
if (!stopReason || groupStopped)
|
|
191
|
+
finish(code);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
/** The last `lines` lines of a command's output, for an error message that has to fit on a screen. */
|
|
195
|
+
export function tail(text, lines = 20) {
|
|
196
|
+
return text.trimEnd().split('\n').slice(-lines).join('\n');
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=run-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-command.js","sourceRoot":"","sources":["../../src/lib/run-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { EXIT_INVALID, InstallerError, } from "./errors.js";
|
|
5
|
+
import { checkHealth, pm2StartFailed, waitForHealthy } from "./health.js";
|
|
6
|
+
import { interruptError, takeInterrupt } from "./interrupt.js";
|
|
7
|
+
import { tail } from "./run-command.js";
|
|
8
|
+
function sameDirectory(a, b) {
|
|
9
|
+
const real = (dir) => {
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(dir);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return path.resolve(dir);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return real(a) === real(b);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The pm2 process under the installation's name must be this installation's. Otherwise stopping it would stop another
|
|
21
|
+
* application, and `pm2 start` on the taken name would restart that process with this one's configuration.
|
|
22
|
+
*/
|
|
23
|
+
export async function checkPm2Ownership(options) {
|
|
24
|
+
const known = await options.pm2.describe(options.name);
|
|
25
|
+
if (known?.cwd && !sameDirectory(known.cwd, options.layout.root)) {
|
|
26
|
+
throw new InstallerError('PM2_NAME_IN_USE', `The pm2 process ${options.name} runs from ${known.cwd}, not from ${options.layout.root}.`, {
|
|
27
|
+
exitCode: EXIT_INVALID,
|
|
28
|
+
suggestions: [
|
|
29
|
+
{
|
|
30
|
+
message: `Stop that process or rename it; installer.json names ${options.name} for this installation.`,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Stops the application and waits until its health route stops answering. pm2 sends SIGINT and allows `kill_timeout`
|
|
38
|
+
* for it to finish what it is doing. An application pm2 does not know, such as one installed with `--no-start`, is
|
|
39
|
+
* simply not running.
|
|
40
|
+
*/
|
|
41
|
+
export async function stopApp(options) {
|
|
42
|
+
const known = await options.pm2.describe(options.name);
|
|
43
|
+
if (known)
|
|
44
|
+
await options.pm2.stop(options.name);
|
|
45
|
+
for (let attempt = 0; attempt < 60; attempt += 1) {
|
|
46
|
+
if (takeInterrupt())
|
|
47
|
+
throw interruptError();
|
|
48
|
+
if (!(await checkHealth(options.healthUrl, options.fetchImpl)))
|
|
49
|
+
return;
|
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, 1_000));
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`${options.healthUrl} still answers after stopping ${options.name}; another process may be serving the application's port.`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Starts whatever `current` points at and waits for it to be healthy. The process entry is deleted and registered again
|
|
56
|
+
* rather than restarted, so pm2's restart count starts from zero — the crash detection reads it — and a changed
|
|
57
|
+
* `ecosystem.config.cjs` is read again. A process pm2 reports as crashed ends the wait early.
|
|
58
|
+
*/
|
|
59
|
+
export async function startApp(options) {
|
|
60
|
+
await options.pm2.remove(options.name);
|
|
61
|
+
await options.pm2.start(options.layout.ecosystemFile, options.layout.root);
|
|
62
|
+
const healthy = await waitForHealthy(options.healthUrl, {
|
|
63
|
+
timeoutMs: options.timeoutMs,
|
|
64
|
+
fetchImpl: options.fetchImpl,
|
|
65
|
+
failed: pm2StartFailed(options.pm2, options.name),
|
|
66
|
+
});
|
|
67
|
+
if (healthy) {
|
|
68
|
+
await options.pm2.save();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// A release that cannot start would otherwise be restarted by pm2 until it gives up.
|
|
72
|
+
await options.pm2.remove(options.name).catch(() => undefined);
|
|
73
|
+
}
|
|
74
|
+
return healthy;
|
|
75
|
+
}
|
|
76
|
+
export async function errorLogTail(layout) {
|
|
77
|
+
const text = await readFile(layout.errorLog, 'utf8').catch(() => '');
|
|
78
|
+
return tail(text, 30);
|
|
79
|
+
}
|
|
80
|
+
/** The command that shows more of the error log than `details.log` carries. */
|
|
81
|
+
export function errorLogCommandLine(layout) {
|
|
82
|
+
return { command: 'tail', args: ['-n', '100', layout.errorLog] };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* How to start the installation by hand once the cause is fixed. Two steps, since a suggestion runs one command: pm2
|
|
86
|
+
* starts it, then saves the process list so that `pm2 startup` brings it back after a reboot.
|
|
87
|
+
*/
|
|
88
|
+
export function startAdvice(layout, message) {
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
message,
|
|
92
|
+
run: { command: 'pm2', args: ['start', layout.ecosystemFile] },
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
message: 'Then save the process list, so pm2 restores it after a reboot:',
|
|
96
|
+
run: { command: 'pm2', args: ['save'] },
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/lib/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { EXIT_INVALID, InstallerError } from "./errors.js";
|
|
4
|
+
import { quoteForShell } from '@nocobase/cli-envelope';
|
|
5
|
+
import { installerCommand, installerCommandLine } from "./invocation.js";
|
|
6
|
+
import { rebuildCommandLine } from "./prechecks.js";
|
|
7
|
+
/**
|
|
8
|
+
* The archive `--archive` names, as an absolute path to a file that exists. Only a local path is accepted for now:
|
|
9
|
+
* fetching one would need downloads, credentials and checksums of its own.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveArchivePath(cwd, value) {
|
|
12
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//iu.test(value)) {
|
|
13
|
+
throw new InstallerError('INVALID_USAGE', `--archive takes a local path; download ${value} to the server first.`, { exitCode: EXIT_INVALID });
|
|
14
|
+
}
|
|
15
|
+
const file = path.resolve(cwd, value);
|
|
16
|
+
let isFile = false;
|
|
17
|
+
try {
|
|
18
|
+
isFile = statSync(file).isFile();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Reported below.
|
|
22
|
+
}
|
|
23
|
+
if (!isFile) {
|
|
24
|
+
throw new InstallerError('ARCHIVE_NOT_FOUND', `${file} is not a file. Pass the deployment archive \`pnpm build --tar\` wrote to storage/exports/dist.tar.gz.`, { exitCode: EXIT_INVALID });
|
|
25
|
+
}
|
|
26
|
+
return file;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* How to get a release that runs on this machine's Node: the archive is built again, in the application project, and
|
|
30
|
+
* the installation upgrades to it.
|
|
31
|
+
*/
|
|
32
|
+
export function nodeRebuildAdvice(state, root) {
|
|
33
|
+
if (state.source.kind === 'app') {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
message: 'Rebuild the installed application for this machine:',
|
|
37
|
+
run: installerCommandLine(['upgrade', '--dir', root, '--rebuild'], {
|
|
38
|
+
registry: state.registry,
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
message: 'Build the archive again for this machine, in the application project:',
|
|
46
|
+
run: rebuildCommandLine(),
|
|
47
|
+
},
|
|
48
|
+
// No `run`: a suggestion's command runs as given, and the archive's path is not known here.
|
|
49
|
+
{
|
|
50
|
+
message: `Then copy it here and upgrade to it: ${installerCommand(`upgrade --dir ${quoteForShell(root)} --archive <the copied archive>`, { registry: state.registry })}`,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/lib/source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,KAAa;IAC3D,IAAI,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,cAAc,CACtB,eAAe,EACf,0CAA0C,KAAK,uBAAuB,EACtE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAC3B,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACtC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,cAAc,CACtB,mBAAmB,EACnB,GAAG,IAAI,wGAAwG,EAC/G,EAAE,QAAQ,EAAE,YAAY,EAAE,CAC3B,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAkD,EAClD,IAAY;IAEZ,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAChC,OAAO;YACL;gBACE,OAAO,EAAE,qDAAqD;gBAC9D,GAAG,EAAE,oBAAoB,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE;oBACjE,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACzB,CAAC;aACH;SACF,CAAC;IACJ,CAAC;IACD,OAAO;QACL;YACE,OAAO,EACL,uEAAuE;YACzE,GAAG,EAAE,kBAAkB,EAAE;SAC1B;QACD,4FAA4F;QAC5F;YACE,OAAO,EAAE,wCAAwC,gBAAgB,CAAC,iBAAiB,aAAa,CAAC,IAAI,CAAC,iCAAiC,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE;SACzK;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { EXIT_INVALID, InstallerError } from "./errors.js";
|
|
3
|
+
import { installerCommandLine } from "./invocation.js";
|
|
4
|
+
import { compareVersions } from "./version.js";
|
|
5
|
+
/** Where a release has to be mounted, or `undefined` when it can be mounted anywhere. */
|
|
6
|
+
export function fixedMountPathOf(record, state) {
|
|
7
|
+
return record.relocatable ? undefined : (record.basePath ?? state.basePath);
|
|
8
|
+
}
|
|
9
|
+
export async function readState(layout) {
|
|
10
|
+
let text;
|
|
11
|
+
try {
|
|
12
|
+
text = await readFile(layout.stateFile, 'utf8');
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === 'ENOENT') {
|
|
16
|
+
throw new InstallerError('NOT_INSTALLED', `${layout.root} holds no application installed by app-installer (installer.json is missing).`, {
|
|
17
|
+
exitCode: EXIT_INVALID,
|
|
18
|
+
suggestions: [
|
|
19
|
+
{
|
|
20
|
+
message: 'Run the command from the installation root, or name it with --dir.',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
const state = JSON.parse(text);
|
|
28
|
+
if (state.schemaVersion !== 1 && state.schemaVersion !== 2) {
|
|
29
|
+
throw new InstallerError('STATE_UNSUPPORTED', `installer.json has schemaVersion ${String(state.schemaVersion)}, which this app-installer does not understand.`, {
|
|
30
|
+
exitCode: EXIT_INVALID,
|
|
31
|
+
suggestions: [
|
|
32
|
+
{
|
|
33
|
+
message: 'Use a newer app-installer:',
|
|
34
|
+
run: installerCommandLine(['status', '--dir', layout.root], {
|
|
35
|
+
version: 'latest',
|
|
36
|
+
...(typeof state.registry === 'string'
|
|
37
|
+
? { registry: state.registry }
|
|
38
|
+
: {}),
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// `{ kind: 'template' }` was the published Hub template built on the server, which this installer no longer offers.
|
|
45
|
+
if (state.source?.kind !== 'archive' &&
|
|
46
|
+
!(state.source?.kind === 'app' &&
|
|
47
|
+
typeof state.source.package === 'string' &&
|
|
48
|
+
state.source.package === state.appName)) {
|
|
49
|
+
throw new InstallerError('STATE_UNSUPPORTED', `${layout.root} was installed from a published template, which this app-installer no longer builds; use the installer version that created it.`, {
|
|
50
|
+
exitCode: EXIT_INVALID,
|
|
51
|
+
suggestions: [
|
|
52
|
+
{
|
|
53
|
+
message: 'Keep managing it with the app-installer version that installed it, or install the application again from a deployment archive built by `pnpm build --tar`.',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
/** Writes the state through a temporary file and a rename, so a crash never leaves half a document behind. */
|
|
61
|
+
export async function writeState(layout, state) {
|
|
62
|
+
const temporary = `${layout.stateFile}.${process.pid}.tmp`;
|
|
63
|
+
await writeFile(temporary, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
|
|
64
|
+
await rename(temporary, layout.stateFile);
|
|
65
|
+
}
|
|
66
|
+
export function findRelease(state, id) {
|
|
67
|
+
return state.releases.find((record) => record.id === id);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The release a `--to` names: an id exactly, or a version, meaning the newest build of it on record other than the
|
|
71
|
+
* running one. A version is what a person remembers; the id is what `status` prints when two builds of one version are
|
|
72
|
+
* on disk. Rolling back "to 1.0.0" while a later build of 1.0.0 runs means the earlier build, not the one already running.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveReleaseRef(state, ref) {
|
|
75
|
+
const exact = findRelease(state, ref);
|
|
76
|
+
if (exact)
|
|
77
|
+
return exact;
|
|
78
|
+
const builds = [...state.releases]
|
|
79
|
+
.filter((record) => record.version === ref)
|
|
80
|
+
.sort((a, b) => b.builtAt.localeCompare(a.builtAt));
|
|
81
|
+
return builds.find((record) => record.id !== state.current) ?? builds[0];
|
|
82
|
+
}
|
|
83
|
+
/** Semantic-version order of two releases' versions; builds of one version compare equal. */
|
|
84
|
+
export function compareReleaseVersions(a, b) {
|
|
85
|
+
return compareVersions(a.version, b.version) ?? 0;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function parse(version) {
|
|
2
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/u.exec(version.trim());
|
|
3
|
+
if (!match)
|
|
4
|
+
return undefined;
|
|
5
|
+
return {
|
|
6
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
7
|
+
prerelease: match[4] ? match[4].split('.') : [],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function compareIdentifiers(a, b) {
|
|
11
|
+
const numeric = /^\d+$/u;
|
|
12
|
+
if (numeric.test(a) && numeric.test(b))
|
|
13
|
+
return Number(a) - Number(b);
|
|
14
|
+
if (numeric.test(a))
|
|
15
|
+
return -1;
|
|
16
|
+
if (numeric.test(b))
|
|
17
|
+
return 1;
|
|
18
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Semantic version order: negative when `a` sorts first, positive when `b` does, zero when equal, and `undefined` when
|
|
22
|
+
* either is not a version at all. A prerelease sorts below its release, and `1.0.0-beta.10` above `1.0.0-beta.9`.
|
|
23
|
+
*/
|
|
24
|
+
export function compareVersions(a, b) {
|
|
25
|
+
const left = parse(a);
|
|
26
|
+
const right = parse(b);
|
|
27
|
+
if (!left || !right)
|
|
28
|
+
return undefined;
|
|
29
|
+
for (let index = 0; index < 3; index += 1) {
|
|
30
|
+
const difference = left.core[index] - right.core[index];
|
|
31
|
+
if (difference !== 0)
|
|
32
|
+
return difference;
|
|
33
|
+
}
|
|
34
|
+
if (left.prerelease.length === 0 || right.prerelease.length === 0) {
|
|
35
|
+
return right.prerelease.length - left.prerelease.length;
|
|
36
|
+
}
|
|
37
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
38
|
+
for (let index = 0; index < length; index += 1) {
|
|
39
|
+
const x = left.prerelease[index];
|
|
40
|
+
const y = right.prerelease[index];
|
|
41
|
+
if (x === undefined)
|
|
42
|
+
return -1;
|
|
43
|
+
if (y === undefined)
|
|
44
|
+
return 1;
|
|
45
|
+
const difference = compareIdentifiers(x, y);
|
|
46
|
+
if (difference !== 0)
|
|
47
|
+
return difference;
|
|
48
|
+
}
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
import { readAppEnv, endpointsOf } from "./lib/env-file.js";
|
|
3
|
+
import { layoutOf } from "./lib/layout.js";
|
|
4
|
+
/** Private SSH entry: the request and secrets arrive over stdin, never in process arguments or files. */
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
import { runInstaller } from "./cli.js";
|
|
10
|
+
import { INSTALL_FLAGS } from "./commands/install.js";
|
|
11
|
+
import { UPGRADE_FLAGS } from "./commands/upgrade.js";
|
|
12
|
+
import { ROLLBACK_FLAGS } from "./commands/rollback.js";
|
|
13
|
+
import { STATUS_FLAGS } from "./commands/status.js";
|
|
14
|
+
import { errorEnvelope, exitCodeOf } from "./lib/output.js";
|
|
15
|
+
import { EXIT_INVALID, InstallerError } from "./lib/errors.js";
|
|
16
|
+
import { sha256 } from "./lib/remote.js";
|
|
17
|
+
let input = '';
|
|
18
|
+
for await (const chunk of process.stdin)
|
|
19
|
+
input += String(chunk);
|
|
20
|
+
let command = 'remote';
|
|
21
|
+
try {
|
|
22
|
+
const request = JSON.parse(input);
|
|
23
|
+
command = request.command;
|
|
24
|
+
const root = String(request.flags.dir);
|
|
25
|
+
if (!path.isAbsolute(root) || root === '/')
|
|
26
|
+
throw new InstallerError('INVALID_USAGE', 'An absolute application directory is required.');
|
|
27
|
+
if (request.archive &&
|
|
28
|
+
(await sha256(request.archive.file)) !== request.archive.sha256)
|
|
29
|
+
throw new InstallerError('CHECKSUM_MISMATCH', 'Application archive checksum does not match.');
|
|
30
|
+
for (const [key, value] of Object.entries(request.env)) {
|
|
31
|
+
if (!/^NOCOBASE_REMOTE_SECRET_\d+$/u.test(key))
|
|
32
|
+
throw new InstallerError('INVALID_USAGE', 'Invalid transported environment variable.');
|
|
33
|
+
process.env[key] = value;
|
|
34
|
+
}
|
|
35
|
+
const stateFile = path.join(root, 'installer.json');
|
|
36
|
+
if (command === 'deploy')
|
|
37
|
+
command = existsSync(stateFile) ? 'upgrade' : 'install';
|
|
38
|
+
if (command === 'install' && request.flags['repair-checksums'] !== undefined)
|
|
39
|
+
throw new InstallerError('INVALID_USAGE', 'Checksum repair approval applies only to upgrading an existing installation.', { exitCode: EXIT_INVALID });
|
|
40
|
+
if (command === 'upgrade') {
|
|
41
|
+
const state = JSON.parse(await readFile(stateFile, 'utf8'));
|
|
42
|
+
if (state.source?.kind !== 'archive')
|
|
43
|
+
throw new InstallerError('REMOTE_SOURCE_UNSUPPORTED', 'Offline SSH upgrades require an archive installation.');
|
|
44
|
+
const installOnly = request.explicit.filter((key) => key in INSTALL_FLAGS && !(key in UPGRADE_FLAGS) && key !== 'json');
|
|
45
|
+
if (installOnly.length)
|
|
46
|
+
throw new InstallerError('INVALID_USAGE', `An existing installation preserves its configuration; remove install-only flags: ${installOnly.map((x) => '--' + x).join(', ')}.`);
|
|
47
|
+
}
|
|
48
|
+
const definitions = {
|
|
49
|
+
install: INSTALL_FLAGS,
|
|
50
|
+
upgrade: UPGRADE_FLAGS,
|
|
51
|
+
rollback: ROLLBACK_FLAGS,
|
|
52
|
+
status: STATUS_FLAGS,
|
|
53
|
+
};
|
|
54
|
+
if (!(command in definitions))
|
|
55
|
+
throw new InstallerError('INVALID_USAGE', 'Unsupported remote operation.');
|
|
56
|
+
const definition = definitions[command];
|
|
57
|
+
const argv = [command, '--json'];
|
|
58
|
+
for (const [key, value] of Object.entries(request.flags)) {
|
|
59
|
+
if (key === 'json' || !(key in definition) || value === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
if (typeof value === 'boolean') {
|
|
62
|
+
if (value)
|
|
63
|
+
argv.push('--' + key);
|
|
64
|
+
else if (['start', 'restore'].includes(key))
|
|
65
|
+
argv.push('--no-' + key);
|
|
66
|
+
}
|
|
67
|
+
else if (Array.isArray(value))
|
|
68
|
+
for (const item of value)
|
|
69
|
+
argv.push(`--${key}=${item}`);
|
|
70
|
+
else
|
|
71
|
+
argv.push(`--${key}=${value}`);
|
|
72
|
+
}
|
|
73
|
+
const metadata = createRequire(import.meta.url)('../package.json');
|
|
74
|
+
let output = '';
|
|
75
|
+
const stdout = new Writable({
|
|
76
|
+
write(chunk, _encoding, callback) {
|
|
77
|
+
output += chunk.toString();
|
|
78
|
+
callback();
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
process.exitCode = await runInstaller({
|
|
82
|
+
argv,
|
|
83
|
+
version: metadata.version,
|
|
84
|
+
stdout,
|
|
85
|
+
});
|
|
86
|
+
const envelope = JSON.parse(output);
|
|
87
|
+
if (envelope.ok &&
|
|
88
|
+
envelope.result &&
|
|
89
|
+
['upgrade', 'rollback'].includes(command)) {
|
|
90
|
+
envelope.result.endpoints = endpointsOf(await readAppEnv(layoutOf(root)));
|
|
91
|
+
}
|
|
92
|
+
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
process.stdout.write(JSON.stringify(errorEnvelope(command, error, [])) + '\n');
|
|
96
|
+
process.exitCode = exitCodeOf(error);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=remote-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-entry.js","sourceRoot":"","sources":["../src/remote-entry.ts"],"names":[],"mappings":"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"}
|