Skip to content

Commit 8af658b

Browse files
authored
Warn about installed extensions that are no longer available in Marketplace (microsoft#248852)
* Display warning for unpublished gallery extensions ref: microsoft#181248 * naming is hard * tidy up * update variable * detect missing extension * add a filter to 'syncMissingFromGallery' so we can compare against subsets of a galleryExtension * move into syncInstalledExtensionsWithGallery
1 parent 268f1f1 commit 8af658b

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

src/vs/workbench/contrib/extensions/browser/extensionsActions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,11 @@ export class ExtensionStatusAction extends ExtensionAction {
26082608
return;
26092609
}
26102610

2611+
if (this.extension.missingFromGallery) {
2612+
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('missing from gallery tooltip', "This extension is no longer available on the Extension Marketplace.")) }, true);
2613+
return;
2614+
}
2615+
26112616
if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
26122617
return;
26132618
}

src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export class Extension implements IExtension {
9595

9696
private galleryResourcesCache = new Map<string, any>();
9797

98+
private _missingFromGallery: boolean | undefined;
99+
98100
constructor(
99101
private stateProvider: IExtensionStateProvider<ExtensionState>,
100102
private runtimeStateProvider: IExtensionStateProvider<ExtensionRuntimeState | undefined>,
@@ -136,6 +138,14 @@ export class Extension implements IExtension {
136138
this.galleryResourcesCache.clear();
137139
}
138140

141+
get missingFromGallery(): boolean {
142+
return !!this._missingFromGallery;
143+
}
144+
145+
set missingFromGallery(missing: boolean) {
146+
this._missingFromGallery = missing;
147+
}
148+
139149
get type(): ExtensionType {
140150
return this.local ? this.local.type : ExtensionType.User;
141151
}
@@ -657,7 +667,7 @@ class Extensions extends Disposable {
657667
return this.local;
658668
}
659669

660-
async syncInstalledExtensionsWithGallery(galleryExtensions: IGalleryExtension[], productVersion: IProductVersion): Promise<void> {
670+
async syncInstalledExtensionsWithGallery(galleryExtensions: IGalleryExtension[], productVersion: IProductVersion, flagExtensionsMissingFromGallery?: IExtensionInfo[]): Promise<void> {
661671
const extensions = await this.mapInstalledExtensionWithCompatibleGalleryExtension(galleryExtensions, productVersion);
662672
for (const [extension, gallery] of extensions) {
663673
// update metadata of the extension if it does not exist
@@ -669,6 +679,23 @@ class Extensions extends Disposable {
669679
this._onChange.fire({ extension });
670680
}
671681
}
682+
// Detect extensions that do not have a corresponding gallery entry.
683+
// This indicates that it was likely removed from the gallery
684+
if (flagExtensionsMissingFromGallery) {
685+
for (const extension of this.local) {
686+
if (!extension.identifier.uuid) {
687+
continue;
688+
}
689+
if (!flagExtensionsMissingFromGallery.some(f => areSameExtensions(f, extension.identifier))) {
690+
continue;
691+
}
692+
const gallery = galleryExtensions.find(g => areSameExtensions(g.identifier, extension.identifier));
693+
if (!gallery) {
694+
extension.missingFromGallery = true;
695+
this._onChange.fire({ extension });
696+
}
697+
}
698+
}
672699
}
673700

674701
private async mapInstalledExtensionWithCompatibleGalleryExtension(galleryExtensions: IGalleryExtension[], productVersion: IProductVersion): Promise<[Extension, IGalleryExtension][]> {
@@ -1874,7 +1901,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
18741901
this.logService.trace(`Checking updates for extensions`, infos.map(e => e.id).join(', '));
18751902
const galleryExtensions = await this.galleryService.getExtensions(infos, { targetPlatform, compatible: true, productVersion: this.getProductVersion(), updateCheck: true }, CancellationToken.None);
18761903
if (galleryExtensions.length) {
1877-
await this.syncInstalledExtensionsWithGallery(galleryExtensions);
1904+
await this.syncInstalledExtensionsWithGallery(galleryExtensions, infos);
18781905
}
18791906
}
18801907
}
@@ -1981,7 +2008,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
19812008
return pick?.id;
19822009
}
19832010

1984-
private async syncInstalledExtensionsWithGallery(gallery: IGalleryExtension[]): Promise<void> {
2011+
private async syncInstalledExtensionsWithGallery(gallery: IGalleryExtension[], flagExtensionsMissingFromGallery?: IExtensionInfo[]): Promise<void> {
19852012
const extensions: Extensions[] = [];
19862013
if (this.localExtensions) {
19872014
extensions.push(this.localExtensions);
@@ -1995,7 +2022,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
19952022
if (!extensions.length) {
19962023
return;
19972024
}
1998-
await Promise.allSettled(extensions.map(extensions => extensions.syncInstalledExtensionsWithGallery(gallery, this.getProductVersion())));
2025+
await Promise.allSettled(extensions.map(extensions => extensions.syncInstalledExtensionsWithGallery(gallery, this.getProductVersion(), flagExtensionsMissingFromGallery)));
19992026
if (this.outdated.length) {
20002027
this.logService.info(`Auto updating outdated extensions.`, this.outdated.map(e => e.identifier.id).join(', '));
20012028
this.eventuallyAutoUpdateExtensions();

src/vs/workbench/contrib/extensions/common/extensions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export interface IExtension {
107107
readonly isMalicious: boolean | undefined;
108108
readonly maliciousInfoLink: string | undefined;
109109
readonly deprecationInfo?: IDeprecationInfo;
110+
readonly missingFromGallery?: boolean;
110111
}
111112

112113
export const IExtensionsWorkbenchService = createDecorator<IExtensionsWorkbenchService>('extensionsWorkbenchService');

0 commit comments

Comments
 (0)