doctor: clean up legacy Linux gateway services (#21188)

* Doctor: clean up legacy Linux gateway services

* doctor: refactor legacy service cleanup flow

* doctor: fix legacy systemd cleanup map key typing

* doctor: add changelog entry for legacy Linux service cleanup

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Phineas1500
2026-02-22 19:18:59 -05:00
committed by GitHub
parent 1c2c7843a8
commit 8a8faf066e
5 changed files with 190 additions and 25 deletions

View File

@@ -9,6 +9,9 @@ const mocks = vi.hoisted(() => ({
buildGatewayInstallPlan: vi.fn(),
resolveGatewayPort: vi.fn(() => 18789),
resolveIsNixMode: vi.fn(() => false),
findExtraGatewayServices: vi.fn().mockResolvedValue([]),
renderGatewayServiceCleanupHints: vi.fn().mockReturnValue([]),
uninstallLegacySystemdUnits: vi.fn().mockResolvedValue([]),
note: vi.fn(),
}));
@@ -18,8 +21,8 @@ vi.mock("../config/paths.js", () => ({
}));
vi.mock("../daemon/inspect.js", () => ({
findExtraGatewayServices: vi.fn().mockResolvedValue([]),
renderGatewayServiceCleanupHints: vi.fn().mockReturnValue([]),
findExtraGatewayServices: mocks.findExtraGatewayServices,
renderGatewayServiceCleanupHints: mocks.renderGatewayServiceCleanupHints,
}));
vi.mock("../daemon/runtime-paths.js", () => ({
@@ -42,6 +45,10 @@ vi.mock("../daemon/service.js", () => ({
}),
}));
vi.mock("../daemon/systemd.js", () => ({
uninstallLegacySystemdUnits: mocks.uninstallLegacySystemdUnits,
}));
vi.mock("../terminal/note.js", () => ({
note: mocks.note,
}));
@@ -50,7 +57,10 @@ vi.mock("./daemon-install-helpers.js", () => ({
buildGatewayInstallPlan: mocks.buildGatewayInstallPlan,
}));
import { maybeRepairGatewayServiceConfig } from "./doctor-gateway-services.js";
import {
maybeRepairGatewayServiceConfig,
maybeScanExtraGatewayServices,
} from "./doctor-gateway-services.js";
function makeDoctorIo() {
return { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
@@ -163,3 +173,58 @@ describe("maybeRepairGatewayServiceConfig", () => {
});
});
});
describe("maybeScanExtraGatewayServices", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.findExtraGatewayServices.mockResolvedValue([]);
mocks.renderGatewayServiceCleanupHints.mockReturnValue([]);
mocks.uninstallLegacySystemdUnits.mockResolvedValue([]);
});
it("removes legacy Linux user systemd services", async () => {
mocks.findExtraGatewayServices.mockResolvedValue([
{
platform: "linux",
label: "moltbot-gateway.service",
detail: "unit: /home/test/.config/systemd/user/moltbot-gateway.service",
scope: "user",
legacy: true,
},
]);
mocks.uninstallLegacySystemdUnits.mockResolvedValue([
{
name: "moltbot-gateway",
unitPath: "/home/test/.config/systemd/user/moltbot-gateway.service",
enabled: true,
exists: true,
},
]);
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
const prompter = {
confirm: vi.fn(),
confirmRepair: vi.fn(),
confirmAggressive: vi.fn(),
confirmSkipInNonInteractive: vi.fn().mockResolvedValue(true),
select: vi.fn(),
shouldRepair: false,
shouldForce: false,
};
await maybeScanExtraGatewayServices({ deep: false }, runtime, prompter);
expect(mocks.uninstallLegacySystemdUnits).toHaveBeenCalledTimes(1);
expect(mocks.uninstallLegacySystemdUnits).toHaveBeenCalledWith({
env: process.env,
stdout: process.stdout,
});
expect(mocks.note).toHaveBeenCalledWith(
expect.stringContaining("moltbot-gateway.service"),
"Legacy gateway removed",
);
expect(runtime.log).toHaveBeenCalledWith(
"Legacy gateway services removed. Installing OpenClaw gateway next.",
);
});
});