diff --git a/package-lock.json b/package-lock.json index 15f954e..5c27704 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "folio", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "folio", - "version": "0.1.7", + "version": "0.1.8", "license": "MIT", "dependencies": { "@tiptap/core": "^2.27.2", diff --git a/package.json b/package.json index a47c543..afb7897 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "folio", - "version": "0.1.8", + "version": "0.1.10", "description": "A simple, local, open-source desktop writing app.", "productName": "Folio", "author": "Folio Contributors", diff --git a/src/main/index.ts b/src/main/index.ts index 24cd5fa..80749cd 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1286,12 +1286,40 @@ handleIpc("folio:performUpdate", async () => { }), { mode: 0o700 } ); + // IMPORTANT: on systemd desktops (Hyprland/uwsm) the app runs inside a + // systemd scope cgroup, and a plain detached child stays in that cgroup — + // when the app quits, systemd sweeps the scope and kills the swap script + // mid-run (root cause of the "update failed" reports: the handoff died + // before swapping, and the app was relaunched from the old tree). + // systemd-run puts the script in its own transient unit that outlives us; + // verified to survive on this machine. Fall back to a detached spawn for + // non-systemd sessions. const { spawn } = await import("child_process"); - const child = spawn("/bin/sh", [script], { detached: true, stdio: "ignore" }); - child.on("error", (err) => { - console.error("[folio] updater spawn failed:", err.message); - }); - child.unref(); + let handedOff = false; + try { + const { execFileSync } = await import("child_process"); + execFileSync("systemd-run", [ + "--user", "--quiet", "--collect", + `--description=Folio updater handoff`, + "/bin/sh", script, + ], { timeout: 10_000, stdio: "ignore" }); + handedOff = true; + } catch { + try { + const child = spawn("/bin/sh", [script], { detached: true, stdio: "ignore" }); + child.on("error", (err) => { + console.error("[folio] updater spawn failed:", err.message); + }); + child.unref(); + handedOff = true; + } catch (e) { + void e; + } + } + if (!handedOff) { + try { fs.appendFileSync(updateLog, new Date().toISOString() + " FAIL: could not start swap script\n"); } catch { /* ignore */ } + return { error: "Could not start the installer. Your current version is unchanged." }; + } // Quit so the swap script can replace the app directory. setTimeout(() => app.quit(), 300); return { ok: true, version: rel.version };