diff --git a/build/set_build_version.ts b/build/set_build_version.ts index ca43876a42..0b1d5724a8 100644 --- a/build/set_build_version.ts +++ b/build/set_build_version.ts @@ -37,6 +37,7 @@ function getBuildChannel(): string { case "rc": return preRelease; case undefined: + case "latest": return "latest"; // needed because electron-updater does not take build information into account when resolving if update is available default: throw new Error(`invalid pre-release ${preRelease}`); diff --git a/src/common/ipc/update-available.ipc.ts b/src/common/ipc/update-available.ipc.ts index 87ec740f44..de6342ae54 100644 --- a/src/common/ipc/update-available.ipc.ts +++ b/src/common/ipc/update-available.ipc.ts @@ -22,6 +22,8 @@ import type { UpdateInfo } from "electron-updater"; export const UpdateAvailableChannel = "update-available"; +export const AutoUpdateChecking = "auto-update:checking"; +export const AutoUpdateNoUpdateAvailable = "auto-update:no-update"; export const AutoUpdateLogPrefix = "[UPDATE-CHECKER]"; export type UpdateAvailableFromMain = [backChannel: string, updateInfo: UpdateInfo]; diff --git a/src/main/app-updater.ts b/src/main/app-updater.ts index a767a44d4f..3c358acde6 100644 --- a/src/main/app-updater.ts +++ b/src/main/app-updater.ts @@ -21,9 +21,9 @@ import { autoUpdater, UpdateInfo } from "electron-updater"; import logger from "./logger"; -import { isDevelopment, isLinux, isMac, isPublishConfigured, isTestEnv } from "../common/vars"; +import { isLinux, isMac, isPublishConfigured, isTestEnv } from "../common/vars"; import { delay } from "../common/utils"; -import { areArgsUpdateAvailableToBackchannel, AutoUpdateLogPrefix, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc"; +import { areArgsUpdateAvailableToBackchannel, AutoUpdateChecking, AutoUpdateLogPrefix, AutoUpdateNoUpdateAvailable, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc"; import { once } from "lodash"; import { ipcMain } from "electron"; import { nextUpdateChannel } from "./utils/update-channel"; @@ -68,23 +68,24 @@ function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: Upd } } +autoUpdater.logger = { + info: message => logger.info(`[AUTO-UPDATE]: electron-updater:`, message), + warn: message => logger.warn(`[AUTO-UPDATE]: electron-updater:`, message), + error: message => logger.error(`[AUTO-UPDATE]: electron-updater:`, message), + debug: message => logger.debug(`[AUTO-UPDATE]: electron-updater:`, message), +}; + /** * starts the automatic update checking * @param interval milliseconds between interval to check on, defaults to 24h */ export const startUpdateChecking = once(function (interval = 1000 * 60 * 60 * 24): void { - if (isDevelopment || isTestEnv) { + if (!isAutoUpdateEnabled() || isTestEnv) { return; } const userStore = UserStore.getInstance(); - autoUpdater.logger = { - info: message => logger.info(`[AUTO-UPDATE]: electron-updater: ${message}`), - warn: message => logger.warn(`[AUTO-UPDATE]: electron-updater: ${message}`), - error: message => logger.error(`[AUTO-UPDATE]: electron-updater: ${message}`), - debug: message => logger.debug(`[AUTO-UPDATE]: electron-updater: ${message}`), - }; autoUpdater.autoDownload = false; autoUpdater.autoInstallOnAppQuit = false; autoUpdater.channel = userStore.updateChannel; @@ -136,7 +137,13 @@ export const startUpdateChecking = once(function (interval = 1000 * 60 * 60 * 24 logger.info(`${AutoUpdateLogPrefix}: update not available from ${autoUpdater.channel}, will check ${nextChannel} channel next`); - autoUpdater.channel = nextChannel; + if (nextChannel !== autoUpdater.channel) { + autoUpdater.channel = nextChannel; + autoUpdater.checkForUpdates() + .catch(error => logger.error(`${AutoUpdateLogPrefix}: failed with an error`, error)); + } else { + broadcastMessage(AutoUpdateNoUpdateAvailable); + } }); async function helper() { @@ -157,8 +164,9 @@ export async function checkForUpdates(): Promise { autoUpdater.channel = userStore.updateChannel; autoUpdater.allowDowngrade = userStore.isAllowedToDowngrade; + broadcastMessage(AutoUpdateChecking); await autoUpdater.checkForUpdates(); } catch (error) { - logger.error(`${AutoUpdateLogPrefix}: failed with an error`, { error: String(error) }); + logger.error(`${AutoUpdateLogPrefix}: failed with an error`, error); } } diff --git a/src/main/menu.ts b/src/main/menu.ts index 6718cf0003..08012cee23 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -29,6 +29,7 @@ import { exitApp } from "./exit-app"; import { broadcastMessage } from "../common/ipc"; import * as packageJson from "../../package.json"; import { preferencesURL, extensionsURL, addClusterURL, catalogURL, welcomeURL } from "../common/routes"; +import { checkForUpdates, isAutoUpdateEnabled } from "./app-updater"; export type MenuTopId = "mac" | "file" | "edit" | "view" | "help"; @@ -61,10 +62,8 @@ export function showAbout(browserWindow: BrowserWindow) { } export function buildMenu(windowManager: WindowManager) { - function ignoreOnMac(menuItems: MenuItemConstructorOptions[]) { - if (isMac) return []; - - return menuItems; + function ignoreIf(check: boolean, menuItems: MenuItemConstructorOptions[]) { + return check ? [] : menuItems; } async function navigate(url: string) { @@ -72,6 +71,10 @@ export function buildMenu(windowManager: WindowManager) { await windowManager.navigate(url); } + const autoUpdateDisabled = !isAutoUpdateEnabled(); + + logger.info(`[MENU]: autoUpdateDisabled=${autoUpdateDisabled}`); + const macAppMenu: MenuItemsOpts = { label: app.getName(), id: "root", @@ -83,6 +86,13 @@ export function buildMenu(windowManager: WindowManager) { showAbout(browserWindow); }, }, + ...ignoreIf(autoUpdateDisabled, [{ + label: "Check for updates", + click() { + checkForUpdates() + .then(() => windowManager.ensureMainWindow()); + }, + }]), { type: "separator" }, { label: "Preferences", @@ -129,7 +139,7 @@ export function buildMenu(windowManager: WindowManager) { navigate(addClusterURL()); }, }, - ...ignoreOnMac([ + ...ignoreIf(isMac, [ { type: "separator" }, { label: "Preferences", @@ -158,7 +168,7 @@ export function buildMenu(windowManager: WindowManager) { }, ] as MenuItemConstructorOptions[] : []), - ...ignoreOnMac([ + ...ignoreIf(isMac, [ { label: "Exit", accelerator: "Alt+F4", @@ -264,7 +274,7 @@ export function buildMenu(windowManager: WindowManager) { shell.openExternal(supportUrl); }, }, - ...ignoreOnMac([ + ...ignoreIf(isMac, [ { label: `About ${productName}`, id: "about", @@ -272,6 +282,13 @@ export function buildMenu(windowManager: WindowManager) { showAbout(browserWindow); }, }, + ...ignoreIf(autoUpdateDisabled, [{ + label: "Check for updates", + click() { + checkForUpdates() + .then(() => windowManager.ensureMainWindow()); + }, + }]), ]), ], }; diff --git a/src/renderer/ipc/index.tsx b/src/renderer/ipc/index.tsx index 4a1c250a6a..211dfbc8f2 100644 --- a/src/renderer/ipc/index.tsx +++ b/src/renderer/ipc/index.tsx @@ -21,7 +21,7 @@ import React from "react"; import { ipcRenderer, IpcRendererEvent } from "electron"; -import { areArgsUpdateAvailableFromMain, UpdateAvailableChannel, onCorrect, UpdateAvailableFromMain, BackchannelArg, ClusterListNamespaceForbiddenChannel, isListNamespaceForbiddenArgs, ListNamespaceForbiddenArgs, HotbarTooManyItems } from "../../common/ipc"; +import { areArgsUpdateAvailableFromMain, UpdateAvailableChannel, onCorrect, UpdateAvailableFromMain, BackchannelArg, ClusterListNamespaceForbiddenChannel, isListNamespaceForbiddenArgs, ListNamespaceForbiddenArgs, HotbarTooManyItems, ipcRendererOn, AutoUpdateChecking, AutoUpdateNoUpdateAvailable } from "../../common/ipc"; import { Notifications, notificationsStore } from "../components/notifications"; import { Button } from "../components/button"; import { isMac } from "../../common/vars"; @@ -153,4 +153,10 @@ export function registerIpcListeners() { listener: HotbarTooManyItemsHandler, verifier: (args: unknown[]): args is [] => args.length === 0, }); + ipcRendererOn(AutoUpdateChecking, () => { + Notifications.shortInfo("Checking for updates"); + }); + ipcRendererOn(AutoUpdateNoUpdateAvailable, () => { + Notifications.shortInfo("No update is currently available"); + }); }