omp-conductor 0.8.0 → 0.9.0
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 +45 -21
- package/package.json +1 -1
- package/src/backups.ts +46 -0
- package/src/board.ts +254 -38
- package/src/brief-upgrade.ts +1 -30
- package/src/cli.ts +17 -5
- package/src/config.ts +13 -0
- package/src/daemon.ts +238 -123
- package/src/diff-flags.ts +35 -6
- package/src/fleet.ts +28 -2
- package/src/label-projection.ts +93 -0
- package/src/routing.ts +20 -0
- package/src/store.ts +254 -2
- package/src/tracker/github.ts +266 -18
- package/src/types.ts +75 -3
- package/src/unblock.ts +70 -19
- package/src/upgrade.ts +28 -4
package/src/upgrade.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { backupTimestamp, copyToUniqueBackup } from "./backups.ts";
|
|
2
3
|
import { inspectBriefLayout, type BriefLayout } from "./brief-upgrade.ts";
|
|
3
4
|
import { setPaused, statusSnapshot } from "./daemon.ts";
|
|
4
5
|
import { fleetLayers, telegramStateDir, type DispatchLayer, type FleetLayers } from "./fleet.ts";
|
|
5
6
|
import { livingDaemon, restartDaemon } from "./lifecycle.ts";
|
|
6
|
-
import { configPath, findProject, loadConfig, resolveCaps, writeConfigRaw } from "./config.ts";
|
|
7
|
+
import { configBackupDir, configPath, findProject, loadConfig, resolveCaps, writeConfigRaw } from "./config.ts";
|
|
7
8
|
import { renderBriefForProject } from "./setup.ts";
|
|
8
9
|
import {
|
|
9
10
|
STAGED_SERVICE_NAME,
|
|
@@ -327,6 +328,7 @@ async function rollbackUpgrade(
|
|
|
327
328
|
herdrReloadStarted: boolean,
|
|
328
329
|
daemonReloadStarted: boolean,
|
|
329
330
|
configBefore?: string,
|
|
331
|
+
preUpgradeBackup?: string,
|
|
330
332
|
): Promise<void> {
|
|
331
333
|
const failures: string[] = [];
|
|
332
334
|
|
|
@@ -344,12 +346,23 @@ async function rollbackUpgrade(
|
|
|
344
346
|
const path = configPath();
|
|
345
347
|
if (readFileSync(path, "utf8") !== configBefore) {
|
|
346
348
|
deps.log("rollback: conductor config.json");
|
|
349
|
+
// Name the durable snapshot: the in-memory restore only runs while
|
|
350
|
+
// this process is alive, and an upgrade that died mid-flight leaves
|
|
351
|
+
// the operator needing exactly these bytes from disk.
|
|
352
|
+
deps.log(
|
|
353
|
+
preUpgradeBackup === undefined
|
|
354
|
+
? "rollback: config.json changed during the upgrade; no durable snapshot was persisted"
|
|
355
|
+
: `rollback: config.json changed during the upgrade; pre-upgrade snapshot at ${preUpgradeBackup}`,
|
|
356
|
+
);
|
|
347
357
|
// Exact bytes. `writeConfigFile(JSON.parse(...))` returns canonically
|
|
348
358
|
// formatted output, which restores the keys but not the file.
|
|
349
359
|
writeConfigRaw(configBefore);
|
|
350
360
|
}
|
|
351
361
|
} catch (err) {
|
|
352
|
-
failures.push(
|
|
362
|
+
failures.push(
|
|
363
|
+
`could not restore config.json: ${err instanceof Error ? err.message : String(err)}` +
|
|
364
|
+
(preUpgradeBackup === undefined ? "" : `; pre-upgrade snapshot at ${preUpgradeBackup}`),
|
|
365
|
+
);
|
|
353
366
|
}
|
|
354
367
|
}
|
|
355
368
|
const restore = async (label: string, command: string, args: readonly string[]): Promise<void> => {
|
|
@@ -496,13 +509,23 @@ export async function upgradeConductor(
|
|
|
496
509
|
// Snapshotted before anything is installed, because the new daemon starts
|
|
497
510
|
// during this transaction and may migrate the file — see the restore in
|
|
498
511
|
// `rollbackUpgrade`. Read as bytes, not through the loader: the point is to
|
|
499
|
-
// put back exactly what was there, dialect and all.
|
|
512
|
+
// put back exactly what was there, dialect and all. The same bytes are also
|
|
513
|
+
// persisted under state, so a process death between here and the rollback
|
|
514
|
+
// cannot take the only pre-upgrade copy with it.
|
|
500
515
|
let configBefore: string | undefined;
|
|
516
|
+
let preUpgradeBackup: string | undefined;
|
|
501
517
|
try {
|
|
502
518
|
configBefore = readFileSync(configPath(), "utf8");
|
|
519
|
+
preUpgradeBackup = copyToUniqueBackup(
|
|
520
|
+
configPath(),
|
|
521
|
+
configBackupDir(),
|
|
522
|
+
`config.json.pre-upgrade-${backupTimestamp()}`,
|
|
523
|
+
);
|
|
524
|
+
deps.log(`config backed up to ${preUpgradeBackup}`);
|
|
503
525
|
} catch {
|
|
504
526
|
// No readable config is not a reason to refuse an upgrade; there is simply
|
|
505
|
-
// nothing to put back.
|
|
527
|
+
// nothing to put back. A snapshot that failed to persist is not fatal
|
|
528
|
+
// either — the in-memory restore below still covers a clean rollback.
|
|
506
529
|
}
|
|
507
530
|
|
|
508
531
|
if (brief.kind === "missing") throw new Error("no ORCHESTRATOR.md exists for the configured project");
|
|
@@ -608,6 +631,7 @@ export async function upgradeConductor(
|
|
|
608
631
|
herdrReloadStarted,
|
|
609
632
|
daemonReloadStarted,
|
|
610
633
|
configBefore,
|
|
634
|
+
preUpgradeBackup,
|
|
611
635
|
);
|
|
612
636
|
} catch (rollbackErr) {
|
|
613
637
|
const rollback = rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr);
|