8000 Minimal version deprecation change by igorkofman · Pull Request #252293 · microsoft/vscode · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Minimal version deprecation change #252293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ export interface IDeprecationInfo {
};
readonly settings?: readonly string[];
readonly additionalInfo?: string;
readonly deprecatedVersion?: string;
}

export interface ISearchPrefferedResults {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { areSameExtensions, getExtensionId } from './extensionManagementUtil.js'
import { IExtensionStorageService } from './extensionStorage.js';
import { ExtensionType } from '../../extensions/common/extensions.js';
import { ILogService } from '../../log/common/log.js';
import * as semver from '../../../base/common/semver/semver.js';

/**
* Migrates the installed unsupported nightly extension to a supported pre-release extension. It includes following:
Expand Down Expand Up @@ -38,6 +39,13 @@ export async function migrateUnsupportedExtensions(extensionManagementService: I
continue;
}

// Check if this is a version-specific deprecation
if (deprecated.deprecatedVersion) {
if (!semver.lte(unsupportedExtension.manifest.version, deprecated.deprecatedVersion)) {
continue;
}
}

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`);
Expand All @@ -53,7 +61,7 @@ export async function migrateUnsupportedExtensions(extensionManagementService: I

let preReleaseExtension = installed.find(i => areSameExtensions(i.identifier, { id: preReleaseExtensionId }));
if (!preReleaseExtension || (!preReleaseExtension.isPreReleaseVersion && isUnsupportedExtensionEnabled)) {
preReleaseExtension = await extensionManagementService.installFromGallery(gallery, { installPreReleaseVersion: true, isMachineScoped: unsupportedExtension.isMachineScoped, operation: InstallOperation.Migrate, context: { [EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT]: true } });
preReleaseExtension = await extensionManagementService.installFromGallery(gallery, { installPreReleaseVersion: preRelease, isMachineScoped: unsupportedExtension.isMachineScoped, operation: InstallOperation.Migrate, context: { [EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT]: true } });
Copy link
Author
@igorkofman igorkofman Jun 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is potentially a bug in the existing implementation as it results in a "pre-release" badge on the extension. Ok skipping this but seems correct to include.

logService.info(`Installed the pre-release extension '${preReleaseExtension.identifier.id}'`);
if (!isUnsupportedExtensionEnabled) {
await extensionEnablementService.disableExtension(preReleaseExtension.identifier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import { ByteSize, IFileService } from '../../../../platform/files/common/files.
import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
import { IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';

function toDateString(date: Date) {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}, ${date.toLocaleTimeString(language, { hourCycle: 'h23' })}`;
Expand Down Expand Up @@ -544,7 +545,7 @@ export class ExtensionEditor extends EditorPane {

template.name.textContent = extension.displayName;
template.name.classList.toggle('clickable', !!extension.url);
template.name.classList.toggle('deprecated', !!extension.deprecationInfo);
template.name.classList.toggle('deprecated', isExtensionDeprecated(extension));
template.preview.style.display = extension.preview ? 'inherit' : 'none';
template.builtin.style.display = extension.isBuiltin ? 'inherit' : 'none';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as json from '../../../../base/common/json.js';
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
import { disposeIfDisposable } from '../../../../base/common/lifecycle.js';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP, ExtensionEditorTab, ExtensionRuntimeActionType, IExtensionArg, AutoUpdateConfigurationKey } from '../common/extensions.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';
import { ExtensionsConfigurationInitialContent } from '../common/extensionsFileTemplate.js';
import { IGalleryExtension, IExtensionGalleryService, ILocalExtension, InstallOptions, InstallOperation, ExtensionManagementErrorCode, IAllowedExtensionsService, shouldRequireRepositorySignatureFor } from '../../../../platform/extensionManagement/common/extensionManagement.js';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js';
Expand Down Expand Up @@ -523,7 +524,7 @@ export class InstallAction extends ExtensionAction {
}
}

if (this.extension.deprecationInfo) {
if (this.extension.deprecationInfo && isExtensionDeprecated(this.extension)) {
let detail: string | MarkdownString = localize('deprecated message', "This extension is deprecated as it is no longer being maintained.");
enum DeprecationChoice {
InstallAnyway = 0,
Expand Down Expand Up @@ -1293,7 +1294,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n
cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]);
cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]);
cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]);
cksOverlay.push(['extensionDisallowInstall', extension.isMalicious || extension.deprecationInfo?.disallowInstall]);
cksOverlay.push(['extensionDisallowInstall', extension.isMalicious || (extension.deprecationInfo?.disallowInstall && isExtensionDeprecated(extension))]);
cksOverlay.push(['isExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName }) === true]);
cksOverlay.push(['isPreReleaseExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName, prerelease: true }) === true]);
cksOverlay.push(['extensionIsUnsigned', extension.gallery && !extension.gallery.isSigned]);
Expand Down Expand Up @@ -2591,7 +2592,7 @@ export class ExtensionStatusAction extends ExtensionAction {
return;
}

if (this.extension.deprecationInfo) {
if (this.extension.deprecationInfo && isExtensionDeprecated(this.extension)) {
if (this.extension.deprecationInfo.extension) {
const link = `[${this.extension.deprecationInfo.extension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.extension.id]))}`)})`;
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension is deprecated. Use the {0} extension instead.", link)) }, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { IPagedRenderer } from '../../../../base/browser/ui/list/listPaging.js';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService, IExtensionsViewState } from '../common/extensions.js';
import { ManageExtensionAction, ExtensionRuntimeStateAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ButtonWithDropDownExtensionAction, InstallDropdownAction, InstallingLabelAction, ButtonWithDropdownExtensionActionViewItem, DropDownExtensionAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction } from './extensionsActions.js';
import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionRuntimeStatusWidget, PreReleaseBookmarkWidget, PublisherWidget, ExtensionKindIndicatorWidget, ExtensionIconWidget } from './extensionsWidgets.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js';
Expand Down Expand Up @@ -183,7 +184,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {

const updateEnablement = () => {
const disabled = extension.state === ExtensionState.Installed && !!extension.local && !this.extensionEnablementService.isEnabled(extension.local);
const deprecated = !!extension.deprecationInfo;
const deprecated = isExtensionDeprecated(extension);
data.element.classList.toggle('deprecated', deprecated);
data.root.classList.toggle('disabled', disabled);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ import { INotificationService } from '../../../../platform/notification/common/n
import { getLocationBasedViewColors } from '../../../browser/parts/views/viewPane.js';
import { DelayedPagedModel, IPagedModel } from '../../../../base/common/paging.js';
import { ExtensionIconWidget } from './extensionsWidgets.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';

function getAriaLabelForExtension(extension: IExtension | null): string {
if (!extension) {
return '';
}
const publisher = extension.publisherDomain?.verified ? localize('extension.arialabel.verifiedPublisher', "Verified Publisher {0}", extension.publisherDisplayName) : localize('extension.arialabel.publisher', "Publisher {0}", extension.publisherDisplayName);
const deprecated = extension?.deprecationInfo ? localize('extension.arialabel.deprecated', "Deprecated") : '';
const deprecated = isExtensionDeprecated(extension) ? localize('extension.arialabel.deprecated', "Deprecated") : '';
const rating = extension?.rating ? localize('extension.arialabel.rating', "Rated {0} out of 5 stars by {1} users", extension.rating.toFixed(2), extension.ratingCount) : '';
return `${extension.displayName}, ${deprecated ? `${deprecated}, ` : ''}${extension.version}, ${publisher}, ${extension.description} ${rating ? `, ${rating}` : ''}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as platform from '../../../../base/common/platform.js';
import { localize } from '../../../../nls.js';
import { IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js';
import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';
import { ILabelService } from '../../../../platform/label/common/label.js';
import { extensionButtonProminentBackground, ExtensionStatusAction } from './extensionsActions.js';
import { IThemeService, registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
Expand Down Expand Up @@ -433,7 +434,7 @@ export class RecommendationWidget extends ExtensionWidget {

render(): void {
this.clear();
if (!this.extension || this.extension.state === ExtensionState.Installed || this.extension.deprecationInfo) {
if (!this.extension || this.extension.state === ExtensionState.Installed || isExtensionDeprecated(this.extension)) {
return;
}
const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason();
Expand Down Expand Up @@ -954,7 +955,7 @@ export class ExtensionHoverWidget extends ExtensionWidget {
if (extension.state === ExtensionState.Installed) {
return undefined;
}
if (extension.deprecationInfo) {
if (isExtensionDeprecated(extension)) {
return undefined;
}
const recommendation = this.extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()];
Expand Down Expand Up @@ -1062,7 +1063,7 @@ export class ExtensionRecommendationWidget extends ExtensionWidget {

private getRecommendationStatus(): { icon: ThemeIcon | undefined; message: string } | undefined {
if (!this.extension
|| this.extension.deprecationInfo
|| isExtensionDeprecated(this.extension)
|| this.extension.state === ExtensionState.Installed
) {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common
import { IHostService } from '../../../services/host/browser/host.js';
import { URI } from '../../../../base/common/uri.js';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, HasOutdatedExtensionsContext, AutoUpdateConfigurationValue, InstallExtensionOptions, ExtensionRuntimeState, ExtensionRuntimeActionType, AutoRestartConfigurationKey, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionsNotification } from '../common/extensions.js';
import { isExtensionDeprecated } from '../common/extensionDeprecation.js';
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js';
import { IURLService, IURLHandler, IOpenURLOptions } from '../../../../platform/url/common/url.js';
import { ExtensionsInput, IExtensionEditorOptions } from '../common/extensionsInput.js';
Expand Down Expand Up @@ -1485,7 +1486,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
}

const deprecatedExtensions = this.local.filter(e => !!e.deprecationInfo && e.local && this.extensionEnablementService.isEnabled(e.local));
const deprecatedExtensions = this.local.filter(e => isExtensionDeprecated(e) && e.local && this.extensionEnablementService.isEnabled(e.local));
if (deprecatedExtensions.length) {
computedNotificiations.push({
message: nls.localize('deprecated extensions', "Deprecated extensions detected. Review them and migrate to alternatives."),
Expand Down
35 changes: 35 additions & 0 deletions src/vs/workbench/contrib/extensions/common/extensionDeprecation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IExtension } from './extensions.js';
import * as semver from '../../../../base/common/semver/semver.js';

/**
* This is the single source of truth for all deprecation UI decisions.
*
* Returns true only if:
* 1. Extension has deprecation info, AND
* 2. Current version is less than or equal to the deprecated version (or no version specified)
*/
export function isExtensionDeprecated(extension: IExtension): boolean {
if (!extension.deprecationInfo) {
return false;
}

// If no deprecated version specified, treat as deprecated (backward compatibility)
const deprecatedVersion = extension.deprecationInfo.deprecatedVersion;
if (!deprecatedVersion) {
return true;
}

// Get current version from local extension or gallery
const currentVersion = extension.local?.manifest.version || extension.version;
if (!currentVersion) {
return true; // If we can't determine version, assume deprecated for safety
}

// Check if current version is less than or equal to deprecated version
return semver.lte(currentVersion, deprecatedVersion);
}
0