From ba46a1cf54448214cd7a5d8d4d08d36c9d7ad615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 14 Sep 2023 20:30:39 +0200 Subject: [PATCH] extensionSystem: Update immediately after major upgrades Currently we periodically check for updated extensions, prepare an update and perform it at the next login. This is largely due to the fact that once an extension has been loaded, its code is cached and reloading it would only make it *appear* as updated, while in reality still running the old code. Of course this only applies *once* we have loaded extensions. Before that, it's possible to download and install updates, and only then initialize extensions with their latest version. The trade-off is that network requests, data download and extraction may introduce a significant delay before extensions are enabled. Most extensions modify the UI one way or another, so that delay would likely be noticeable by the user. Assuming that users are usually happy enough with the current extension version, that trade-off doesn't seem worthwhile. However there is an exception: After a major version update, extensions are likely disabled as out-of-date, or at least more likely to break (when the version check is disabled). In that case delaying extension initialization to download and install updates looks like the better trade-off, so do that. Part-of: --- js/ui/extensionSystem.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js index 21b3b196f..3592e5b3a 100644 --- a/js/ui/extensionSystem.js +++ b/js/ui/extensionSystem.js @@ -27,6 +27,8 @@ export class ExtensionManager extends Signals.EventEmitter { this._initializationPromise = null; this._updateNotified = false; + this._updateInProgress = false; + this._updatedUUIDS = []; this._extensions = new Map(); this._unloadedExtensions = new Map(); @@ -316,6 +318,11 @@ export class ExtensionManager extends Signals.EventEmitter { } notifyExtensionUpdate(uuid) { + if (this._updateInProgress) { + this._updatedUUIDS.push(uuid); + return; + } + let extension = this.lookup(uuid); if (!extension) return; @@ -646,6 +653,30 @@ export class ExtensionManager extends Signals.EventEmitter { } } + async _handleMajorUpdate() { + const [majorVersion] = Config.PACKAGE_VERSION.split('.'); + const path = `${global.userdatadir}/update-check-${majorVersion}`; + const file = Gio.File.new_for_path(path); + + try { + if (!await file.touch_async()) + return; + } catch (e) { + logError(e); + } + + this._updateInProgress = true; + + await ExtensionDownloader.checkForUpdates(); + this._installExtensionUpdates(); + + this._updatedUUIDS.map(uuid => this.lookup(uuid)).forEach( + ext => this.reloadExtension(ext)); + this._updatedUUIDS = []; + + this._updateInProgress = false; + } + _installExtensionUpdates() { if (!this.updatesSupported) return; @@ -719,6 +750,10 @@ export class ExtensionManager extends Signals.EventEmitter { return extension; }).filter(extension => extension !== null); + // after updating to a new major version, + // update extensions before loading them + await this._handleMajorUpdate(); + for (const extension of extensionObjects) { // eslint-disable-next-line no-await-in-loop await this.loadExtension(extension);