@danypops/pi-packed 0.27.4 → 0.27.5
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
|
@@ -455,7 +455,13 @@ export function listManagedPackages(piHome: string, projectRoot?: string): Insta
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
export interface ReconcileAllResult {
|
|
458
|
-
|
|
458
|
+
/** `versionChanged` is true only when this pass's newly-resolved version genuinely differs
|
|
459
|
+
* from what Armada had registered before this sweep started -- `installed: true` alone means
|
|
460
|
+
* only "the register call itself succeeded", which is true on every healthy pass whether or
|
|
461
|
+
* not anything actually changed underneath (register()/reconcile() is a safe no-op against
|
|
462
|
+
* unchanged desired state). A caller wanting to know whether this pass genuinely restarted
|
|
463
|
+
* something (vs. just re-confirmed a healthy, unchanged fleet) must check this, not `installed`. */
|
|
464
|
+
reconciled: Array<{ packageName: string; vehicleName: string; installed: boolean; versionChanged: boolean; reason?: string }>;
|
|
459
465
|
skipped: number;
|
|
460
466
|
failed: Array<{ packageName: string; reason: string }>;
|
|
461
467
|
/** Every Vehicle unregistered because this sweep no longer discovers it at all -- see pruneStaleVehicles. */
|
|
@@ -555,6 +561,13 @@ export async function reconcileAllDaemonServices(
|
|
|
555
561
|
const failed: ReconcileAllResult["failed"] = [];
|
|
556
562
|
const seen = new Set<string>();
|
|
557
563
|
let skipped = 0;
|
|
564
|
+
// Snapshot BEFORE this sweep's own install() calls mutate Armada's registration -- the only
|
|
565
|
+
// way to tell "this pass's register() call genuinely changed something" from "register()
|
|
566
|
+
// succeeded against an already-current registration", since install()'s own ServiceInstallResult
|
|
567
|
+
// reports only the latter.
|
|
568
|
+
const previousVersions = new Map<string, string>(
|
|
569
|
+
(await installer.listRegisteredVehicles()).map((vehicle) => [String(vehicle.name), vehicle.version]),
|
|
570
|
+
);
|
|
558
571
|
for (const pkg of packages) {
|
|
559
572
|
// Re-registering the daemon from inside its own process makes Armada stop
|
|
560
573
|
// that process before registration can start the replacement.
|
|
@@ -577,6 +590,7 @@ export async function reconcileAllDaemonServices(
|
|
|
577
590
|
packageName: pkg.name,
|
|
578
591
|
vehicleName: resolved.spec.name,
|
|
579
592
|
installed: resolved.result.installed,
|
|
593
|
+
versionChanged: previousVersions.get(resolved.spec.name) !== resolved.spec.version,
|
|
580
594
|
...(resolved.result.installed ? {} : { reason: resolved.result.reason }),
|
|
581
595
|
});
|
|
582
596
|
}
|
|
@@ -134,9 +134,10 @@ export function daemonOptions(options: StartPackedDaemonOptions): StartDaemonOpt
|
|
|
134
134
|
// success indistinguishable from "this never ran" -- there was no way to confirm a
|
|
135
135
|
// restart this task performed actually happened, short of checking the process's own
|
|
136
136
|
// PID by hand.
|
|
137
|
-
|
|
137
|
+
const changed = result.reconciled.filter((entry) => entry.installed && entry.versionChanged);
|
|
138
|
+
if (changed.length > 0 || result.pruned.length > 0) {
|
|
138
139
|
logger.info("vehicle-reconcile applied changes", {
|
|
139
|
-
reconciled:
|
|
140
|
+
reconciled: changed.map((entry) => entry.vehicleName),
|
|
140
141
|
pruned: result.pruned.map((entry) => entry.vehicleName),
|
|
141
142
|
});
|
|
142
143
|
}
|
package/service/test/cli.test.ts
CHANGED
|
@@ -120,7 +120,7 @@ class FakeDaemonServiceInstaller {
|
|
|
120
120
|
return {
|
|
121
121
|
ok: true,
|
|
122
122
|
output: "reconciled 1 Vehicle(s), skipped 0 non-daemon package(s)",
|
|
123
|
-
reconciled: [{ packageName: "@danypops/probe", vehicleName: "probe", installed: true }],
|
|
123
|
+
reconciled: [{ packageName: "@danypops/probe", vehicleName: "probe", installed: true, versionChanged: true }],
|
|
124
124
|
skipped: 0,
|
|
125
125
|
failed: [],
|
|
126
126
|
pruned: [],
|
|
@@ -604,7 +604,7 @@ describe("CLI", () => {
|
|
|
604
604
|
expect(JSON.parse(json.out)).toEqual({
|
|
605
605
|
ok: true,
|
|
606
606
|
output: "reconciled 1 Vehicle(s), skipped 0 non-daemon package(s)",
|
|
607
|
-
reconciled: [{ packageName: "@danypops/probe", vehicleName: "probe", installed: true }],
|
|
607
|
+
reconciled: [{ packageName: "@danypops/probe", vehicleName: "probe", installed: true, versionChanged: true }],
|
|
608
608
|
skipped: 0,
|
|
609
609
|
failed: [],
|
|
610
610
|
pruned: [],
|
|
@@ -343,6 +343,46 @@ describe("startPackedDaemon's own maintenance-task wiring (self-heals Vehicle dr
|
|
|
343
343
|
});
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Real gap this closes: install()'s own ServiceInstallResult reports `installed: true`
|
|
348
|
+
* whenever register()/reconcile() SUCCEEDS -- which is true on every healthy pass whether or
|
|
349
|
+
* not anything actually changed underneath (register() is a safe no-op against unchanged
|
|
350
|
+
* desired state). A caller logging "applied changes" off `installed` alone would say so on
|
|
351
|
+
* literally every tick of a healthy, unchanged fleet -- see pkg-update-never-restarts-
|
|
352
|
+
* vehicle-daemon. versionChanged is the honest signal instead.
|
|
353
|
+
*/
|
|
354
|
+
describe("reconcileAllDaemonServices reports versionChanged honestly, not just installed", () => {
|
|
355
|
+
it("is true on a package's first-ever registration, false on an unchanged re-sweep, true again once its on-disk version actually moves", async () => {
|
|
356
|
+
const piHome = fakePiHome(["npm:@danypops/probe"]);
|
|
357
|
+
installMockDaemon(piHome, "@danypops/probe");
|
|
358
|
+
const harness = await createArmadaTestHarness();
|
|
359
|
+
try {
|
|
360
|
+
const installer = new RealDaemonServiceInstaller(harness.registrar);
|
|
361
|
+
|
|
362
|
+
const first = await reconcileAllDaemonServices(piHome, undefined, installer);
|
|
363
|
+
expect(first.reconciled).toEqual([
|
|
364
|
+
{ packageName: "@danypops/probe", vehicleName: "probe", installed: true, versionChanged: true },
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
const second = await reconcileAllDaemonServices(piHome, undefined, installer);
|
|
368
|
+
expect(second.reconciled).toEqual([
|
|
369
|
+
{ packageName: "@danypops/probe", vehicleName: "probe", installed: true, versionChanged: false },
|
|
370
|
+
]);
|
|
371
|
+
|
|
372
|
+
writeFileSync(
|
|
373
|
+
join(piHome, "npm", "node_modules", "@danypops", "probe", "package.json"),
|
|
374
|
+
JSON.stringify({ name: "@danypops/probe", version: "1.1.0", packed: { daemonService: { binPath: "cli.ts", args: ["serve"] } } }),
|
|
375
|
+
);
|
|
376
|
+
const third = await reconcileAllDaemonServices(piHome, undefined, installer);
|
|
377
|
+
expect(third.reconciled).toEqual([
|
|
378
|
+
{ packageName: "@danypops/probe", vehicleName: "probe", installed: true, versionChanged: true },
|
|
379
|
+
]);
|
|
380
|
+
} finally {
|
|
381
|
+
await harness.dispose();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
346
386
|
describe("reconcileAllDaemonServices pruning (the-armada-registrar-jittor-web-spider-daemon-collision)", () => {
|
|
347
387
|
function stalePiHomeVehicle(piHome: string, name: string): VehicleSpec {
|
|
348
388
|
return {
|