diff --git a/packages/compass/src/main/application.ts b/packages/compass/src/main/application.ts index a858b63e3fc..17b44e9d0bc 100644 --- a/packages/compass/src/main/application.ts +++ b/packages/compass/src/main/application.ts @@ -175,15 +175,13 @@ class CompassApplication { await this.setupCORSBypass(); void this.setupCompassAuthService(); - if (!process.env.CI || process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { - this.setupAutoUpdate(); - } await setupCSFLELibrary(); setupTheme(this); this.setupJavaScriptArguments(); this.setupLifecycleListeners(); this.setupApplicationMenu(); this.setupWindowManager(); + this.setupAutoUpdate(); this.trackApplicationLaunched(globalPreferences); } @@ -213,7 +211,9 @@ class CompassApplication { } private static setupAutoUpdate(): void { - CompassAutoUpdateManager.init(this); + if (!process.env.CI || process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { + void CompassAutoUpdateManager.init(this); + } } private static setupApplicationMenu(): void { diff --git a/packages/compass/src/main/auto-update-manager.spec.ts b/packages/compass/src/main/auto-update-manager.spec.ts index 026f7b27f1f..942f8dc55dc 100644 --- a/packages/compass/src/main/auto-update-manager.spec.ts +++ b/packages/compass/src/main/auto-update-manager.spec.ts @@ -6,7 +6,7 @@ import { CompassAutoUpdateManager, } from './auto-update-manager'; import type { DownloadItem } from 'electron'; -import { dialog, autoUpdater } from 'electron'; +import { dialog, autoUpdater, BrowserWindow } from 'electron'; import os from 'os'; import dl from 'electron-dl'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; @@ -344,6 +344,10 @@ describe('CompassAutoUpdateManager', function () { return Promise.resolve({ response: 0, checkboxChecked: false }); }); + sandbox.stub(BrowserWindow, 'getAllWindows').callsFake(() => { + return [{} as BrowserWindow]; + }); + const stub = sandbox.stub(dl, 'download').callsFake(() => { return Promise.resolve({} as DownloadItem); }); @@ -356,6 +360,10 @@ describe('CompassAutoUpdateManager', function () { ) ).to.eq(true); + // Any small timeout will do, we're allowing for the async tasks to + // clear + await wait(300); + expect(stub).to.be.calledOnce; }); diff --git a/packages/compass/src/main/auto-update-manager.ts b/packages/compass/src/main/auto-update-manager.ts index 3735c860ae7..6f72ee7c40d 100644 --- a/packages/compass/src/main/auto-update-manager.ts +++ b/packages/compass/src/main/auto-update-manager.ts @@ -16,6 +16,7 @@ import type { PreferencesAccess } from 'compass-preferences-model'; import { getOsInfo } from '@mongodb-js/get-os-info'; import { createIpcTrack } from '@mongodb-js/compass-telemetry'; import type { Response } from '@mongodb-js/devtools-proxy-support'; +import { pathToFileURL } from 'url'; const { log, mongoLogId, debug } = createLogger('COMPASS-AUTO-UPDATES'); const track = createIpcTrack(); @@ -60,6 +61,37 @@ function isMismatchedArchDarwin(): boolean { return process.platform === 'darwin' && getSystemArch() !== process.arch; } +async function waitForWindow(timeout = 5_000) { + const start = Date.now(); + while (start + timeout > Date.now()) { + await new Promise((resolve) => setTimeout(resolve, 100)); + const window = BrowserWindow.getAllWindows()[0]; + if (window) { + return window; + } + } + return null; +} + +async function download(url: string): Promise { + const maybeWindow = await waitForWindow(); + if (maybeWindow) { + await dl.download(maybeWindow, url, { + onCompleted(file) { + const fileURL = pathToFileURL(file.path).toString(); + void shell.openExternal(fileURL); + }, + }); + } else { + await shell.openExternal(url); + } +} + +function getMacOSDownloadUrl(channel: string, version: string): string { + version = channel === 'dev' ? 'latest' : version; + return `https://compass.mongodb.com/api/v2/download/${version}/compass/${channel}/darwin-${getSystemArch()}`; +} + type PromptForUpdateResult = 'download' | 'update' | 'cancel'; async function promptForUpdate( from: string, @@ -445,7 +477,7 @@ const STATE_UPDATE: Record< }, [AutoUpdateManagerState.ManualDownload]: { nextStates: [AutoUpdateManagerState.UserPromptedManualCheck], - enter: function (_updateManager, _fromState, updateInfo: UpdateInfo) { + enter: function (updateManager, _fromState, updateInfo: UpdateInfo) { log.info( mongoLogId(1_001_000_167), 'AutoUpdateManager', @@ -467,10 +499,11 @@ const STATE_UPDATE: Record< ); } - const url = `https://downloads.mongodb.com/compass/${ - process.env.HADRON_PRODUCT - }-${updateInfo.to}-${process.platform}-${getSystemArch()}.dmg`; - void dl.download(BrowserWindow.getAllWindows()[0], url); + const url = getMacOSDownloadUrl( + updateManager.autoUpdateOptions.channel, + updateInfo.to + ); + void download(url); }, }, [AutoUpdateManagerState.UpdateDismissed]: { @@ -827,11 +860,54 @@ class CompassAutoUpdateManager { this.setState(AutoUpdateManagerState.RestartDismissed); } - private static _init( + private static checkForMismatchedMacOSArch() { + const mismatchedOnArm = + isMismatchedArchDarwin() && getSystemArch() === 'arm64'; + + if (!mismatchedOnArm) { + return; + } + + void dialog + .showMessageBox({ + icon: COMPASS_ICON, + message: 'Mismatched architecture detected', + detail: + 'You are currently using a build of Compass that is not optimized for Apple Silicon processors. This version might have significant performance issues when used. ' + + 'Would you like to download the version of Compass optimized for Apple Silicon processors now?', + buttons: [ + 'Download Compass for Apple Silicon (Recommended)', + 'Not now', + ], + cancelId: 1, + }) + .then(({ response }) => { + if (response === 0) { + const url = getMacOSDownloadUrl( + this.autoUpdateOptions.channel, + this.autoUpdateOptions.version + ); + return download(url); + } + }) + .catch((err) => { + log.warn( + mongoLogId(1_001_000_362), + 'AutoUpdateManager', + 'Failed to download Compass for a mismatched macos arch', + { error: err.message } + ); + }); + } + + private static async _init( compassApp: typeof CompassApplication, options: Partial = {} - ): void { + ): Promise { + await app.whenReady(); + this.fetch = (url: string) => compassApp.httpClient.fetch(url); + compassApp.addExitHandler(() => { this.stop(); return Promise.resolve(); @@ -867,6 +943,8 @@ class CompassAutoUpdateManager { ...options, }; + this.checkForMismatchedMacOSArch(); + // TODO(COMPASS-7232): If auto-updates are not supported, then there is // still a menu item to check for updates and then if it finds an update but // auto-updates aren't supported it will still display a popup with an @@ -961,13 +1039,13 @@ class CompassAutoUpdateManager { ); } - static init( + static async init( compassApp: typeof CompassApplication, options: Partial = {} - ): void { + ): Promise { if (!this.initCalled) { this.initCalled = true; - this._init(compassApp, options); + await this._init(compassApp, options); } }