@celilo/cli 0.3.22 → 0.3.24
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 +1 -1
- package/src/cli/commands/system-update.ts +100 -12
package/package.json
CHANGED
|
@@ -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
|
-
|
|
222
|
-
|
|
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
|
}
|
|
@@ -233,15 +248,45 @@ function formatResult(result: SystemUpdateResult): string {
|
|
|
233
248
|
` self-update: ${result.selfUpdate.performed ? `${result.selfUpdate.from} → ${result.selfUpdate.to}` : `(${result.selfUpdate.reason})`}`,
|
|
234
249
|
);
|
|
235
250
|
lines.push(` backups: ${result.backupsCreated ? 'created' : 'skipped'}`);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
|
|
252
|
+
// Surface audit findings inline. DRIFT means "something is drifting
|
|
253
|
+
// but it didn't block the update" — without listing the findings the
|
|
254
|
+
// operator has no idea what; they'd have to run `celilo system audit`
|
|
255
|
+
// as a follow-up. Show them here so it's a single round-trip.
|
|
256
|
+
// BLOCKED also gets the listing (the orchestrator already short-
|
|
257
|
+
// circuited to skip the run, but the operator still needs to know
|
|
258
|
+
// why — formatResult is the friendly path for that too).
|
|
259
|
+
if (result.audit.findings.length > 0) {
|
|
260
|
+
lines.push('');
|
|
261
|
+
const drift = result.audit.findings.filter((f) => f.severity === 'drift');
|
|
262
|
+
const blocked = result.audit.findings.filter((f) => f.severity === 'blocked');
|
|
263
|
+
if (blocked.length > 0) {
|
|
264
|
+
lines.push(` BLOCKED (${blocked.length}):`);
|
|
265
|
+
for (const f of blocked) {
|
|
266
|
+
lines.push(` ✗ [${f.category}] ${f.subject}: ${f.message}`);
|
|
267
|
+
if (f.remediation) lines.push(` → ${f.remediation}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (drift.length > 0) {
|
|
271
|
+
lines.push(` Drift findings (${drift.length}, informational):`);
|
|
272
|
+
for (const f of drift) {
|
|
273
|
+
lines.push(` ▸ [${f.category}] ${f.subject}: ${f.message}`);
|
|
274
|
+
if (f.remediation) lines.push(` → ${f.remediation}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (result.modules.length > 0) {
|
|
280
|
+
lines.push('');
|
|
281
|
+
for (const m of result.modules) {
|
|
282
|
+
const tag =
|
|
283
|
+
m.step === 'done' ? '✓' : m.step === 'failed' ? '✗' : m.step === 'skipped' ? '↳' : '?';
|
|
284
|
+
const change =
|
|
285
|
+
m.fromVersion === m.toVersion ? m.fromVersion : `${m.fromVersion} → ${m.toVersion}`;
|
|
286
|
+
lines.push(` ${tag} ${m.moduleId} (${change}) — ${m.step}`);
|
|
287
|
+
if (m.error) lines.push(` ${m.error}`);
|
|
288
|
+
if (m.skipReason) lines.push(` ${m.skipReason}`);
|
|
289
|
+
}
|
|
245
290
|
}
|
|
246
291
|
return lines.join('\n');
|
|
247
292
|
}
|
|
@@ -394,6 +439,49 @@ export async function handleSystemUpdate(
|
|
|
394
439
|
};
|
|
395
440
|
}
|
|
396
441
|
|
|
442
|
+
// Decide whether the celilo-db snapshot is even needed for this run.
|
|
443
|
+
// If nothing's changing at the module level, there's nothing to roll
|
|
444
|
+
// back to — taking a snapshot would be pointless work, and on a fresh
|
|
445
|
+
// box (no storage configured yet) it would actively fail. Treat the
|
|
446
|
+
// "nothing-to-update" case as implicit --no-backup.
|
|
447
|
+
const hasModuleUpdates = [...snapshots.values()].some(
|
|
448
|
+
(s) => s.latestVersion && s.latestVersion !== s.installedVersion,
|
|
449
|
+
);
|
|
450
|
+
const effectiveNoBackup = noBackup || !hasModuleUpdates;
|
|
451
|
+
|
|
452
|
+
// Pre-flight the storage check so a missing default doesn't reach the
|
|
453
|
+
// orchestrator's snapshot hook (where the throw would surface as a
|
|
454
|
+
// hostile stack trace). Skip when we're already not going to backup.
|
|
455
|
+
if (!effectiveNoBackup) {
|
|
456
|
+
const { getDefaultBackupStorage } = await import('../../services/backup-storage');
|
|
457
|
+
const storage = getDefaultBackupStorage();
|
|
458
|
+
if (!storage) {
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
error: `No default backup storage configured.
|
|
462
|
+
|
|
463
|
+
celilo system update snapshots the celilo DB before applying module
|
|
464
|
+
updates as a safety net. Configure backup storage first:
|
|
465
|
+
|
|
466
|
+
celilo storage add local
|
|
467
|
+
|
|
468
|
+
Or skip the safety net entirely (the CLI self-update still runs):
|
|
469
|
+
|
|
470
|
+
celilo system update --no-backup`,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (!storage.verified) {
|
|
474
|
+
return {
|
|
475
|
+
success: false,
|
|
476
|
+
error: `Default backup storage '${storage.storageId}' is not verified.
|
|
477
|
+
|
|
478
|
+
Run: celilo storage verify ${storage.storageId}
|
|
479
|
+
|
|
480
|
+
Then re-run system update.`,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
397
485
|
const ops = buildOps(registry);
|
|
398
486
|
|
|
399
487
|
const result = await runSystemUpdate({
|
|
@@ -423,7 +511,7 @@ export async function handleSystemUpdate(
|
|
|
423
511
|
},
|
|
424
512
|
},
|
|
425
513
|
progress: { emit() {} },
|
|
426
|
-
noBackup,
|
|
514
|
+
noBackup: effectiveNoBackup,
|
|
427
515
|
allowDestructive,
|
|
428
516
|
onlyModule,
|
|
429
517
|
});
|