From 3961e3d7bb7e53bb7a1f3d098d87c657d7ad3bb6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Sep 2025 12:49:31 +0100 Subject: [PATCH] Handle unsupported macOS versions better (#2552) --- src/electron-main.ts | 1 - src/i18n/strings/en_EN.json | 5 ++++ src/ipc.ts | 7 ++++++ src/preload.cts | 1 + src/updater.ts | 48 +++++++++++++++++++++++++++++++++---- tsconfig.json | 2 +- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/electron-main.ts b/src/electron-main.ts index 3f5465cc..0f221dc2 100644 --- a/src/electron-main.ts +++ b/src/electron-main.ts @@ -424,7 +424,6 @@ app.on("ready", async () => { if (argv["update"] === false) { console.log("Auto update disabled via command line flag"); } else if (global.vectorConfig["update_base_url"]) { - console.log(`Starting auto update with base URL: ${global.vectorConfig["update_base_url"]}`); void updater.start(global.vectorConfig["update_base_url"]); } else { console.log("No update_base_url is defined: auto update is disabled"); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d4882be5..88c584a0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -32,6 +32,11 @@ "speech_start_speaking": "Start Speaking", "speech_stop_speaking": "Stop Speaking" }, + "eol": { + "no_more_updates": "You are running an unsupported version of macOS. Please upgrade to receive %(brand)s updates.", + "title": "System unsupported", + "warning": "You are running an unsupported version of macOS. Please upgrade to ensure %(brand)s keeps working." + }, "file_menu": { "label": "File" }, diff --git a/src/ipc.ts b/src/ipc.ts index 580e3356..8efcd947 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -218,3 +218,10 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) { }); ipcMain.handle("getConfig", () => global.vectorConfig); + +const initialisePromiseWithResolvers = Promise.withResolvers(); +export const initialisePromise = initialisePromiseWithResolvers.promise; + +ipcMain.once("initialise", () => { + initialisePromiseWithResolvers.resolve(); +}); diff --git a/src/preload.cts b/src/preload.cts index 21d75939..3f9e2e6f 100644 --- a/src/preload.cts +++ b/src/preload.cts @@ -60,6 +60,7 @@ contextBridge.exposeInMainWorld("electron", { */ supportsBadgeOverlay: boolean; }> { + ipcRenderer.emit("initialise"); const [{ protocol, sessionId }, config, supportedSettings] = await Promise.all([ ipcRenderer.invoke("getProtocol"), ipcRenderer.invoke("getConfig"), diff --git a/src/updater.ts b/src/updater.ts index d0cdf7f8..438d841b 100644 --- a/src/updater.ts +++ b/src/updater.ts @@ -7,8 +7,11 @@ Please see LICENSE files in the repository root for full details. import { app, autoUpdater, ipcMain } from "electron"; import fs from "node:fs/promises"; +import os from "node:os"; import { getSquirrelExecutable } from "./squirrelhooks.js"; +import { _t } from "./language-helper.js"; +import { initialisePromise } from "./ipc.js"; const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; const INITIAL_UPDATE_DELAY_MS = 30 * 1000; @@ -69,7 +72,8 @@ async function pollForUpdates(): Promise { } export async function start(updateBaseUrl: string): Promise { - if (!(await available(updateBaseUrl))) return; + if (!(await available())) return; + console.log(`Starting auto update with base URL: ${updateBaseUrl}`); if (!updateBaseUrl.endsWith("/")) { updateBaseUrl = updateBaseUrl + "/"; } @@ -111,10 +115,15 @@ export async function start(updateBaseUrl: string): Promise { } } -async function available(updateBaseUrl?: string): Promise { +/** + * Check if auto update is available on this platform. + * Has a side effect of firing showToast on EOL platforms so must only be called once! + * @returns True if auto update is available + */ +async function available(): Promise { if (process.platform === "linux") { // Auto update is not supported on Linux - console.log("Auto update not supported on this platform"); + console.warn("Auto update not supported on this platform"); return false; } @@ -122,13 +131,42 @@ async function available(updateBaseUrl?: string): Promise { try { await fs.access(getSquirrelExecutable()); } catch { - console.log("Squirrel not found, auto update not supported"); + console.warn("Squirrel not found, auto update not supported"); return false; } } // Otherwise we're either on macOS or Windows with Squirrel - return !!updateBaseUrl; + if (process.platform === "darwin") { + // OS release returns the Darwin kernel version, not the macOS version, see + // https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history to interpret it + const release = os.release(); + const major = parseInt(release.split(".")[0], 10); + + if (major < 21) { + // If the macOS version is too old for modern Electron support then disable auto update to prevent the app updating and bricking itself. + // The oldest macOS version supported by Chromium/Electron 38 is Monterey (12.x) which started with Darwin 21.0 + initialisePromise.then(() => { + ipcMain.emit("showToast", { + title: _t("eol|title"), + description: _t("eol|no_more_updates", { brand: global.trayConfig.brand }), + }); + }); + console.warn("Auto update not supported, macOS version too old"); + return false; + } else if (major < 22) { + // If the macOS version is EOL then show a warning message. + // The oldest macOS version still supported by Apple is Ventura (13.x) which started with Darwin 22.0 + initialisePromise.then(() => { + ipcMain.emit("showToast", { + title: _t("eol|title"), + description: _t("eol|warning", { brand: global.trayConfig.brand }), + }); + }); + } + } + + return true; } ipcMain.on("install_update", installUpdate); diff --git a/tsconfig.json b/tsconfig.json index 6248dcff..d07b7005 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "rootDir": "./src", "declaration": true, "typeRoots": ["src/@types", "node_modules/@types"], - "lib": ["es2022"], + "lib": ["es2022", "es2024.promise"], "types": ["node"], "strict": true },