From f67c9ff65e7dca30017c21dbe4cdeb67e61799fb Mon Sep 17 00:00:00 2001 From: Thomas Hart Date: Tue, 25 Aug 2026 18:33:30 +0000 Subject: [PATCH 1/2] feat: Apply downloaded update when all windows are closed Install a waiting update when Hyper has no open windows, instead of leaving the process idle on the old version. --- app/index.ts | 4 ++++ app/updater.ts | 17 +++++++++++++++++ app/utils/should-auto-install-update.ts | 3 +++ test/unit/updater.test.ts | 20 ++++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 app/utils/should-auto-install-update.ts create mode 100644 test/unit/updater.test.ts diff --git a/app/index.ts b/app/index.ts index 84a804a4da14..2717e4a1189c 100644 --- a/app/index.ts +++ b/app/index.ts @@ -34,6 +34,7 @@ import parseUrl from 'parse-url'; import * as AppMenu from './menus/menu'; import * as plugins from './plugins'; import {newWindow} from './ui/window'; +import {installUpdateIfNoWindows} from './updater'; import {installCLI} from './utils/cli-install'; import * as windowUtils from './utils/window-utils'; @@ -168,6 +169,9 @@ app.on('ready', () => }); app.on('window-all-closed', () => { + if (installUpdateIfNoWindows()) { + return; + } if (process.platform !== 'darwin') { app.quit(); } diff --git a/app/updater.ts b/app/updater.ts index c7d07d4d4f73..2635d48d0035 100644 --- a/app/updater.ts +++ b/app/updater.ts @@ -10,6 +10,7 @@ import autoUpdaterLinux from './auto-updater-linux'; import {getDefaultProfile} from './config'; import {version} from './package.json'; import {getDecoratedConfig} from './plugins'; +import {shouldAutoInstallUpdate} from './utils/should-auto-install-update'; const {platform} = process; const isLinux = platform === 'linux'; @@ -36,6 +37,15 @@ const checkForUpdates = async () => { let isInit = false; // Default to the "stable" update channel let canaryUpdates = false; +let updateReady = false; + +export function installUpdateIfNoWindows(): boolean { + if (!shouldAutoInstallUpdate(updateReady, app.getWindows().size, !isLinux)) { + return false; + } + autoUpdater.quitAndInstall(); + return true; +} const buildFeedUrl = (canary: boolean, currentVersion: string) => { const updatePrefix = canary ? 'releases-canary' : 'releases'; @@ -61,6 +71,13 @@ async function init() { autoUpdater.setFeedURL({url: feedURL}); + if (!isLinux) { + autoUpdater.on('update-downloaded', () => { + updateReady = true; + installUpdateIfNoWindows(); + }); + } + setTimeout(() => { void checkForUpdates(); }, ms('10s')); diff --git a/app/utils/should-auto-install-update.ts b/app/utils/should-auto-install-update.ts new file mode 100644 index 000000000000..3ab63cfa793d --- /dev/null +++ b/app/utils/should-auto-install-update.ts @@ -0,0 +1,3 @@ +export function shouldAutoInstallUpdate(updateReady: boolean, openWindowCount: number, canInstall: boolean): boolean { + return updateReady && canInstall && openWindowCount === 0; +} diff --git a/test/unit/updater.test.ts b/test/unit/updater.test.ts new file mode 100644 index 000000000000..c5d472000a85 --- /dev/null +++ b/test/unit/updater.test.ts @@ -0,0 +1,20 @@ +import test from 'ava'; + +import {shouldAutoInstallUpdate} from '../../app/utils/should-auto-install-update'; + +test('installs when an update is ready and no windows are open', (t) => { + t.true(shouldAutoInstallUpdate(true, 0, true)); +}); + +test('does not install while windows are still open', (t) => { + t.false(shouldAutoInstallUpdate(true, 1, true)); + t.false(shouldAutoInstallUpdate(true, 3, true)); +}); + +test('does not install when no update has been downloaded', (t) => { + t.false(shouldAutoInstallUpdate(false, 0, true)); +}); + +test('does not install when the platform cannot apply updates', (t) => { + t.false(shouldAutoInstallUpdate(true, 0, false)); +}); From 8aa5d9b6a9128dfa34fbf8c5974425e1436cba64 Mon Sep 17 00:00:00 2001 From: Thomas Hart Date: Tue, 25 Aug 2026 18:41:27 +0000 Subject: [PATCH 2/2] fix: only auto-install idle updates on darwin quitAndInstall restarts the app, so closing the last window on Windows must keep falling through to app.quit(). macOS is the only platform where zero windows still means Hyper is running. --- app/updater.ts | 2 +- app/utils/should-auto-install-update.ts | 8 ++++++-- test/unit/updater.test.ts | 22 +++++++++++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/updater.ts b/app/updater.ts index 2635d48d0035..6cfcf9969a94 100644 --- a/app/updater.ts +++ b/app/updater.ts @@ -40,7 +40,7 @@ let canaryUpdates = false; let updateReady = false; export function installUpdateIfNoWindows(): boolean { - if (!shouldAutoInstallUpdate(updateReady, app.getWindows().size, !isLinux)) { + if (!shouldAutoInstallUpdate(updateReady, app.getWindows().size, platform)) { return false; } autoUpdater.quitAndInstall(); diff --git a/app/utils/should-auto-install-update.ts b/app/utils/should-auto-install-update.ts index 3ab63cfa793d..8a416e31be2e 100644 --- a/app/utils/should-auto-install-update.ts +++ b/app/utils/should-auto-install-update.ts @@ -1,3 +1,7 @@ -export function shouldAutoInstallUpdate(updateReady: boolean, openWindowCount: number, canInstall: boolean): boolean { - return updateReady && canInstall && openWindowCount === 0; +export function shouldAutoInstallUpdate( + updateReady: boolean, + openWindowCount: number, + platform: NodeJS.Platform +): boolean { + return updateReady && openWindowCount === 0 && platform === 'darwin'; } diff --git a/test/unit/updater.test.ts b/test/unit/updater.test.ts index c5d472000a85..7003506206b8 100644 --- a/test/unit/updater.test.ts +++ b/test/unit/updater.test.ts @@ -2,19 +2,23 @@ import test from 'ava'; import {shouldAutoInstallUpdate} from '../../app/utils/should-auto-install-update'; -test('installs when an update is ready and no windows are open', (t) => { - t.true(shouldAutoInstallUpdate(true, 0, true)); +test('installs on darwin when an update is ready and no windows are open', (t) => { + t.true(shouldAutoInstallUpdate(true, 0, 'darwin')); }); -test('does not install while windows are still open', (t) => { - t.false(shouldAutoInstallUpdate(true, 1, true)); - t.false(shouldAutoInstallUpdate(true, 3, true)); +test('does not install on win32 when an update is ready and no windows are open', (t) => { + t.false(shouldAutoInstallUpdate(true, 0, 'win32')); }); -test('does not install when no update has been downloaded', (t) => { - t.false(shouldAutoInstallUpdate(false, 0, true)); +test('does not install on linux when an update is ready and no windows are open', (t) => { + t.false(shouldAutoInstallUpdate(true, 0, 'linux')); +}); + +test('does not install while windows are still open', (t) => { + t.false(shouldAutoInstallUpdate(true, 1, 'darwin')); + t.false(shouldAutoInstallUpdate(true, 3, 'darwin')); }); -test('does not install when the platform cannot apply updates', (t) => { - t.false(shouldAutoInstallUpdate(true, 0, false)); +test('does not install when no update has been downloaded', (t) => { + t.false(shouldAutoInstallUpdate(false, 0, 'darwin')); });