diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 2500040f7a0..0329c7b9d4a 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -352,7 +352,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl return allDependenciesAndPacks.filter(e => !installed.some(i => areSameExtensions(i.identifier, e.gallery.identifier))); } - private async checkAndGetCompatibleVersion(extension: IGalleryExtension, fetchCompatibleVersion: boolean, includePreRelease: boolean): Promise<{ extension: IGalleryExtension, manifest: IExtensionManifest }> { + private async checkAndGetCompatibleVersion(extension: IGalleryExtension, fetchCompatibleVersion: boolean, installPreRelease: boolean): Promise<{ extension: IGalleryExtension, manifest: IExtensionManifest }> { if (await this.isMalicious(extension)) { throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } @@ -362,10 +362,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform); } - const compatibleExtension = await this.getCompatibleVersion(extension, fetchCompatibleVersion, includePreRelease); + const compatibleExtension = await this.getCompatibleVersion(extension, fetchCompatibleVersion, installPreRelease); if (compatibleExtension) { - if (includePreRelease && !compatibleExtension.properties.isPreReleaseVersion && extension.hasPreReleaseVersion) { - throw new ExtensionManagementError(nls.localize('notFoundCompatiblePrereleaseDependency', "Can't install pre-release version of '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.Incompatible); + if (installPreRelease && extension.hasPreReleaseVersion && !compatibleExtension.properties.isPreReleaseVersion) { + throw new ExtensionManagementError(nls.localize('notFoundCompatiblePrereleaseDependency', "Can't install pre-release version of '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.IncompatiblePreRelease); } } else { throw new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.Incompatible); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 5254a77cd81..556150baccf 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -365,6 +365,7 @@ export enum ExtensionManagementErrorCode { Unsupported = 'Unsupported', Malicious = 'Malicious', Incompatible = 'Incompatible', + IncompatiblePreRelease = 'IncompatiblePreRelease', IncompatibleTargetPlatform = 'IncompatibleTargetPlatform', Invalid = 'Invalid', Download = 'Download', diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index eabe5968200..86563619e8e 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -419,7 +419,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } } catch (error) { addToSkipped.push(e); - if (error instanceof ExtensionManagementError && (error.code === ExtensionManagementErrorCode.Incompatible || error.code === ExtensionManagementErrorCode.IncompatibleTargetPlatform)) { + if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) { this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id); } else { this.logService.error(error); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index bc284327540..5dbf7c83eaa 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -612,7 +612,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi try { await this.extensionsWorkbenchService.install(extension); } catch (err) { - runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, err)); + runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, undefined, err)); } })); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 93a88c70587..d2c027bbe8c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -105,6 +105,7 @@ export class PromptExtensionInstallFailureAction extends Action { private readonly extension: IExtension, private readonly version: string, private readonly installOperation: InstallOperation, + private readonly installOptions: InstallOptions | undefined, private readonly error: Error, @IProductService private readonly productService: IProductService, @IOpenerService private readonly openerService: IOpenerService, @@ -113,6 +114,7 @@ export class PromptExtensionInstallFailureAction extends Action { @ICommandService private readonly commandService: ICommandService, @ILogService private readonly logService: ILogService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super('extension.promptExtensionInstallFailure'); } @@ -139,8 +141,26 @@ export class PromptExtensionInstallFailureAction extends Action { return; } + let operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id) + : localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id); + let additionalMessage; const promptChoices: IPromptChoice[] = []; - if (this.extension.gallery && this.productService.extensionsGallery && (this.extensionManagementServerService.localExtensionManagementServer || this.extensionManagementServerService.remoteExtensionManagementServer) && !isIOS) { + + if (ExtensionManagementErrorCode.IncompatiblePreRelease === (this.error.name)) { + operationMessage = getErrorMessage(this.error); + additionalMessage = localize('install release version', "Would you like to install the released version?"); + promptChoices.push({ + label: localize('install released version', "Install Released Version"), + run: () => { + const installAction = this.installOptions?.isMachineScoped ? this.instantiationService.createInstance(InstallAction, !!this.installOptions.installPreReleaseVersion) : this.instantiationService.createInstance(InstallAndSyncAction, !!this.installOptions?.installPreReleaseVersion); + installAction.extension = this.extension; + return installAction.run(); + } + }); + } + + else if (this.extension.gallery && this.productService.extensionsGallery && (this.extensionManagementServerService.localExtensionManagementServer || this.extensionManagementServerService.remoteExtensionManagementServer) && !isIOS) { + additionalMessage = localize('check logs', "Please check the [log]({0}) for more details.", `command:${Constants.showWindowLogActionId}`); promptChoices.push({ label: localize('download', "Try Downloading Manually..."), run: () => this.openerService.open(URI.parse(`${this.productService.extensionsGallery!.serviceUrl}/publishers/${this.extension.publisher}/vsextensions/${this.extension.name}/${this.version}/vspackage`)).then(() => { @@ -156,10 +176,8 @@ export class PromptExtensionInstallFailureAction extends Action { }); } - const operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id) - : localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id); - const checkLogsMessage = localize('check logs', "Please check the [log]({0}) for more details.", `command:${Constants.showWindowLogActionId}`); - this.notificationService.prompt(Severity.Error, `${operationMessage} ${checkLogsMessage}`, promptChoices); + let message = `${operationMessage}${additionalMessage ? ` ${additionalMessage}` : ''}`; + this.notificationService.prompt(Severity.Error, message, promptChoices); } } @@ -310,10 +328,11 @@ export abstract class AbstractInstallAction extends ExtensionAction { } private async install(extension: IExtension): Promise { + const installOptions = this.getInstallOptions(); try { - return await this.extensionsWorkbenchService.install(extension, this.getInstallOptions()); + return await this.extensionsWorkbenchService.install(extension, installOptions); } catch (error) { - await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Install, error).run(); + await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Install, installOptions, error).run(); return undefined; } } @@ -761,7 +780,7 @@ export class UpdateAction extends ExtensionAction { await this.extensionsWorkbenchService.install(extension); alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion)); } catch (err) { - this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, err).run(); + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, undefined, err).run(); } } @@ -1124,7 +1143,7 @@ export class InstallAnotherVersionAction extends ExtensionAction { await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); } } catch (error) { - this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, pick.latest ? this.extension!.latestVersion : pick.id, InstallOperation.Install, error).run(); + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, pick.latest ? this.extension!.latestVersion : pick.id, InstallOperation.Install, undefined, error).run(); } } return null; @@ -1715,7 +1734,7 @@ export class InstallRecommendedExtensionAction extends Action { try { await this.extensionWorkbenchService.install(extension); } catch (err) { - this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Install, err).run(); + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Install, undefined, err).run(); } } } diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index 03e8a8ba247..3eba7863d31 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -103,7 +103,7 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC const compatible = await this.galleryService.getCompatibleExtension(extension, includePreRelease, await this.getTargetPlatform()); if (compatible) { if (includePreRelease && !compatible.properties.isPreReleaseVersion && extension.hasPreReleaseVersion) { - throw new ExtensionManagementError(localize('notFoundCompatiblePrereleaseDependency', "Can't install pre-release version of '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.Incompatible); + throw new ExtensionManagementError(localize('notFoundCompatiblePrereleaseDependency', "Can't install pre-release version of '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.IncompatiblePreRelease); } } else { throw new ExtensionManagementError(localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.Incompatible);