diff --git a/.changeset/restart-web-before-sessiond.md b/.changeset/restart-web-before-sessiond.md new file mode 100644 index 0000000..d4bbbea --- /dev/null +++ b/.changeset/restart-web-before-sessiond.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Restart the web/UI services before the session daemon in the suggested "Restart all" command and `pi-web restart`, so running the command from a PI WEB terminal still restarts the UI even though restarting the session daemon kills the terminal. diff --git a/src/cli.ts b/src/cli.ts index ca28bfa..d7f9dea 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -111,6 +111,10 @@ const serviceRefs: Record = { const productionServiceIds: ServiceId[] = ["sessiond", "web"]; const startServiceOrder: ServiceId[] = ["sessiond", "web", "uiDev"]; const stopServiceOrder: ServiceId[] = ["web", "uiDev", "sessiond"]; +// Restart web/UI before sessiond: when `pi-web restart` runs in a pi-web +// terminal (owned by sessiond), restarting sessiond kills the command, so any +// services handled after it would never be restarted. +const restartServiceOrder: ServiceId[] = ["web", "uiDev", "sessiond"]; function platformLabel(): string { if (process.platform === "darwin") return "macOS"; @@ -377,6 +381,10 @@ function stopOrder(refs: ServiceRef[]): ServiceRef[] { return orderServiceRefs(refs, stopServiceOrder); } +function restartOrder(refs: ServiceRef[]): ServiceRef[] { + return orderServiceRefs(refs, restartServiceOrder); +} + function productionServiceDefinitions(options: InstallOptions, configPath: string, executables: ServiceExecutables): ServiceDefinition[] { return [ { @@ -827,7 +835,7 @@ async function uninstall(): Promise { } function systemdServiceAction(action: "start" | "stop" | "restart", refs: ServiceRef[]): void { - const orderedRefs = action === "stop" ? stopOrder(refs) : startOrder(refs); + const orderedRefs = action === "stop" ? stopOrder(refs) : action === "restart" ? restartOrder(refs) : startOrder(refs); run("systemctl", ["--user", action, ...orderedRefs.map((ref) => ref.systemdName)], { check: true }); } @@ -838,7 +846,13 @@ function launchdServiceAction(action: "start" | "stop" | "restart", refs: Servic } if (action === "restart") { - for (const ref of stopOrder(refs)) launchdBootout(ref); + // Restart each service fully (bootout + start) before moving to the next, + // so the web/UI services are back up before sessiond is restarted. + for (const ref of restartOrder(refs)) { + launchdBootout(ref); + launchdStart(ref); + } + return; } for (const ref of startOrder(refs)) launchdStart(ref); diff --git a/src/server/piWebStatus.test.ts b/src/server/piWebStatus.test.ts index b31c5da..1060652 100644 --- a/src/server/piWebStatus.test.ts +++ b/src/server/piWebStatus.test.ts @@ -71,7 +71,7 @@ describe("PI WEB status", () => { const status = await getPiWebStatus(daemon); - expect(status.commands.restart).toBe("systemctl --user restart pi-web-sessiond.service pi-web-ui-dev.service"); + expect(status.commands.restart).toBe("systemctl --user restart pi-web-ui-dev.service pi-web-sessiond.service"); expect(status.commands.restartWeb).toBe("systemctl --user restart pi-web-ui-dev.service"); expect(status.commands.restartSessiond).toBe("systemctl --user restart pi-web-sessiond.service"); expect(status.messages.find((message) => message.id === "sessiond-stale")?.command).toBe("systemctl --user restart pi-web-sessiond.service"); diff --git a/src/server/piWebStatus.ts b/src/server/piWebStatus.ts index e033e12..85d1203 100644 --- a/src/server/piWebStatus.ts +++ b/src/server/piWebStatus.ts @@ -58,6 +58,10 @@ const serviceRefs: Record = { }; const startServiceOrder: ServiceId[] = ["sessiond", "web", "uiDev"]; +// Restart web/UI before sessiond: when the restart command runs in a pi-web +// terminal (owned by sessiond), restarting sessiond kills the command, so any +// services listed after it would never be restarted. +const restartServiceOrder: ServiceId[] = ["web", "uiDev", "sessiond"]; interface PackageInfo { name: string; @@ -430,7 +434,7 @@ async function nativeServiceCommands(): Promise { if (installed.size === 0) return {}; const web = installedServiceRefs(installed, ["web", "uiDev"]); const sessiond = installedServiceRefs(installed, ["sessiond"]); - const restartable = web.length === 0 ? [] : installedServiceRefs(installed); + const restartable = web.length === 0 ? [] : installedServiceRefs(installed, restartServiceOrder, restartServiceOrder); const status = installedServiceRefs(installed); return { ...(restartable.length === 0 ? {} : { restart: restartNativeServicesCommand(backend, restartable) }), @@ -450,8 +454,8 @@ function installedServiceIds(backend: NativeServiceBackendKind): Set return new Set(startServiceOrder.filter((id) => existsSync(serviceFilePath(backend, serviceRefs[id])))); } -function installedServiceRefs(installed: Set, candidates: ServiceId[] = startServiceOrder): NativeServiceRef[] { - return startServiceOrder.filter((id) => candidates.includes(id) && installed.has(id)).map((id) => serviceRefs[id]); +function installedServiceRefs(installed: Set, candidates: ServiceId[] = startServiceOrder, order: ServiceId[] = startServiceOrder): NativeServiceRef[] { + return order.filter((id) => candidates.includes(id) && installed.has(id)).map((id) => serviceRefs[id]); } function serviceFilePath(backend: NativeServiceBackendKind, ref: NativeServiceRef): string {