fix(cli): verify update pause before install

This commit is contained in:
i-trytoohard 2026-05-21 04:03:06 +05:30
parent ea3d44bfef
commit 00a867530e
2 changed files with 74 additions and 14 deletions

View File

@ -570,13 +570,15 @@ describe("update command", () => {
);
Object.defineProperty(process.stdin, "isTTY", { value: true, configurable: true });
Object.defineProperty(process.stdout, "isTTY", { value: true, configurable: true });
mockGetRunning.mockResolvedValue({
pid: 12345,
configPath: "/tmp/test-global-config.yaml",
port: 3000,
startedAt: new Date().toISOString(),
projects: ["my-app"],
});
mockGetRunning
.mockResolvedValueOnce({
pid: 12345,
configPath: "/tmp/test-global-config.yaml",
port: 3000,
startedAt: new Date().toISOString(),
projects: ["my-app"],
})
.mockResolvedValue(null);
mockLoadConfig.mockImplementation((path?: string) => ({
projects: { "my-app": { path: "/tmp/foo" } },
configPath: path ?? "/cwd/agent-orchestrator.yaml",
@ -618,6 +620,7 @@ describe("update command", () => {
});
it("does not stop/start when no daemon or active sessions exist", async () => {
mockGetRunning.mockReset();
mockGetRunning.mockResolvedValue(null);
mockExistsSync.mockReturnValue(false);
mockSessions.value = [];
@ -653,7 +656,30 @@ describe("update command", () => {
]);
});
it("aborts before install if ao stop exits 0 but AO still appears active", async () => {
mockGetRunning.mockResolvedValue({
pid: 12345,
configPath: "/tmp/test-global-config.yaml",
port: 3000,
startedAt: new Date().toISOString(),
projects: ["my-app"],
});
mockSpawn.mockReturnValue(createMockChild(0, undefined, { stdout: "" }));
await expect(program.parseAsync(["node", "test", "update"])).rejects.toThrow(
"process.exit(1)",
);
expect(mockSpawn).toHaveBeenCalledTimes(1);
expect(mockSpawn.mock.calls[0][0]).toBe("ao");
expect(mockSpawn.mock.calls[0][1]).toEqual(["stop", "--yes"]);
expect(mockSpawn.mock.calls.some((call) => call[0] === "pnpm")).toBe(false);
const stderr = vi.mocked(console.error).mock.calls.map((c) => String(c[0])).join("\n");
expect(stderr).toContain("AO still appears to be running after `ao stop --yes`");
});
it("prints a friendly pnpm diagnostic and npm fallback when pnpm fails", async () => {
mockGetRunning.mockReset();
mockGetRunning.mockResolvedValue(null);
mockExistsSync.mockReturnValue(false);
mockSpawn.mockReturnValue(
@ -955,13 +981,16 @@ describe("update command", () => {
it("orchestrates stop/install/verify/start when active sessions exist and update is API-invoked", async () => {
mockSessions.value = [{ id: "feat-1", status: "working" }];
mockGetRunning.mockResolvedValue({
pid: 12345,
configPath: "/tmp/test-global-config.yaml",
port: 3000,
startedAt: new Date().toISOString(),
projects: ["my-app"],
});
mockExistsSync.mockReturnValue(false);
mockGetRunning
.mockResolvedValueOnce({
pid: 12345,
configPath: "/tmp/test-global-config.yaml",
port: 3000,
startedAt: new Date().toISOString(),
projects: ["my-app"],
})
.mockResolvedValue(null);
await program.parseAsync(["node", "test", "update"]);

View File

@ -245,6 +245,37 @@ async function pauseAoForUpdate(plan: UpdateLifecyclePlan): Promise<boolean> {
process.exit(stopExit);
}
const afterStop = await getUpdateLifecyclePlan();
if (afterStop.runningBeforeUpdate || afterStop.activeSessions.length > 0) {
recordActivityEvent({
source: "cli",
kind: "cli.update_failed",
level: "error",
summary: `ao update failed: AO still appears active after internal ao stop`,
data: {
runningAfterStop: afterStop.runningBeforeUpdate,
activeSessionCount: afterStop.activeSessions.length,
activeSessionIds: afterStop.activeSessions.map((s) => s.id).slice(0, 20),
},
});
console.error(
chalk.red(
"\nAO update stopped before installing because AO still appears to be running after `ao stop --yes`.",
),
);
if (afterStop.activeSessions.length > 0) {
console.error(chalk.dim("Still-active sessions:"));
for (const s of afterStop.activeSessions.slice(0, 5)) {
console.error(chalk.dim(`${s.id} (${s.status})`));
}
if (afterStop.activeSessions.length > 5) {
console.error(chalk.dim(` … and ${afterStop.activeSessions.length - 5} more`));
}
}
console.error(chalk.dim("Run `ao stop` and retry `ao update` after AO is fully stopped."));
process.exit(1);
}
return true;
}