| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 | diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.tsindex 199f433..a6cbb10 100644--- a/src/vs/platform/update/common/update.ts+++ b/src/vs/platform/update/common/update.ts@@ -51,3 +51,4 @@ export const enum UpdateType { 	Archive,-	Snap+	Snap,+	WindowsInstaller, }@@ -110 +111,38 @@ export interface IUpdateService { }++export type Architecture =+	| "arm"+	| "arm64"+	| "ia32"+	| "loong64"+	| "mips"+	| "mipsel"+	| "ppc"+	| "ppc64"+	| "riscv64"+	| "s390"+	| "s390x"+	| "x64";++export type Platform =+	| "aix"+	| "android"+	| "darwin"+	| "freebsd"+	| "haiku"+	| "linux"+	| "openbsd"+	| "sunos"+	| "win32"+	| "cygwin"+	| "netbsd";++export type Quality =+	| "insider"+	| "stable";++export type Target =+	| "archive"+	| "msi"+	| "system"+	| "user";\ No newline at end of filediff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.tsindex 48d0d86..840b9d3 100644--- a/src/vs/platform/update/electron-main/abstractUpdateService.ts+++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts@@ -14,6 +14,10 @@ import { IProductService } from '../../product/common/productService.js'; import { IRequestService } from '../../request/common/request.js';-import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js';+import { Architecture, AvailableForDownload, DisablementReason, IUpdateService, Platform, State, StateType, Target, UpdateType } from '../common/update.js'; -export function createUpdateURL(platform: string, quality: string, productService: IProductService): string {-	return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`;+export function createUpdateURL(productService: IProductService, quality: string, platform: Platform, architecture: Architecture, target?: Target): string {+	if (target) {+		return `${productService.updateUrl}/${quality}/${platform}/${architecture}/${target}/latest.json`;+	} else {+		return `${productService.updateUrl}/${quality}/${platform}/${architecture}/latest.json`;+	} }diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.tsindex b78ebc5..a4a3b1d 100644--- a/src/vs/platform/update/electron-main/updateService.darwin.ts+++ b/src/vs/platform/update/electron-main/updateService.darwin.ts@@ -15,3 +15,3 @@ import { ILogService } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js';-import { IRequestService } from '../../request/common/request.js';+import { IRequestService, asJson } from '../../request/common/request.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js';@@ -19,2 +19,4 @@ import { IUpdate, State, StateType, UpdateType } from '../common/update.js'; import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js';+import { CancellationToken } from '../../../base/common/cancellation.js';+import * as semver from 'semver'; @@ -76,17 +78,3 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau 	protected buildUpdateFeedUrl(quality: string): string | undefined {-		let assetID: string;-		if (!this.productService.darwinUniversalAssetId) {-			assetID = process.arch === 'x64' ? 'darwin' : 'darwin-arm64';-		} else {-			assetID = this.productService.darwinUniversalAssetId;-		}-		const url = createUpdateURL(assetID, quality, this.productService);-		try {-			electron.autoUpdater.setFeedURL({ url });-		} catch (e) {-			// application is very likely not signed-			this.logService.error('Failed to set update feed URL', e);-			return undefined;-		}-		return url;+		return createUpdateURL(this.productService, quality, process.platform, process.arch); 	}@@ -100,5 +88,30 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau -		const url = explicit ? this.url : `${this.url}?bg=true`;-		electron.autoUpdater.setFeedURL({ url });-		electron.autoUpdater.checkForUpdates();+		this.requestService.request({ url: this.url }, CancellationToken.None)+			.then<IUpdate | null>(asJson)+			.then(update => {+				if (!update || !update.url || !update.version || !update.productVersion) {+					this.setState(State.Idle(UpdateType.Setup));++					return Promise.resolve(null);+				}++				const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')+				const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')++				if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) {+					this.setState(State.Idle(UpdateType.Setup));+				}+				else {+					electron.autoUpdater.setFeedURL({ url: this.url! });+					electron.autoUpdater.checkForUpdates();+				}++				return Promise.resolve(null);+			})+			.then(undefined, err => {+				this.logService.error(err);+				// only show message when explicitly checking for updates+				const message: string | undefined = explicit ? (err.message || err) : undefined;+				this.setState(State.Idle(UpdateType.Setup, message));+			}); 	}diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.tsindex 8550ace..c2fddcb 100644--- a/src/vs/platform/update/electron-main/updateService.linux.ts+++ b/src/vs/platform/update/electron-main/updateService.linux.ts@@ -15,2 +15,3 @@ import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/upda import { AbstractUpdateService, createUpdateURL } from './abstractUpdateService.js';+import * as semver from 'semver'; @@ -31,3 +32,3 @@ export class LinuxUpdateService extends AbstractUpdateService { 	protected buildUpdateFeedUrl(quality: string): string {-		return createUpdateURL(`linux-${process.arch}`, quality, this.productService);+		return createUpdateURL(this.productService, quality, process.platform, process.arch); 	}@@ -39,6 +40,5 @@ export class LinuxUpdateService extends AbstractUpdateService { -		const url = explicit ? this.url : `${this.url}?bg=true`; 		this.setState(State.CheckingForUpdates(explicit)); -		this.requestService.request({ url }, CancellationToken.None)+		this.requestService.request({ url: this.url }, CancellationToken.None) 			.then<IUpdate | null>(asJson)@@ -47,5 +47,17 @@ export class LinuxUpdateService extends AbstractUpdateService { 					this.setState(State.Idle(UpdateType.Archive));-				} else {++					return Promise.resolve(null);+				}++				const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')+				const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')++				if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) {+					this.setState(State.Idle(UpdateType.Archive));+				}+				else { 					this.setState(State.AvailableForDownload(update)); 				}++				return Promise.resolve(null); 			})diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.tsindex 8f92a3e..020e690 100644--- a/src/vs/platform/update/electron-main/updateService.win32.ts+++ b/src/vs/platform/update/electron-main/updateService.win32.ts@@ -11,3 +11,2 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { memoize } from '../../../base/common/decorators.js';-import { hash } from '../../../base/common/hash.js'; import * as path from '../../../base/common/path.js';@@ -25,4 +24,5 @@ import { asJson, IRequestService } from '../../request/common/request.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js';-import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, UpdateType } from '../common/update.js';-import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js';+import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, Target, UpdateType } from '../common/update.js';+import { AbstractUpdateService, createUpdateURL} from './abstractUpdateService.js';+import * as semver from 'semver'; @@ -42,5 +42,9 @@ function getUpdateType(): UpdateType { 	if (typeof _updateType === 'undefined') {-		_updateType = fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))-			? UpdateType.Setup-			: UpdateType.Archive;+		if (fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))) {+			_updateType = UpdateType.Setup;+		} else if (path.basename(path.normalize(path.join(process.execPath, '..', '..'))) === 'Program Files') {+			_updateType = UpdateType.WindowsInstaller;+		} else {+			_updateType = UpdateType.Archive;+		} 	}@@ -63,2 +67,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun 		@IConfigurationService configurationService: IConfigurationService,+		// @ts-expect-error 		@ITelemetryService private readonly telemetryService: ITelemetryService,@@ -102,11 +107,21 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun 	protected buildUpdateFeedUrl(quality: string): string | undefined {-		let platform = `win32-${process.arch}`;--		if (getUpdateType() === UpdateType.Archive) {-			platform += '-archive';-		} else if (this.productService.target === 'user') {-			platform += '-user';+		let target: Target;++		switch (getUpdateType()) {+			case UpdateType.Archive:+				target = "archive"+				break;+			case UpdateType.WindowsInstaller:+				target = "msi"+				break;+			default:+				if (this.productService.target === 'user') {+					target = "user"+				}+				else {+					target = "system"+				} 		} -		return createUpdateURL(platform, quality, this.productService);+		return createUpdateURL(this.productService, quality, process.platform, process.arch, target); 	}@@ -131,2 +146,10 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun +				const fetchedVersion = /\d+\.\d+\.\d+\.\d+/.test(update.productVersion) ? update.productVersion.replace(/(\d+\.\d+\.\d+)\.\d+(\-\w+)?/, '$1$2') : update.productVersion.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')+				const currentVersion = this.productService.version.replace(/(\d+\.\d+\.)0+(\d+)(\-\w+)?/, '$1$2$3')++				if(semver.compareBuild(currentVersion, fetchedVersion) >= 0) {+					this.setState(State.Idle(updateType));+					return Promise.resolve(null);+				}+ 				if (updateType === UpdateType.Archive) {@@ -157,3 +180,3 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun -						const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');+						const fastUpdatesEnabled = getUpdateType() == UpdateType.Setup && this.configurationService.getValue('update.enableWindowsBackgroundUpdates'); 						if (fastUpdatesEnabled) {@@ -169,3 +192,2 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun 			.then(undefined, err => {-				this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) }); 				this.logService.error(err);@@ -253,6 +275,14 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun 		} else {-			spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {-				detached: true,-				stdio: ['ignore', 'ignore', 'ignore']-			});+			const type = getUpdateType();+			if (type == UpdateType.WindowsInstaller) {+				spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], {+					detached: true,+					stdio: ['ignore', 'ignore', 'ignore']+				});+			} else {+				spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {+					detached: true,+					stdio: ['ignore', 'ignore', 'ignore']+				});+			} 		}
 |