@nocobase/app-installer 0.0.0-stage → 0.1.0-beta.4

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.
Files changed (61) hide show
  1. package/README.md +199 -2
  2. package/bin/run.js +43 -0
  3. package/dist/cli.js +168 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/install.js +319 -0
  6. package/dist/commands/install.js.map +1 -0
  7. package/dist/commands/rollback.js +284 -0
  8. package/dist/commands/rollback.js.map +1 -0
  9. package/dist/commands/status.js +132 -0
  10. package/dist/commands/status.js.map +1 -0
  11. package/dist/commands/upgrade.js +549 -0
  12. package/dist/commands/upgrade.js.map +1 -0
  13. package/dist/lib/app-cli.js +89 -0
  14. package/dist/lib/app-cli.js.map +1 -0
  15. package/dist/lib/backup.js +167 -0
  16. package/dist/lib/backup.js.map +1 -0
  17. package/dist/lib/confirm.js +36 -0
  18. package/dist/lib/confirm.js.map +1 -0
  19. package/dist/lib/current-link.js +22 -0
  20. package/dist/lib/current-link.js.map +1 -0
  21. package/dist/lib/ecosystem.js +82 -0
  22. package/dist/lib/ecosystem.js.map +1 -0
  23. package/dist/lib/env-file.js +87 -0
  24. package/dist/lib/env-file.js.map +1 -0
  25. package/dist/lib/errors.js +32 -0
  26. package/dist/lib/errors.js.map +1 -0
  27. package/dist/lib/health.js +55 -0
  28. package/dist/lib/health.js.map +1 -0
  29. package/dist/lib/initial-admin.js +39 -0
  30. package/dist/lib/initial-admin.js.map +1 -0
  31. package/dist/lib/interrupt.js +50 -0
  32. package/dist/lib/interrupt.js.map +1 -0
  33. package/dist/lib/invocation.js +33 -0
  34. package/dist/lib/invocation.js.map +1 -0
  35. package/dist/lib/layout.js +48 -0
  36. package/dist/lib/layout.js.map +1 -0
  37. package/dist/lib/lock.js +59 -0
  38. package/dist/lib/lock.js.map +1 -0
  39. package/dist/lib/mount-path.js +34 -0
  40. package/dist/lib/mount-path.js.map +1 -0
  41. package/dist/lib/output.js +61 -0
  42. package/dist/lib/output.js.map +1 -0
  43. package/dist/lib/pm2.js +92 -0
  44. package/dist/lib/pm2.js.map +1 -0
  45. package/dist/lib/prechecks.js +199 -0
  46. package/dist/lib/prechecks.js.map +1 -0
  47. package/dist/lib/registry.js +9 -0
  48. package/dist/lib/registry.js.map +1 -0
  49. package/dist/lib/release.js +173 -0
  50. package/dist/lib/release.js.map +1 -0
  51. package/dist/lib/run-command.js +108 -0
  52. package/dist/lib/run-command.js.map +1 -0
  53. package/dist/lib/service.js +100 -0
  54. package/dist/lib/service.js.map +1 -0
  55. package/dist/lib/source.js +44 -0
  56. package/dist/lib/source.js.map +1 -0
  57. package/dist/lib/state.js +84 -0
  58. package/dist/lib/state.js.map +1 -0
  59. package/dist/lib/version.js +51 -0
  60. package/dist/lib/version.js.map +1 -0
  61. package/package.json +51 -3
