From cab43b8d061e6c778bec9be756e1d4e46da9b50c Mon Sep 17 00:00:00 2001 From: Bernd Rodler Date: Tue, 4 Aug 2026 12:57:13 +0200 Subject: [PATCH] feat(electron): auto-update via electron-updater + GitHub Releases Phase 1 step 7 of VNCprodbuild. electron/main.ts calls autoUpdater.checkForUpdatesAndNotify() once the app is ready, only for packaged builds (app.isPackaged) - dev/test runs have no latest.yml and would just log a noisy 404 on every launch. electron-builder.config.js gets a matching `publish` block pointing at this repo's own GitHub Releases (brvncde-dotcom/vncmail-plus) - the skill's recommendation over standing up a new distribution channel, since the repo is already private. Flagged as the "light decision" the skill calls it, not blocking. Deliberately defensive: no code signing yet (step 9), so update verification can fail on macOS in particular. Wrapped in try/catch + autoUpdater's "error" event so a failed check is logged and swallowed, never fatal - this is background maintenance, not something the user should be blocked on. Verified with a --dir packaged build: checkForUpdatesAndNotify() throws ENOENT for app-update.yml (expected - that file is only emitted by a full `electron-builder build`, not --dir) and the error handling swallows it cleanly; the standalone server still boots and serves the app normally. npm run test:electron still green (4/4) - autoUpdater is a no-op in the unpacked dev/test path this suite exercises. --- electron-builder.config.js | 16 ++++++++++++++-- electron/main.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/electron-builder.config.js b/electron-builder.config.js index 4638e607..da98fb9e 100644 --- a/electron-builder.config.js +++ b/electron-builder.config.js @@ -3,8 +3,8 @@ // Phase 1 of the VNCprodbuild rollout (~/.claude/skills/VNCprodbuild/SKILL.md): // step 1 - base config, no targets (superseded by this file) // step 6 - this file: real packaging targets + branding icon (below) -// step 7 - electron-updater wiring (GitHub Releases feed) - adds a -// `publish` block on top of this file in a later commit. +// step 7 - this file's `publish` block + electron/main.ts's +// setupAutoUpdater() - electron-updater against GitHub Releases. // step 9 - still open: code signing / notarization (Apple Developer ID, // optional Windows cert) - both are human-owned purchases, not // configured here. Builds below ship UNSIGNED. @@ -79,4 +79,16 @@ module.exports = { ], category: "Network;Email;", }, + // electron-updater feed (see electron/main.ts's setupAutoUpdater()). + // GitHub Releases, not a new distribution channel - the skill's + // recommendation since this repo is already private and this needs no + // extra infrastructure. "Light decision" per VNCprodbuild step 7, not + // blocking, but flagged: switching later (e.g. to a self-hosted update + // server) would mean revisiting this block and the `provider` electron- + // updater talks to. + publish: { + provider: "github", + owner: "brvncde-dotcom", + repo: "vncmail-plus", + }, }; diff --git a/electron/main.ts b/electron/main.ts index a62a252d..b051f198 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -7,6 +7,7 @@ // deliberately the same server, not a reimplementation - lib/jmap/client.ts // and every app/api/** route behave identically to the web deployment. import { app, BrowserWindow, ipcMain, Notification } from "electron"; +import { autoUpdater } from "electron-updater"; import { spawn, type ChildProcess } from "node:child_process"; import { createServer } from "node:net"; import { get as httpGet } from "node:http"; @@ -156,8 +157,36 @@ ipcMain.handle( }, ); +// --- Auto-update ------------------------------------------------------- +// GitHub Releases as the update feed (electron-builder.config.js's +// `publish` block) - the skill's recommendation over standing up a new +// distribution channel, since the repo is already private. "Light +// decision" per VNCprodbuild step 7, not re-litigated here. +// +// Deliberately best-effort: there's no code signing yet (step 9), so on +// macOS in particular an update download/install can fail signature +// verification. A failed check must never take the app down - it's +// background maintenance, not something the user is blocked on. +function setupAutoUpdater(): void { + if (!app.isPackaged) { + // Unpacked dev/test runs (npm run electron:dev, the Playwright smoke + // test) have no latest.yml alongside them - checking would just log a + // noisy 404 against GitHub Releases for every dev run. + return; + } + autoUpdater.autoDownload = true; + autoUpdater.autoInstallOnAppQuit = true; + autoUpdater.on("error", (error) => { + console.error("[electron] auto-update error:", error); + }); + autoUpdater.checkForUpdatesAndNotify().catch((error) => { + console.error("[electron] checkForUpdatesAndNotify failed:", error); + }); +} + app.whenReady().then(() => { void createMainWindow(); + setupAutoUpdater(); }); app.on("window-all-closed", () => {