diff --git a/package.json b/package.json index 1422323d7b0..5ca24d43a8d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.68.0", - "distro": "5e85c2493e771e21081d8f4188922225406ed06b", + "distro": "dec8abea6c1191c66ab4a551172264be30a76e26", "author": { "name": "Microsoft Corporation" }, @@ -229,4 +229,4 @@ "elliptic": "^6.5.3", "nwmatcher": "^1.4.4" } -} +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 81fa42981c2..a73af96a7c8 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -10,6 +10,7 @@ import { CancellationError, getErrorMessage } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { isBoolean } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { @@ -363,8 +364,9 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } - if (!!report.unsupportedPreReleaseExtensions && !!report.unsupportedPreReleaseExtensions[extension.identifier.id]) { - throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is no longer supported. It is now part of the '{1}' extension as a pre-release version.", extension.identifier.id, report.unsupportedPreReleaseExtensions[extension.identifier.id].displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); + const deprecated = report.deprecated ? report.deprecated[extension.identifier.id.toLowerCase()] : undefined; + if (deprecated && !isBoolean(deprecated)) { + throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is deprecated. Use '{1}' extension instead.", extension.identifier.id, deprecated.displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); } if (!await this.canInstall(extension)) { diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index eba50f040e4..b75a76752b8 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -535,11 +535,9 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller }; } -type PreReleaseMigrationInfo = { id: string; displayName: string; migrateStorage?: boolean; engine?: string }; interface IRawExtensionsControlManifest { malicious: string[]; - unsupported?: IStringDictionary; - migrateToPreRelease?: IStringDictionary; + deprecated?: IStringDictionary; } abstract class AbstractExtensionGalleryService implements IExtensionGalleryService { @@ -1150,30 +1148,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const result = await asJson(context); const malicious: IExtensionIdentifier[] = []; - const unsupportedPreReleaseExtensions: IStringDictionary<{ id: string; displayName: string; migrateStorage?: boolean }> = {}; - if (result) { for (const id of result.malicious) { malicious.push({ id }); } - if (result.unsupported) { - for (const extensionId of Object.keys(result.unsupported)) { - const value = result.unsupported[extensionId]; - if (!isBoolean(value)) { - unsupportedPreReleaseExtensions[extensionId.toLowerCase()] = value.preReleaseExtension; - } - } - } - if (result.migrateToPreRelease) { - for (const [unsupportedPreReleaseExtensionId, preReleaseExtensionInfo] of Object.entries(result.migrateToPreRelease)) { - if (!preReleaseExtensionInfo.engine || isEngineValid(preReleaseExtensionInfo.engine, this.productService.version, this.productService.date)) { - unsupportedPreReleaseExtensions[unsupportedPreReleaseExtensionId.toLowerCase()] = preReleaseExtensionInfo; - } - } - } } - return { malicious, unsupportedPreReleaseExtensions }; + return { malicious, deprecated: result?.deprecated }; } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index b28421b4bf1..061de6580a9 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -284,7 +284,7 @@ export const enum StatisticType { export interface IExtensionsControlManifest { malicious: IExtensionIdentifier[]; - unsupportedPreReleaseExtensions?: IStringDictionary<{ id: string; displayName: string; migrateStorage?: boolean }>; + deprecated?: IStringDictionary; } export const enum InstallOperation { diff --git a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts index 15ea0319efc..87eaf1447a6 100644 --- a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts +++ b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; +import { isBoolean } from 'vs/base/common/types'; import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; @@ -20,18 +21,22 @@ import { ILogService } from 'vs/platform/log/common/log'; export async function migrateUnsupportedExtensions(extensionManagementService: IExtensionManagementService, galleryService: IExtensionGalleryService, extensionStorageService: IExtensionStorageService, extensionEnablementService: IGlobalExtensionEnablementService, logService: ILogService): Promise { try { const extensionsControlManifest = await extensionManagementService.getExtensionsControlManifest(); - if (!extensionsControlManifest.unsupportedPreReleaseExtensions) { + if (!extensionsControlManifest.deprecated) { return; } const installed = await extensionManagementService.getInstalled(ExtensionType.User); - for (const [unsupportedExtensionId, { id: preReleaseExtensionId, migrateStorage }] of Object.entries(extensionsControlManifest.unsupportedPreReleaseExtensions)) { + for (const [unsupportedExtensionId, deprecated] of Object.entries(extensionsControlManifest.deprecated)) { + if (isBoolean(deprecated)) { + continue; + } + const { id: preReleaseExtensionId, migrateStorage, preRelease } = deprecated; const unsupportedExtension = installed.find(i => areSameExtensions(i.identifier, { id: unsupportedExtensionId })); // Unsupported Extension is not installed if (!unsupportedExtension) { continue; } - const gallery = (await galleryService.getExtensions([{ id: preReleaseExtensionId, preRelease: true }], { targetPlatform: await extensionManagementService.getTargetPlatform(), compatible: true }, CancellationToken.None))[0]; + const gallery = (await galleryService.getExtensions([{ id: preReleaseExtensionId, preRelease }], { targetPlatform: await extensionManagementService.getTargetPlatform(), compatible: true }, CancellationToken.None))[0]; if (!gallery) { logService.info(`Skipping migrating '${unsupportedExtension.identifier.id}' extension because, the comaptible target '${preReleaseExtensionId}' extension is not found`); continue; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index ba209ca06a9..2f1244e6ebd 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -658,7 +658,7 @@ export class ExtensionEditor extends EditorPane { } }; reset(template.recommendation); - if (extension.state === ExtensionState.Installed) { + if (extension.deprecated || extension.state === ExtensionState.Installed) { return; } updateRecommendationText(false); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 6e4013f7272..d3b1573086e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -250,6 +250,7 @@ export abstract class AbstractInstallAction extends ExtensionAction { @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @ILabelService private readonly labelService: ILabelService, + @IDialogService private readonly dialogService: IDialogService, ) { super(id, localize('install', "Install"), cssClass, false); this.update(); @@ -274,6 +275,19 @@ export abstract class AbstractInstallAction extends ExtensionAction { if (!this.extension) { return; } + + if (this.extension.deprecated && isBoolean(this.extension.deprecated)) { + const result = await this.dialogService.confirm({ + type: 'warning', + title: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), + message: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), + primaryButton: localize('install anyway', "Install Anyway"), + }); + if (!result.confirmed) { + return; + } + } + this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.installPreReleaseVersion }); alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName)); @@ -373,12 +387,13 @@ export class InstallAction extends AbstractInstallAction { @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, + @IDialogService dialogService: IDialogService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IWorkbenchExtensionManagementService private readonly workbenchExtensioManagementService: IWorkbenchExtensionManagementService, @IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super(`extensions.install`, installPreReleaseVersion, InstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); this.updateLabel(); this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this)); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, @@ -438,11 +453,12 @@ export class InstallAndSyncAction extends AbstractInstallAction { @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, + @IDialogService dialogService: IDialogService, @IProductService productService: IProductService, @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super('extensions.installAndSync', installPreReleaseVersion, AbstractInstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); this.tooltip = localize({ key: 'install everywhere tooltip', comment: ['Placeholder is the name of the product. Eg: Visual Studio Code or Visual Studio Code - Insiders'] }, "Install this extension in all your synced {0} instances", productService.nameLong); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, Event.filter(userDataSyncEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update())); @@ -2156,13 +2172,13 @@ export class ExtensionStatusAction extends ExtensionAction { return; } - if (this.extension.isUnsupported) { - if (isBoolean(this.extension.isUnsupported)) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported tooltip', "This extension no longer supported.")) }, true); + if (this.extension.deprecated) { + if (isBoolean(this.extension.deprecated)) { + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported tooltip', "This extension is no longer being maintained and is deprecated.")) }, true); } else { - const link = `[${this.extension.isUnsupported.preReleaseExtension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.isUnsupported.preReleaseExtension.id]))}`)})`; + const link = `[${this.extension.deprecated.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecated.id]))}`)})`; if (this.extension.state !== ExtensionState.Installed) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension is now part of the {0} extension as a pre-release version.", link)) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension has been deprecated. Use {0} instead.", link)) }, true); } } return; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 9a31eb974cc..4755c672581 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -188,7 +188,7 @@ export class Renderer implements IPagedRenderer { const updateEnablement = async () => { let isDisabled = false; if (extension.state === ExtensionState.Uninstalled) { - isDisabled = !(await this.extensionsWorkbenchService.canInstall(extension)); + isDisabled = !!extension.deprecated || !(await this.extensionsWorkbenchService.canInstall(extension)); } else if (extension.local && !isLanguagePackExtension(extension.local.manifest)) { const runningExtensions = await this.extensionService.getExtensions(); const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 9b38a120296..e4592383fe6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -185,7 +185,7 @@ export class RecommendationWidget extends ExtensionWidget { render(): void { this.clear(); - if (!this.extension || this.extension.state === ExtensionState.Installed) { + if (!this.extension || this.extension.state === ExtensionState.Installed || this.extension.deprecated) { return; } const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); @@ -549,6 +549,9 @@ export class ExtensionHoverWidget extends ExtensionWidget { if (extension.state === ExtensionState.Installed) { return undefined; } + if (extension.deprecated) { + return undefined; + } const recommendation = this.extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]; if (!recommendation?.reasonText) { return undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 8f96b5e7fcf..3aacb593829 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -188,7 +188,7 @@ export class Extension implements IExtension { } public isMalicious: boolean = false; - public isUnsupported: boolean | { preReleaseExtension: { id: string; displayName: string } } = false; + public deprecated: boolean | { id: string; displayName: string } = false; get installCount(): number | undefined { return this.gallery ? this.gallery.installCount : undefined; @@ -390,17 +390,9 @@ class Extensions extends Disposable { static updateExtensionFromControlManifest(extension: Extension, extensionsControlManifest: IExtensionsControlManifest): void { const isMalicious = extensionsControlManifest.malicious.some(identifier => areSameExtensions(extension.identifier, identifier)); - if (extension.isMalicious !== isMalicious) { - extension.isMalicious = isMalicious; - } - const unsupportedPreRelease = extensionsControlManifest.unsupportedPreReleaseExtensions ? extensionsControlManifest.unsupportedPreReleaseExtensions[extension.identifier.id.toLowerCase()] : undefined; - if (unsupportedPreRelease) { - if (isBoolean(extension.isUnsupported) || !areSameExtensions({ id: extension.isUnsupported.preReleaseExtension.id }, { id: unsupportedPreRelease.id })) { - extension.isUnsupported = { preReleaseExtension: unsupportedPreRelease }; - } - } else if (extension.isUnsupported) { - extension.isUnsupported = false; - } + extension.isMalicious = isMalicious; + const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()] : undefined; + extension.deprecated = deprecated ?? false; } private readonly _onChange: Emitter<{ extension: Extension; operation?: InstallOperation } | undefined> = this._register(new Emitter<{ extension: Extension; operation?: InstallOperation } | undefined>()); @@ -1155,7 +1147,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return false; } - if (extension.isUnsupported) { + if (extension.deprecated && !isBoolean(extension.deprecated)) { return false; } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index b3846d038fc..23744cd4f9b 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -78,7 +78,7 @@ export interface IExtension { readonly local?: ILocalExtension; gallery?: IGalleryExtension; readonly isMalicious: boolean; - readonly isUnsupported: boolean | { preReleaseExtension: { id: string; displayName: string } }; + readonly deprecated: boolean | { id: string; displayName: string }; } export const SERVICE_ID = 'extensionsWorkbenchService'; diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 8438e2f3377..aa1eb99bc0e 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -21,7 +21,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import * as semver from 'vs/base/common/semver/semver'; -import { isString } from 'vs/base/common/types'; +import { isBoolean, isString } from 'vs/base/common/types'; import { getErrorMessage } from 'vs/base/common/errors'; import { ResourceMap } from 'vs/base/common/map'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; @@ -140,10 +140,11 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten this.logService.info(`Checking additional builtin extensions: Ignoring '${extension.id}' because it is reported to be malicious.`); continue; } - if (extensionsControlManifest.unsupportedPreReleaseExtensions && extensionsControlManifest.unsupportedPreReleaseExtensions[extension.id.toLowerCase()]) { - const preReleaseExtensionId = extensionsControlManifest.unsupportedPreReleaseExtensions[extension.id.toLowerCase()].id; - this.logService.info(`Checking additional builtin extensions: '${extension.id}' is no longer supported, instead using '${preReleaseExtensionId}'`); - result.push({ id: preReleaseExtensionId, preRelease: true }); + const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.id.toLowerCase()] : undefined; + if (deprecated && !isBoolean(deprecated)) { + const preReleaseExtensionId = deprecated.id; + this.logService.info(`Checking additional builtin extensions: '${extension.id}' is deprecated, instead using '${preReleaseExtensionId}'`); + result.push({ id: preReleaseExtensionId, preRelease: !!deprecated.preRelease }); } else { result.push(extension); }