@celilo/cli 0.3.22 → 0.3.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/cli",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "Celilo — home lab orchestration CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -218,8 +218,23 @@ function buildOps(registry: RegistryClient): OrchestratorOps {
218
218
  return { status: r.status, detail: r.error };
219
219
  },
220
220
  snapshotCeliloDb: async (_updateId) => {
221
- const result = await createSystemStateBackup();
222
- return result.success ? { ok: true } : { ok: false, error: result.error };
221
+ // resolveStorage throws when no default backup storage is configured
222
+ // (or it isn't verified). Catch here so the orchestrator gets a
223
+ // clean { ok: false, error } shape — the alternative is the throw
224
+ // escapes runSystemUpdate and surfaces as an unhandled stack trace.
225
+ // The handleSystemUpdate pre-flight already catches the common
226
+ // "no storage configured" case with a friendly message; this is
227
+ // a belt-and-suspenders for any other throw path resolveStorage
228
+ // takes (e.g. storage exists but isn't verified).
229
+ try {
230
+ const result = await createSystemStateBackup();
231
+ return result.success ? { ok: true } : { ok: false, error: result.error };
232
+ } catch (err) {
233
+ return {
234
+ ok: false,
235
+ error: err instanceof Error ? err.message : String(err),
236
+ };
237
+ }
223
238
  },
224
239
  };
225
240
  }
@@ -394,6 +409,49 @@ export async function handleSystemUpdate(
394
409
  };
395
410
  }
396
411
 
412
+ // Decide whether the celilo-db snapshot is even needed for this run.
413
+ // If nothing's changing at the module level, there's nothing to roll
414
+ // back to — taking a snapshot would be pointless work, and on a fresh
415
+ // box (no storage configured yet) it would actively fail. Treat the
416
+ // "nothing-to-update" case as implicit --no-backup.
417
+ const hasModuleUpdates = [...snapshots.values()].some(
418
+ (s) => s.latestVersion && s.latestVersion !== s.installedVersion,
419
+ );
420
+ const effectiveNoBackup = noBackup || !hasModuleUpdates;
421
+
422
+ // Pre-flight the storage check so a missing default doesn't reach the
423
+ // orchestrator's snapshot hook (where the throw would surface as a
424
+ // hostile stack trace). Skip when we're already not going to backup.
425
+ if (!effectiveNoBackup) {
426
+ const { getDefaultBackupStorage } = await import('../../services/backup-storage');
427
+ const storage = getDefaultBackupStorage();
428
+ if (!storage) {
429
+ return {
430
+ success: false,
431
+ error: `No default backup storage configured.
432
+
433
+ celilo system update snapshots the celilo DB before applying module
434
+ updates as a safety net. Configure backup storage first:
435
+
436
+ celilo storage add local
437
+
438
+ Or skip the safety net entirely (the CLI self-update still runs):
439
+
440
+ celilo system update --no-backup`,
441
+ };
442
+ }
443
+ if (!storage.verified) {
444
+ return {
445
+ success: false,
446
+ error: `Default backup storage '${storage.storageId}' is not verified.
447
+
448
+ Run: celilo storage verify ${storage.storageId}
449
+
450
+ Then re-run system update.`,
451
+ };
452
+ }
453
+ }
454
+
397
455
  const ops = buildOps(registry);
398
456
 
399
457
  const result = await runSystemUpdate({
@@ -423,7 +481,7 @@ export async function handleSystemUpdate(
423
481
  },
424
482
  },
425
483
  progress: { emit() {} },
426
- noBackup,
484
+ noBackup: effectiveNoBackup,
427
485
  allowDestructive,
428
486
  onlyModule,
429
487
  });