feat: detached systemd restart + runnable Updates panel commands

Run Linux restart commands inside a detached transient systemd user
service (systemd-run --user) so the restart survives the launching
terminal being killed and its logs can be inspected via journalctl.

Make the Updates panel actionable: every command has Copy and Run,
a single recommended all-in-one command is shown at the top, and the
rest are grouped as optional additional commands.
This commit is contained in:
Federico Jaramillo Martinez
2026-06-13 12:58:09 +02:00
parent 1000d4ddd6
commit 3c6b4a4869
5 changed files with 117 additions and 30 deletions
+15 -5
View File
@@ -437,9 +437,9 @@ async function nativeServiceCommands(): Promise<NativeServiceCommands> {
const restartable = web.length === 0 ? [] : installedServiceRefs(installed, restartServiceOrder, restartServiceOrder);
const status = installedServiceRefs(installed);
return {
...(restartable.length === 0 ? {} : { restart: restartNativeServicesCommand(backend, restartable) }),
...(web.length === 0 ? {} : { restartWeb: restartNativeServicesCommand(backend, web) }),
...(sessiond.length === 0 ? {} : { restartSessiond: restartNativeServicesCommand(backend, sessiond) }),
...(restartable.length === 0 ? {} : { restart: restartNativeServicesCommand(backend, restartable, "pi-web-restart") }),
...(web.length === 0 ? {} : { restartWeb: restartNativeServicesCommand(backend, web, "pi-web-restart-web") }),
...(sessiond.length === 0 ? {} : { restartSessiond: restartNativeServicesCommand(backend, sessiond, "pi-web-restart-sessiond") }),
...(status.length === 0 ? {} : { status: statusNativeServicesCommand(backend, status) }),
};
}
@@ -470,8 +470,18 @@ function launchdServiceDir(): string {
return join(homedir(), "Library", "LaunchAgents");
}
function restartNativeServicesCommand(backend: NativeServiceBackendKind, refs: NativeServiceRef[]): string {
if (backend === "systemd") return `systemctl --user restart ${refs.map((ref) => ref.systemdName).join(" ")}`;
function restartNativeServicesCommand(backend: NativeServiceBackendKind, refs: NativeServiceRef[], systemdUnit: string): string {
// On systemd, run the restart inside a transient, detached `.service` unit
// (the default `systemd-run` mode, not `--scope`). A scope would stay a child
// of the calling shell and die with the terminal; a transient service is
// reparented to the user service manager, so the restart finishes even when
// restarting the session daemon kills the pi-web terminal that launched it.
// `--collect` cleans the unit up afterwards, and the fixed `--unit` name makes
// logs easy to find with `journalctl --user -u <unit>`.
if (backend === "systemd") {
const names = refs.map((ref) => ref.systemdName).join(" ");
return `systemd-run --user --collect --unit=${systemdUnit} -- systemctl --user restart ${names}`;
}
return refs.map((ref) => `launchctl kickstart -k gui/$(id -u)/${ref.launchdLabel}`).join(" && ");
}