diff --git a/HOW-TO-UPDATE.md b/HOW-TO-UPDATE.md new file mode 100644 index 0000000000..85fbb7f105 --- /dev/null +++ b/HOW-TO-UPDATE.md @@ -0,0 +1,35 @@ +# Updating Blap + +All builds are published on the **[Blap releases page](https://codeberg.org/Enkisu/Blap/releases)**. +Grab the newest file for your platform below. + +## Windows +Automatic — when a new version is out, Blap shows a **"New update"** prompt; click it and Blap +updates itself. (Or download the latest `Blap Setup X.Y.Z.exe` and run it.) + +## Linux +Blap can't update itself on Linux (packages are owned by your package manager / the flatpak +sandbox), so it just **notifies** you — then you update with one command for your format: + +### Flatpak (Bazzite, etc.) +Download the new `Blap-X.Y.Z-x86_64.flatpak` and: +``` +flatpak install --user Blap-X.Y.Z-x86_64.flatpak +``` +(installing the newer bundle upgrades the existing install) + +### Debian / Ubuntu (`.deb`) +``` +sudo apt install ./blap_X.Y.Z_amd64.deb +``` + +### Arch (`.pacman`) +``` +sudo pacman -U blap-X.Y.Z.pacman +``` + +### Portable (`.tar.gz`) +Extract the new tarball over your existing copy and run the `blap` executable — no install needed. + +--- +_Replace `X.Y.Z` with the version from the [releases page](https://codeberg.org/Enkisu/Blap/releases)._ diff --git a/apps/desktop/electron-builder.ts b/apps/desktop/electron-builder.ts index efd5f2272f..44637e6654 100644 --- a/apps/desktop/electron-builder.ts +++ b/apps/desktop/electron-builder.ts @@ -138,8 +138,7 @@ const config: Omit, "electronFuses"> & { channel: "latest", }, linux: { - // AppImage is the only Linux format electron-updater can auto-update in-app. - target: ["tar.gz", "deb", "AppImage"], + target: ["deb", "flatpak", "tar.gz", "pacman"], category: "Network;InstantMessaging;Chat", icon: "icon.png", executableName: variant.name, // element-desktop or element-desktop-nightly @@ -196,9 +195,8 @@ const config: Omit, "electronFuses"> & { badgeIcon: "build/icon.icon", }, win: { - // NSIS is required for electron-updater auto-update on Windows (replaces - // the old Squirrel target). msi kept for manual/enterprise installs. - target: ["nsis", "msi"], + // NSIS = the .exe installer (also what electron-updater uses on Windows). + target: ["nsis"], signtoolOptions: { signingHashAlgorithms: ["sha256"], }, diff --git a/apps/desktop/src/updater.ts b/apps/desktop/src/updater.ts index 489ba32f91..5cf69469f1 100644 --- a/apps/desktop/src/updater.ts +++ b/apps/desktop/src/updater.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { app, ipcMain } from "electron"; +import { app, ipcMain, Notification, shell } from "electron"; // electron-updater is CommonJS; this is the documented ESM interop form. import electronUpdater, { type UpdateInfo } from "electron-updater"; @@ -14,6 +14,11 @@ const { autoUpdater } = electronUpdater; const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; const INITIAL_UPDATE_DELAY_MS = 30 * 1000; +// Blap update feed (same Codeberg release the electron-builder `publish` config points at). +const CODEBERG_BASE = "https://codeberg.org/Enkisu/Blap"; +const LINUX_FEED_URL = `${CODEBERG_BASE}/releases/latest/download/latest-linux.yml`; +const HOWTO_UPDATE_URL = `${CODEBERG_BASE}/src/branch/main/HOW-TO-UPDATE.md`; + function ipcChannelSendUpdateStatus(status: boolean | string): void { global.mainWindow?.webContents.send("check_updates", status); } @@ -34,18 +39,56 @@ async function pollForUpdates(): Promise { } } +/** Compare dotted numeric versions; true if `latest` is strictly newer than `current`. */ +function isNewer(latest: string, current: string): boolean { + const l = latest.split(".").map((n) => parseInt(n, 10) || 0); + const c = current.split(".").map((n) => parseInt(n, 10) || 0); + for (let i = 0; i < Math.max(l.length, c.length); i++) { + const a = l[i] ?? 0; + const b = c[i] ?? 0; + if (a !== b) return a > b; + } + return false; +} + +let lastLinuxNotifiedVersion: string | undefined; + +/** + * Linux notify-only update check. + * + * Linux builds can't self-install (deb/pacman are package-manager owned, flatpak + * is sandboxed), so instead of updating in place we read the same Codeberg feed + * the Windows updater uses and, if there's a newer version, show a desktop + * notification that opens HOW-TO-UPDATE.md (which has the per-format commands). + */ +async function checkLinuxUpdate(): Promise { + try { + const res = await fetch(`${LINUX_FEED_URL}?t=${Date.now()}`); + if (!res.ok) return; + const text = await res.text(); + const latest = /^version:\s*(.+)$/m.exec(text)?.[1]?.trim(); + if (!latest || !isNewer(latest, app.getVersion())) return; + if (latest === lastLinuxNotifiedVersion) return; // don't nag every poll + lastLinuxNotifiedVersion = latest; + if (!Notification.isSupported()) return; + const notification = new Notification({ + title: `Blap ${latest} is available`, + body: "Click to see how to update.", + }); + notification.on("click", () => void shell.openExternal(HOWTO_UPDATE_URL)); + notification.show(); + } catch (e) { + console.log("Couldn't check for Linux update", e); + } +} + /** * Start the auto-updater. * - * Uses electron-updater, which reads its feed from `app-update.yml` baked in at - * package time from the electron-builder `publish` config (a Codeberg release - * "latest/download" URL). No runtime feed URL is needed. - * - * Notes: - * - Only works for packaged builds (dev has no app-update.yml). - * - On Linux, electron-updater only supports AppImage; deb/flatpak update via - * their own channels, so this no-ops there. - * - macOS would need a signed build + zip target to update. + * Windows/macOS use electron-updater (reads its feed from `app-update.yml`, baked + * in at package time from the electron-builder Codeberg `publish` config), which + * downloads and installs in place. Linux uses a notify-only check (see above). + * Only runs for packaged builds (dev has no app-update.yml). */ export async function start(): Promise { if (!app.isPackaged) { @@ -53,6 +96,13 @@ export async function start(): Promise { return; } + if (process.platform === "linux") { + console.log("Starting Linux update notifier (notify-only)"); + setTimeout(checkLinuxUpdate, INITIAL_UPDATE_DELAY_MS); + setInterval(checkLinuxUpdate, UPDATE_POLL_INTERVAL_MS); + return; + } + console.log("Starting auto update via electron-updater"); autoUpdater.autoDownload = true; @@ -89,4 +139,8 @@ export async function start(): Promise { } ipcMain.on("install_update", installUpdate); -ipcMain.on("check_updates", pollForUpdates); +ipcMain.on("check_updates", () => { + // Manual "check for updates" from the renderer. + if (process.platform === "linux") void checkLinuxUpdate(); + else void pollForUpdates(); +});