CLI: avoid false update restart failures without listener attribution (#39508)

This commit is contained in:
Vincent Koc
2026-03-07 21:42:25 -08:00
committed by GitHub
parent e0f80cf0e9
commit c3810346f9
2 changed files with 37 additions and 3 deletions

View File

@@ -198,4 +198,26 @@ describe("inspectGatewayRestart", () => {
expect(snapshot.healthy).toBe(true);
});
it("treats busy ports with unavailable listener details as healthy when runtime is running", async () => {
const service = {
readRuntime: vi.fn(async () => ({ status: "running", pid: 8000 })),
} as unknown as GatewayService;
inspectPortUsage.mockResolvedValue({
port: 18789,
status: "busy",
listeners: [],
hints: [
"Port is in use but process details are unavailable (install lsof or run as an admin user).",
],
errors: ["Error: spawn lsof ENOENT"],
});
const { inspectGatewayRestart } = await import("./restart-health.js");
const snapshot = await inspectGatewayRestart({ service, port: 18789 });
expect(snapshot.healthy).toBe(true);
expect(probeGateway).not.toHaveBeenCalled();
});
});

View File

@@ -28,6 +28,16 @@ export type GatewayPortHealthSnapshot = {
healthy: boolean;
};
function hasListenerAttributionGap(portUsage: PortUsage): boolean {
if (portUsage.status !== "busy" || portUsage.listeners.length > 0) {
return false;
}
if (portUsage.errors?.length) {
return true;
}
return portUsage.hints.some((hint) => hint.includes("process details are unavailable"));
}
function listenerOwnedByRuntimePid(params: {
listener: PortUsage["listeners"][number];
runtimePid: number;
@@ -131,11 +141,13 @@ export async function inspectGatewayRestart(params: {
: [];
const running = runtime.status === "running";
const runtimePid = runtime.pid;
const listenerAttributionGap = hasListenerAttributionGap(portUsage);
const ownsPort =
runtimePid != null
? portUsage.listeners.some((listener) => listenerOwnedByRuntimePid({ listener, runtimePid }))
: gatewayListeners.length > 0 ||
(portUsage.status === "busy" && portUsage.listeners.length === 0);
? portUsage.listeners.some((listener) =>
listenerOwnedByRuntimePid({ listener, runtimePid }),
) || listenerAttributionGap
: gatewayListeners.length > 0 || listenerAttributionGap;
let healthy = running && ownsPort;
if (!healthy && running && portUsage.status === "busy") {
try {