@@ -0,0 +1,549 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { Flags } from '@oclif/core';
5
+ import { pendingTaskCount, runAppCli } from "../lib/app-cli.js";
6
+ import { backupForUpgrade, backupName, readDatabaseInventory, removeBackup, restoreDatabase, } from "../lib/backup.js";
7
+ import { confirm } from "../lib/confirm.js";
8
+ import { switchCurrent } from "../lib/current-link.js";
9
+ import { healthUrl, mountPathOf, readAppEnv } from "../lib/env-file.js";
10
+ import { assertFixedMountPath } from "../lib/mount-path.js";
11
+ import { EXIT_FAILED, EXIT_INVALID, EXIT_ROLLBACK_FAILED, EXIT_ROLLED_BACK, InstallerError, } from "../lib/errors.js";
12
+ import { quoteForShell } from '@nocobase/cli-envelope';
13
+ import { installerCommand, installerCommandLine } from "../lib/invocation.js";
14
+ import { layoutOf, releaseDir, releaseLinkTarget, } from "../lib/layout.js";
15
+ import { acquireLock } from "../lib/lock.js";
16
+ import { checkPlatform, checkPm2, checkPortFree, currentNodeMajor, } from "../lib/prechecks.js";
17
+ import { assertStorageOutsideRelease, checkArchiveDriver, unpackRelease, } from "../lib/release.js";
18
+ import { runCommand } from "../lib/run-command.js";
19
+ import { resolveArchivePath } from "../lib/source.js";
20
+ import { checkPm2Ownership, errorLogCommandLine, errorLogTail, startAdvice, startApp, stopApp, } from "../lib/service.js";
21
+ import { findRelease, fixedMountPathOf, readState, writeState, } from "../lib/state.js";
22
+ import { compareVersions } from "../lib/version.js";
23
+ export const UPGRADE_FLAGS = {
24
+ dir: Flags.string({
25
+ description: 'Installation root managed by app-installer. Defaults to the current directory.',
26
+ }),
27
+ archive: Flags.string({
28
+ description: 'The new deployment archive, built by `pnpm build --tar` for this machine.',
29
+ }),
30
+ 'backup-done': Flags.boolean({
31
+ default: false,
32
+ description: 'Confirm that the databases app-installer cannot back up, any but SQLite, are backed up.',
33
+ }),
34
+ 'health-timeout': Flags.integer({
35
+ default: 180,
36
+ min: 1,
37
+ description: 'Seconds to wait for the new release to answer its health check.',
38
+ }),
39
+ keep: Flags.integer({
40
+ default: 3,
41
+ min: 2,
42
+ description: 'Releases to keep on disk, counting the new one; the one upgraded from is always kept so rollback stays possible.',
43
+ }),
44
+ yes: Flags.boolean({
45
+ default: false,
46
+ description: 'Proceed without asking for confirmation.',
47
+ }),
48
+ json: Flags.boolean({
49
+ default: false,
50
+ description: 'Print one JSON result on stdout; progress stays on stderr.',
51
+ }),
52
+ };
53
+ /** The installation was interrupted by a previous operation that never finished; refuse to stack another on top. */
54
+ export function assertNoPending(state, root) {
55
+ if (!state.pending)
56
+ return;
57
+ const { action, from, to, startedAt } = state.pending;
58
+ throw new InstallerError('OPERATION_INTERRUPTED', `${action === 'upgrade' ? 'An upgrade from' : 'A rollback from'} ${from} to ${to}, started ${startedAt}, did not finish; the application may be stopped or half-switched.`, {
59
+ exitCode: EXIT_INVALID,
60
+ suggestions: [
61
+ {
62
+ message: 'Recover first; an interrupted upgrade is undone and an interrupted rollback is finished:',
63
+ run: installerCommandLine(['rollback', '--dir', root], {
64
+ registry: state.registry,
65
+ }),
66
+ },
67
+ ],
68
+ });
69
+ }
70
+ /**
71
+ * Keeps the newest `keep` releases by install time. `protect` — the current release and the one it was upgraded from,
72
+ * which `rollback` returns to — is never pruned, whatever its install time.
73
+ */
74
+ export function releasesToPrune(releases, protect, keep) {
75
+ const newestFirst = [...releases].sort((a, b) => b.installedAt.localeCompare(a.installedAt));
76
+ const kept = new Set(protect);
77
+ for (const record of newestFirst) {
78
+ if (kept.size >= keep)
79
+ break;
80
+ kept.add(record.id);
81
+ }
82
+ return releases.filter((record) => !kept.has(record.id));
83
+ }
84
+ function recordOf(prepared) {
85
+ return {
86
+ id: prepared.id,
87
+ version: prepared.version,
88
+ builtAt: prepared.builtAt,
89
+ installedAt: new Date().toISOString(),
90
+ buildTarget: prepared.buildTarget,
91
+ ...(prepared.relocatable
92
+ ? { relocatable: true }
93
+ : { basePath: prepared.basePath }),
94
+ };
95
+ }
96
+ function replaceRecord(state, record) {
97
+ state.releases = [
98
+ ...state.releases.filter((entry) => entry.id !== record.id),
99
+ record,
100
+ ];
101
+ }
102
+ /** An upgrade moves to another deployment archive, which `--archive` names. */
103
+ function requireArchive(state, root, flags) {
104
+ if (flags.archive === undefined) {
105
+ throw new InstallerError('INVALID_USAGE', `${root} upgrades from a deployment archive; pass the new one with --archive.`, {
106
+ exitCode: EXIT_INVALID,
107
+ // No `run`: a suggestion's command runs as printed, and the archive's path is not known here.
108
+ suggestions: [
109
+ {
110
+ message: `Build it for this machine in the application project, copy it to the server, then run: ${installerCommand(`upgrade --dir ${quoteForShell(root)} --archive <the copied archive>`, { registry: state.registry })}`,
111
+ },
112
+ ],
113
+ });
114
+ }
115
+ return flags.archive;
116
+ }
117
+ /**
118
+ * The archive must be a later build of the same application: its package name owns the migration history in the
119
+ * database. An archive from before relocatable builds also has its base path compiled into the client, so it has to
120
+ * be the path the installation serves now.
121
+ */
122
+ function checkArchiveMatches(state, prepared, env) {
123
+ if (prepared.appName !== state.appName) {
124
+ throw new InstallerError('APP_MISMATCH', `The archive holds ${prepared.appName}, but this installation runs ${state.appName}; a different application has a migration history of its own.`, {
125
+ exitCode: EXIT_INVALID,
126
+ suggestions: [
127
+ {
128
+ message: 'Install a different application into a directory of its own with install --archive.',
129
+ },
130
+ ],
131
+ });
132
+ }
133
+ if (!prepared.relocatable) {
134
+ assertFixedMountPath('The archive', prepared.basePath, mountPathOf(env));
135
+ }
136
+ }
137
+ /**
138
+ * Undoes an upgrade that failed after the switch: the new release is stopped, `current` goes back, the databases are
139
+ * restored when the new release may have migrated them, and the previous release is started again. Always throws: exit
140
+ * 3 when the previous release is healthy again, exit 4 when it is not.
141
+ *
142
+ * When the previous release does not come back, the operation stays pending and the new release stays on disk and on
143
+ * record: it may be the only one that can run, as when the machine's Node major changed, and `rollback` recovers from
144
+ * the pending state.
145
+ */
146
+ async function rollBackUpgrade(context) {
147
+ const { layout, state, service, from, to, backup } = context;
148
+ const reason = context.cause instanceof Error
149
+ ? context.cause.message
150
+ : String(context.cause);
151
+ await service.pm2.remove(service.name).catch(() => undefined);
152
+ const log = await errorLogTail(layout);
153
+ const hasDatabase = backup.databases.some((database) => database.files.length > 0);
154
+ let databaseRestored = false;
155
+ let healthy = false;
156
+ let rollbackError;
157
+ try {
158
+ await switchCurrent(layout, releaseLinkTarget(from.id));
159
+ if (context.pending > 0 && hasDatabase) {
160
+ await restoreDatabase(layout, backup.relative);
161
+ databaseRestored = true;
162
+ }
163
+ healthy = await startApp({ ...service, timeoutMs: context.timeoutMs });
164
+ }
165
+ catch (error) {
166
+ rollbackError = error;
167
+ }
168
+ const kept = context.newRecord !== undefined;
169
+ if (healthy) {
170
+ if (kept) {
171
+ await rm(path.dirname(releaseDir(layout, to.id)), {
172
+ recursive: true,
173
+ force: true,
174
+ });
175
+ }
176
+ delete state.pending;
177
+ }
178
+ else if (kept) {
179
+ replaceRecord(state, context.newRecord);
180
+ }
181
+ state.history.push({
182
+ action: 'upgrade',
183
+ from: from.id,
184
+ to: to.id,
185
+ at: new Date().toISOString(),
186
+ outcome: 'rolled-back',
187
+ migrations: context.pending,
188
+ backup: backup.relative,
189
+ databaseRestored,
190
+ });
191
+ await writeState(layout, state);
192
+ const doing = `Upgrading to ${to.id}`;
193
+ const externalDatabaseNote = context.pending > 0 && !hasDatabase
194
+ ? ` ${to.id} may already have migrated the external database; restore it from your own backup if ${from.id} misbehaves.`
195
+ : '';
196
+ if (healthy) {
197
+ throw new InstallerError('UPGRADE_ROLLED_BACK', `${doing} failed (${reason}). ${from.id} is running again${databaseRestored ? ' on the databases restored from the backup' : ''}.${externalDatabaseNote}`, {
198
+ exitCode: EXIT_ROLLED_BACK,
199
+ details: { log, backup: backup.relative, databaseRestored },
200
+ suggestions: [
201
+ {
202
+ message: 'The failed release logged:',
203
+ run: errorLogCommandLine(layout),
204
+ },
205
+ ],
206
+ });
207
+ }
208
+ throw new InstallerError('ROLLBACK_FAILED', `${doing} failed (${reason}), and ${from.id} did not come back either${rollbackError instanceof Error ? ` (${rollbackError.message})` : ''}. The application is down.`, {
209
+ exitCode: EXIT_ROLLBACK_FAILED,
210
+ details: { log, backup: backup.relative, databaseRestored },
211
+ suggestions: [
212
+ {
213
+ message: 'Read the error log:',
214
+ run: errorLogCommandLine(layout),
215
+ },
216
+ ...(hasDatabase && !databaseRestored
217
+ ? [
218
+ {
219
+ message: `The databases from before the upgrade are in ${path.join(layout.root, backup.relative)}; the rollback below restores them from there.`,
220
+ },
221
+ ]
222
+ : []),
223
+ {
224
+ message: 'Once the cause is fixed, finish the rollback; it restores the databases from the backup if needed:',
225
+ run: installerCommandLine(['rollback', '--dir', layout.root], {
226
+ registry: state.registry,
227
+ }),
228
+ },
229
+ ...(kept
230
+ ? [
231
+ {
232
+ message: `Or return to ${to.id}, which was kept:`,
233
+ run: installerCommandLine([
234
+ 'rollback',
235
+ '--dir',
236
+ layout.root,
237
+ '--to',
238
+ to.id,
239
+ '--no-restore',
240
+ ], { registry: state.registry }),
241
+ },
242
+ ]
243
+ : []),
244
+ ],
245
+ });
246
+ }
247
+ export async function upgrade(input, deps) {
248
+ const { flags } = input;
249
+ const { reporter, pm2 } = deps;
250
+ const root = path.resolve(deps.cwd ?? process.cwd(), flags.dir ?? '.');
251
+ const layout = layoutOf(root);
252
+ const run = deps.run ?? runCommand;
253
+ const releaseLock = await acquireLock(layout.lockFile);
254
+ try {
255
+ // Read under the lock: a run that waited on it must see what the previous one wrote.
256
+ const state = await readState(layout);
257
+ assertNoPending(state, root);
258
+ const archive = resolveArchivePath(deps.cwd ?? process.cwd(), requireArchive(state, root, flags));
259
+ const env = await readAppEnv(layout);
260
+ const from = findRelease(state, state.current);
261
+ if (!from) {
262
+ throw new InstallerError('RELEASE_MISSING', `installer.json names ${state.current} as the running release but holds no record of it.`, { exitCode: EXIT_INVALID });
263
+ }
264
+ const machineMajor = currentNodeMajor();
265
+ const nodeChanged = from.buildTarget.nodeMajor !== machineMajor;
266
+ const noop = (warning) => {
267
+ if (warning)
268
+ reporter.warn(warning);
269
+ return {
270
+ status: 'success-noop',
271
+ result: {
272
+ directory: root,
273
+ current: from.id,
274
+ version: from.version,
275
+ upgraded: false,
276
+ nodeMatches: !nodeChanged,
277
+ },
278
+ summary: [
279
+ `The application is already on ${from.version} (${from.id}).`,
280
+ ...(warning ? [` ${warning}`] : []),
281
+ ],
282
+ };
283
+ };
284
+ const refuseDowngrade = (version) => {
285
+ throw new InstallerError('DOWNGRADE', `${version} is older than the running ${from.version}. An older release does not know the newer migrations and would run on a schema it does not understand.`, {
286
+ exitCode: EXIT_INVALID,
287
+ suggestions: [
288
+ {
289
+ message: 'Go back with rollback, which restores the databases backed up before the upgrade:',
290
+ run: installerCommandLine(['rollback', '--dir', root, '--to', version], { registry: state.registry }),
291
+ },
292
+ ],
293
+ });
294
+ };
295
+ const url = healthUrl(env);
296
+ const service = {
297
+ layout,
298
+ pm2,
299
+ name: state.name,
300
+ healthUrl: url,
301
+ fetchImpl: deps.fetchImpl,
302
+ };
303
+ checkPlatform();
304
+ await checkPm2(pm2);
305
+ await checkPm2Ownership(service);
306
+ // What to switch to: an archive has to be unpacked first, because only its manifest says which release it is.
307
+ reporter.progress(`Unpacking ${archive}`);
308
+ const unpacked = await unpackRelease({ layout, archive });
309
+ const discard = async () => {
310
+ if (!unpacked.reused) {
311
+ await rm(path.dirname(unpacked.dir), {
312
+ recursive: true,
313
+ force: true,
314
+ });
315
+ }
316
+ };
317
+ try {
318
+ checkArchiveMatches(state, unpacked, env);
319
+ checkArchiveDriver(unpacked.dir, state.dialect);
320
+ if (unpacked.id === from.id) {
321
+ return noop();
322
+ }
323
+ if ((compareVersions(unpacked.version, from.version) ?? 0) < 0) {
324
+ refuseDowngrade(unpacked.version);
325
+ }
326
+ }
327
+ catch (error) {
328
+ await discard();
329
+ throw error;
330
+ }
331
+ const reuse = unpacked.reused ? findRelease(state, unpacked.id) : undefined;
332
+ const prepared = reuse ? undefined : unpacked;
333
+ const inventory = readDatabaseInventory(layout, env);
334
+ const unprotected = [...inventory.external, ...inventory.unresolved];
335
+ const discardPrepared = async () => {
336
+ if (prepared) {
337
+ await rm(path.dirname(prepared.dir), { recursive: true, force: true });
338
+ }
339
+ };
340
+ try {
341
+ if (unprotected.length > 0 && !flags['backup-done']) {
342
+ throw new InstallerError('BACKUP_REQUIRED', `app-installer cannot back up the database connection${unprotected.length === 1 ? '' : 's'} ${unprotected.join(', ')}. Back ${unprotected.length === 1 ? 'it' : 'them'} up, then pass --backup-done.`, { exitCode: EXIT_INVALID });
343
+ }
344
+ await confirm([
345
+ `Upgrade the application at ${root} from ${from.id} to ${unpacked.id}.`,
346
+ 'The application stops while the release switches.',
347
+ inventory.sqlite.length > 0
348
+ ? 'The SQLite databases and the configuration are copied to backups/ before anything is migrated.'
349
+ : 'The configuration is copied to backups/ before anything is migrated.',
350
+ ...(unprotected.length > 0
351
+ ? [
352
+ `You confirmed with --backup-done that ${unprotected.join(', ')} ${unprotected.length === 1 ? 'is' : 'are'} backed up.`,
353
+ ]
354
+ : []),
355
+ ...(nodeChanged
356
+ ? [
357
+ `This machine runs Node ${machineMajor}, but ${from.id} was built for Node ${from.buildTarget.nodeMajor}: it cannot be rolled back to.`,
358
+ ]
359
+ : []),
360
+ ], { yes: flags.yes, json: flags.json });
361
+ }
362
+ catch (error) {
363
+ await discardPrepared();
364
+ throw error;
365
+ }
366
+ // Everything up to the stop happens while the current release keeps serving.
367
+ let to;
368
+ let newRecord;
369
+ if (reuse) {
370
+ reporter.progress(`Reusing the ${reuse.id} release already on disk`);
371
+ to = reuse;
372
+ }
373
+ else {
374
+ to = recordOf(prepared);
375
+ newRecord = to;
376
+ }
377
+ // A release built before relocatable builds runs only at the path it was built for.
378
+ const fixedMountPath = fixedMountPathOf(to, state);
379
+ if (fixedMountPath !== undefined) {
380
+ try {
381
+ assertFixedMountPath(to.id, fixedMountPath, mountPathOf(env));
382
+ }
383
+ catch (error) {
384
+ if (newRecord) {
385
+ await rm(path.dirname(releaseDir(layout, to.id)), {
386
+ recursive: true,
387
+ force: true,
388
+ });
389
+ }
390
+ throw error;
391
+ }
392
+ }
393
+ const dir = releaseDir(layout, to.id);
394
+ const removeNewRelease = async () => {
395
+ if (newRecord) {
396
+ await rm(path.dirname(dir), { recursive: true, force: true });
397
+ }
398
+ };
399
+ const cli = { releaseDir: dir, cwd: root, env, run };
400
+ let pending;
401
+ try {
402
+ reporter.progress(`Checking the configuration and pending migrations with ${to.id}`);
403
+ await runAppCli(['config', 'check'], cli);
404
+ pending = pendingTaskCount(await runAppCli(['db', 'apply', '--dry-run'], cli));
405
+ }
406
+ catch (error) {
407
+ await removeNewRelease();
408
+ throw error;
409
+ }
410
+ reporter.progress(pending > 0
411
+ ? `${to.id} will apply ${pending} migration and seed task(s)`
412
+ : `${to.id} has no pending migrations`);
413
+ const name = backupName(from.id, to.id, new Date());
414
+ const backupRelative = path.join('backups', name);
415
+ state.pending = {
416
+ action: 'upgrade',
417
+ from: from.id,
418
+ to: to.id,
419
+ startedAt: new Date().toISOString(),
420
+ };
421
+ await writeState(layout, state);
422
+ // Downtime starts here.
423
+ reporter.progress(`Stopping ${from.id}`);
424
+ let backup;
425
+ try {
426
+ await stopApp(service);
427
+ // Health stopped answering; make sure nothing else holds the port the new release has to bind.
428
+ await checkPortFree(env.APP_SERVER_HOST ?? '127.0.0.1', Number(env.APP_SERVER_PORT ?? 13000));
429
+ reporter.progress('Backing up the databases and configuration');
430
+ backup = await backupForUpgrade(layout, name, inventory.sqlite);
431
+ const main = backup.databases.find((database) => database.connection === inventory.defaultConnection);
432
+ if (main && main.files.length === 0) {
433
+ throw new Error(`the SQLite database was not found at ${main.file}`);
434
+ }
435
+ // Recorded only now that it is complete: a recovery must never restore from a backup that was cut short.
436
+ state.pending.backup = backup.relative;
437
+ await writeState(layout, state);
438
+ }
439
+ catch (error) {
440
+ // Nothing was switched: bring the current release back and report the upgrade as not done.
441
+ await removeBackup(layout, backupRelative);
442
+ const healthy = await startApp({
443
+ ...service,
444
+ timeoutMs: flags['health-timeout'] * 1000,
445
+ }).catch(() => false);
446
+ delete state.pending;
447
+ await writeState(layout, state);
448
+ await removeNewRelease();
449
+ throw new InstallerError('UPGRADE_ABORTED', `Upgrading to ${to.id} stopped before switching: ${error instanceof Error ? error.message : String(error)}. ${healthy ? `${from.id} is running again.` : `${from.id} did not start again.`}`, {
450
+ exitCode: healthy ? EXIT_FAILED : EXIT_ROLLBACK_FAILED,
451
+ cause: error,
452
+ suggestions: healthy
453
+ ? []
454
+ : startAdvice(layout, `Start ${from.id} again once the cause is fixed:`),
455
+ });
456
+ }
457
+ try {
458
+ // Marked before the link moves, so an interruption from here on is undone with a database restore.
459
+ state.pending.switched = true;
460
+ await writeState(layout, state);
461
+ await switchCurrent(layout, releaseLinkTarget(to.id));
462
+ reporter.progress('Applying database migrations');
463
+ await runAppCli(['db', 'apply'], cli);
464
+ assertStorageOutsideRelease(dir);
465
+ reporter.progress(`Starting ${to.id}`);
466
+ const healthy = await startApp({
467
+ ...service,
468
+ timeoutMs: flags['health-timeout'] * 1000,
469
+ });
470
+ if (!healthy) {
471
+ throw new Error(`${url} did not become healthy (waited up to ${flags['health-timeout']}s)`);
472
+ }
473
+ }
474
+ catch (error) {
475
+ reporter.progress(`Rolling back to ${from.id}`);
476
+ // Awaited here rather than returned as a promise: the `finally` below awaits the lock before the caller can
477
+ // attach a handler, and a rollback that fails first would be reported as an unhandled rejection.
478
+ return await rollBackUpgrade({
479
+ layout,
480
+ state,
481
+ service,
482
+ from,
483
+ to,
484
+ pending,
485
+ backup,
486
+ newRecord,
487
+ timeoutMs: flags['health-timeout'] * 1000,
488
+ cause: error,
489
+ });
490
+ }
491
+ // The new release is serving: record that first, so nothing after this can make it look unfinished.
492
+ const at = new Date().toISOString();
493
+ state.current = to.id;
494
+ if (newRecord)
495
+ replaceRecord(state, newRecord);
496
+ // A release whose directory was removed by hand can never be returned to; forget it rather than prune around it.
497
+ state.releases = state.releases.filter((record) => existsSync(releaseDir(layout, record.id)));
498
+ delete state.pending;
499
+ state.history.push({
500
+ action: 'upgrade',
501
+ from: from.id,
502
+ to: to.id,
503
+ at,
504
+ outcome: 'completed',
505
+ migrations: pending,
506
+ backup: backup.relative,
507
+ });
508
+ await writeState(layout, state);
509
+ const pruned = releasesToPrune(state.releases, [to.id, from.id], flags.keep);
510
+ for (const record of pruned) {
511
+ await rm(path.dirname(releaseDir(layout, record.id)), {
512
+ recursive: true,
513
+ force: true,
514
+ });
515
+ }
516
+ if (pruned.length > 0) {
517
+ state.releases = state.releases.filter((record) => !pruned.some((gone) => gone.id === record.id));
518
+ await writeState(layout, state);
519
+ }
520
+ return {
521
+ status: 'success',
522
+ result: {
523
+ directory: root,
524
+ from: from.id,
525
+ to: to.id,
526
+ fromVersion: from.version,
527
+ toVersion: to.version,
528
+ upgraded: true,
529
+ reused: reuse !== undefined,
530
+ migrations: pending,
531
+ backup: backup.relative,
532
+ pruned: pruned.map((record) => record.id),
533
+ },
534
+ summary: [
535
+ `Upgraded the application from ${from.version} to ${to.version}.`,
536
+ ` Release ${to.id}`,
537
+ ` Migrations ${pending}`,
538
+ ` Backup ${backup.relative}`,
539
+ ...(pruned.length > 0
540
+ ? [` Pruned ${pruned.map((record) => record.id).join(', ')}`]
541
+ : []),
542
+ ],
543
+ };
544
+ }
545
+ finally {
546
+ await releaseLock();
547
+ }
548
+ }
549
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import path from 'node:path';
2
+ import { InstallerError } from "./errors.js";
3
+ import { formatCommandLine } from '@nocobase/cli-envelope';
4
+ import { CommandFailedError, runCommand, tail, } from "./run-command.js";
5
+ /**
6
+ * Carries a suggestion of the release's CLI over into the installer's error, with its command folded into the message.
7
+ * None of them runs as-is from an installation root: they name the release's own `dist/cli/index.js`, which a failed
8
+ * install or upgrade has already removed and which reads the wrong configuration without `app.env`, or `pnpm add`,
9
+ * which would turn the installation root into a project.
10
+ */
11
+ export function toSuggestion(entry) {
12
+ const run = typeof entry.run === 'string'
13
+ ? entry.run
14
+ : entry.run
15
+ ? formatCommandLine(entry.run)
16
+ : undefined;
17
+ const message = entry.message ?? '';
18
+ return {
19
+ message: run
20
+ ? [message, `(the application CLI's command: ${run})`]
21
+ .filter(Boolean)
22
+ .join(' ')
23
+ : message,
24
+ };
25
+ }
26
+ /**
27
+ * The command as it may appear in a message: `config set` values are masked, since a `--set` may carry a secret that
28
+ * would otherwise end up in the JSON result and the terminal. `--from-env` pairs name a variable, not a value, and stay.
29
+ */
30
+ export function describeAppCommand(args) {
31
+ const setting = args[0] === 'config' && args[1] === 'set';
32
+ let fromEnv = false;
33
+ const shown = args.map((arg) => {
34
+ if (arg === '--from-env')
35
+ fromEnv = true;
36
+ if (!setting || fromEnv || arg.startsWith('-'))
37
+ return arg;
38
+ const index = arg.indexOf('=');
39
+ return index > 0 ? `${arg.slice(0, index + 1)}***` : arg;
40
+ });
41
+ return `nocobase ${shown.join(' ')}`;
42
+ }
43
+ /**
44
+ * Runs the release's own CLI, `node <release>/dist/cli/index.js <args> --json`, and returns its envelope. It is the code
45
+ * that runs in production, so configuration and migrations are checked by the same version that will serve them.
46
+ */
47
+ export async function runAppCli(args, options) {
48
+ const entry = path.join(options.releaseDir, 'dist/cli/index.js');
49
+ const label = describeAppCommand(args);
50
+ const run = options.run ?? runCommand;
51
+ let stdout;
52
+ let stderr;
53
+ try {
54
+ ({ stdout, stderr } = await run(process.execPath, [entry, ...args, '--json'], {
55
+ cwd: options.cwd,
56
+ env: { ...process.env, ...options.env },
57
+ timeoutMs: 30 * 60_000,
58
+ }));
59
+ }
60
+ catch (error) {
61
+ if (!(error instanceof CommandFailedError))
62
+ throw error;
63
+ stdout = error.stdout;
64
+ stderr = error.stderr;
65
+ }
66
+ let envelope;
67
+ try {
68
+ envelope = JSON.parse(stdout);
69
+ }
70
+ catch {
71
+ throw new InstallerError('APP_CLI_FAILED', `${label} did not print a JSON result.`, { details: { stderr: tail(stderr), stdout: tail(stdout) } });
72
+ }
73
+ if (!envelope.ok) {
74
+ throw new InstallerError(envelope.error?.code ?? 'APP_CLI_FAILED', `${label} failed: ${envelope.error?.message ?? 'no message'}`, {
75
+ suggestions: (envelope.error?.suggestions ?? []).map(toSuggestion),
76
+ details: { command: label },
77
+ });
78
+ }
79
+ return envelope;
80
+ }
81
+ /**
82
+ * Counts the migration and seed tasks a `db apply --dry-run` would run. `status` is not a reliable signal: a dry run
83
+ * reports `success-noop` even when tasks are pending, so the plan itself is read.
84
+ */
85
+ export function pendingTaskCount(envelope) {
86
+ const plan = envelope.result?.plan;
87
+ return (plan ?? []).reduce((count, entry) => count + (entry.tasks?.length ?? 0), 0);
88
+ }
89
+ //# sourceMappingURL=app-cli.js.map