@codingame/monaco-vscode-extension-gallery-service-override 1.83.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensionGallery.d.ts +12 -0
- package/extensionGallery.js +76 -0
- package/external/rollup-plugin-styles/dist/runtime/inject-css.js +3 -0
- package/external/tslib/tslib.es6.js +11 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/override/vs/platform/dialogs/common/dialogs.js +8 -0
- package/package.json +30 -0
- package/vscode/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.js +784 -0
- package/vscode/src/vs/platform/extensionManagement/common/extensionEnablementService.js +142 -0
- package/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService.js +855 -0
- package/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc.js +119 -0
- package/vscode/src/vs/platform/extensionManagement/common/extensionTipsService.js +57 -0
- package/vscode/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.js +58 -0
- package/vscode/src/vs/platform/externalServices/common/marketplace.js +17 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.js +492 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.js +28 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.js +78 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker.js +87 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.js +60 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionEditor.js +1868 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.js +52 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.js +437 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.js +17 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.js +221 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensions.contribution.js +2063 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.js +13 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActivationProgress.js +46 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.js +65 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.js +98 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList.js +224 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess.js +121 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer.js +285 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.js +912 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews.js +1270 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.js +824 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.js +1730 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.js +324 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.js +28 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/languageRecommendations.js +28 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/media/extension.css.js +6 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css.js +6 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css.js +6 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css.js +6 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css.js +6 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/remoteRecommendations.js +29 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/unsupportedExtensionsMigrationContribution.js +26 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/webRecommendations.js +38 -0
- package/vscode/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.js +88 -0
- package/vscode/src/vs/workbench/contrib/extensions/common/extensionQuery.js +67 -0
- package/vscode/src/vs/workbench/contrib/extensions/common/extensionsInput.js +43 -0
- package/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils.js +104 -0
- package/vscode/src/vs/workbench/contrib/extensions/common/reportExtensionIssueAction.js +31 -0
- package/vscode/src/vs/workbench/contrib/extensions/common/runtimeExtensionsInput.js +41 -0
- package/vscode/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.js +207 -0
- package/vscode/src/vs/workbench/contrib/update/common/update.js +3 -0
- package/vscode/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.js +84 -0
- package/vscode/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.js +634 -0
- package/vscode/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.js +883 -0
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.js +109 -0
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.js +62 -0
- package/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementService.js +633 -0
- package/vscode/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.js +58 -0
- package/vscode/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.js +269 -0
- package/vscode/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.js +83 -0
- package/vscode/src/vs/workbench/services/remote/common/remoteExtensionsScanner.js +73 -0
package/vscode/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.js
ADDED
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
import { __decorate, __param } from '../../../../../../external/tslib/tslib.es6.js';
|
|
2
|
+
import { distinct, isNonEmptyArray } from 'monaco-editor/esm/vs/base/common/arrays.js';
|
|
3
|
+
import { Barrier, createCancelablePromise } from 'monaco-editor/esm/vs/base/common/async.js';
|
|
4
|
+
import { CancellationToken } from 'monaco-editor/esm/vs/base/common/cancellation.js';
|
|
5
|
+
import { getErrorMessage, CancellationError } from 'monaco-editor/esm/vs/base/common/errors.js';
|
|
6
|
+
import { Emitter, Event } from 'monaco-editor/esm/vs/base/common/event.js';
|
|
7
|
+
import { Disposable, toDisposable } from 'monaco-editor/esm/vs/base/common/lifecycle.js';
|
|
8
|
+
import { isWeb } from 'monaco-editor/esm/vs/base/common/platform.js';
|
|
9
|
+
import { isDefined } from 'monaco-editor/esm/vs/base/common/types.js';
|
|
10
|
+
import { URI } from 'monaco-editor/esm/vs/base/common/uri.js';
|
|
11
|
+
import * as nls from 'monaco-editor/esm/vs/nls.js';
|
|
12
|
+
import { isTargetPlatformCompatible, ExtensionManagementError, ExtensionManagementErrorCode, EXTENSION_INSTALL_DEP_PACK_CONTEXT, TargetPlatformToString, IExtensionGalleryService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagement';
|
|
13
|
+
import { areSameExtensions, getGalleryExtensionTelemetryData, getGalleryExtensionId, ExtensionKey, getLocalExtensionTelemetryData } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
14
|
+
import { isApplicationScopedExtension } from 'monaco-editor/esm/vs/platform/extensions/common/extensions.js';
|
|
15
|
+
import { ILogService } from 'monaco-editor/esm/vs/platform/log/common/log.js';
|
|
16
|
+
import { IProductService } from 'monaco-editor/esm/vs/platform/product/common/productService.js';
|
|
17
|
+
import { ITelemetryService } from 'monaco-editor/esm/vs/platform/telemetry/common/telemetry.js';
|
|
18
|
+
import { IUriIdentityService } from 'monaco-editor/esm/vs/platform/uriIdentity/common/uriIdentity.js';
|
|
19
|
+
import { IUserDataProfilesService } from 'monaco-editor/esm/vs/platform/userDataProfile/common/userDataProfile.js';
|
|
20
|
+
|
|
21
|
+
let AbstractExtensionManagementService = class AbstractExtensionManagementService extends Disposable {
|
|
22
|
+
get onInstallExtension() { return this._onInstallExtension.event; }
|
|
23
|
+
get onDidInstallExtensions() { return this._onDidInstallExtensions.event; }
|
|
24
|
+
get onUninstallExtension() { return this._onUninstallExtension.event; }
|
|
25
|
+
get onDidUninstallExtension() { return this._onDidUninstallExtension.event; }
|
|
26
|
+
get onDidUpdateExtensionMetadata() { return this._onDidUpdateExtensionMetadata.event; }
|
|
27
|
+
constructor(galleryService, telemetryService, uriIdentityService, logService, productService, userDataProfilesService) {
|
|
28
|
+
super();
|
|
29
|
+
this.galleryService = galleryService;
|
|
30
|
+
this.telemetryService = telemetryService;
|
|
31
|
+
this.uriIdentityService = uriIdentityService;
|
|
32
|
+
this.logService = logService;
|
|
33
|
+
this.productService = productService;
|
|
34
|
+
this.userDataProfilesService = userDataProfilesService;
|
|
35
|
+
this.lastReportTimestamp = 0;
|
|
36
|
+
this.installingExtensions = ( new Map());
|
|
37
|
+
this.uninstallingExtensions = ( new Map());
|
|
38
|
+
this._onInstallExtension = this._register(( new Emitter()));
|
|
39
|
+
this._onDidInstallExtensions = this._register(( new Emitter()));
|
|
40
|
+
this._onUninstallExtension = this._register(( new Emitter()));
|
|
41
|
+
this._onDidUninstallExtension = this._register(( new Emitter()));
|
|
42
|
+
this._onDidUpdateExtensionMetadata = this._register(( new Emitter()));
|
|
43
|
+
this.participants = [];
|
|
44
|
+
this._register(toDisposable(() => {
|
|
45
|
+
this.installingExtensions.forEach(({ task }) => task.cancel());
|
|
46
|
+
this.uninstallingExtensions.forEach(promise => promise.cancel());
|
|
47
|
+
this.installingExtensions.clear();
|
|
48
|
+
this.uninstallingExtensions.clear();
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
async canInstall(extension) {
|
|
52
|
+
const currentTargetPlatform = await this.getTargetPlatform();
|
|
53
|
+
return ( extension.allTargetPlatforms.some(
|
|
54
|
+
targetPlatform => isTargetPlatformCompatible(targetPlatform, extension.allTargetPlatforms, currentTargetPlatform)
|
|
55
|
+
));
|
|
56
|
+
}
|
|
57
|
+
async installFromGallery(extension, options = {}) {
|
|
58
|
+
try {
|
|
59
|
+
const results = await this.installGalleryExtensions([{ extension, options }]);
|
|
60
|
+
const result = results.find(({ identifier }) => areSameExtensions(identifier, extension.identifier));
|
|
61
|
+
if (result?.local) {
|
|
62
|
+
return result?.local;
|
|
63
|
+
}
|
|
64
|
+
if (result?.error) {
|
|
65
|
+
throw result.error;
|
|
66
|
+
}
|
|
67
|
+
throw toExtensionManagementError(new Error(`Unknown error while installing extension ${extension.identifier.id}`));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw toExtensionManagementError(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async installGalleryExtensions(extensions) {
|
|
74
|
+
if (!this.galleryService.isEnabled()) {
|
|
75
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', 'MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal);
|
|
76
|
+
}
|
|
77
|
+
const results = [];
|
|
78
|
+
const installableExtensions = [];
|
|
79
|
+
await Promise.allSettled(( extensions.map(async ({ extension, options }) => {
|
|
80
|
+
try {
|
|
81
|
+
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options?.installGivenVersion, !!options?.installPreReleaseVersion);
|
|
82
|
+
installableExtensions.push({ ...compatible, options });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
results.push({ identifier: extension.identifier, operation: 2 , source: extension, error });
|
|
86
|
+
}
|
|
87
|
+
})));
|
|
88
|
+
if (installableExtensions.length) {
|
|
89
|
+
results.push(...(await this.installExtensions(installableExtensions)));
|
|
90
|
+
}
|
|
91
|
+
for (const result of results) {
|
|
92
|
+
if (result.error) {
|
|
93
|
+
this.logService.error(`Failed to install extension.`, result.identifier.id);
|
|
94
|
+
this.logService.error(result.error);
|
|
95
|
+
if (result.source && !URI.isUri(result.source)) {
|
|
96
|
+
reportTelemetry(this.telemetryService, 'extensionGallery:install', { extensionData: getGalleryExtensionTelemetryData(result.source), error: result.error });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
async uninstall(extension, options = {}) {
|
|
103
|
+
this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id);
|
|
104
|
+
return this.uninstallExtension(extension, options);
|
|
105
|
+
}
|
|
106
|
+
async toggleAppliationScope(extension, fromProfileLocation) {
|
|
107
|
+
if (isApplicationScopedExtension(extension.manifest) || extension.isBuiltin) {
|
|
108
|
+
return extension;
|
|
109
|
+
}
|
|
110
|
+
if (extension.isApplicationScoped) {
|
|
111
|
+
let local = await this.updateMetadata(extension, { isApplicationScoped: false }, this.userDataProfilesService.defaultProfile.extensionsResource);
|
|
112
|
+
if (!this.uriIdentityService.extUri.isEqual(fromProfileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)) {
|
|
113
|
+
local = await this.copyExtension(extension, this.userDataProfilesService.defaultProfile.extensionsResource, fromProfileLocation);
|
|
114
|
+
}
|
|
115
|
+
for (const profile of this.userDataProfilesService.profiles) {
|
|
116
|
+
const existing = (await this.getInstalled(1 , profile.extensionsResource))
|
|
117
|
+
.find(e => areSameExtensions(e.identifier, extension.identifier));
|
|
118
|
+
if (existing) {
|
|
119
|
+
this._onDidUpdateExtensionMetadata.fire(existing);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this._onDidUninstallExtension.fire({ identifier: extension.identifier, profileLocation: profile.extensionsResource });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return local;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const local = this.uriIdentityService.extUri.isEqual(fromProfileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)
|
|
129
|
+
? await this.updateMetadata(extension, { isApplicationScoped: true }, this.userDataProfilesService.defaultProfile.extensionsResource)
|
|
130
|
+
: await this.copyExtension(extension, fromProfileLocation, this.userDataProfilesService.defaultProfile.extensionsResource, { isApplicationScoped: true });
|
|
131
|
+
this._onDidInstallExtensions.fire([{ identifier: local.identifier, operation: 2 , local, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource, applicationScoped: true }]);
|
|
132
|
+
return local;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
getExtensionsControlManifest() {
|
|
136
|
+
const now = ( new Date()).getTime();
|
|
137
|
+
if (!this.extensionsControlManifest || now - this.lastReportTimestamp > 1000 * 60 * 5) {
|
|
138
|
+
this.extensionsControlManifest = this.updateControlCache();
|
|
139
|
+
this.lastReportTimestamp = now;
|
|
140
|
+
}
|
|
141
|
+
return this.extensionsControlManifest;
|
|
142
|
+
}
|
|
143
|
+
registerParticipant(participant) {
|
|
144
|
+
this.participants.push(participant);
|
|
145
|
+
}
|
|
146
|
+
async installExtensions(extensions) {
|
|
147
|
+
const results = [];
|
|
148
|
+
await Promise.allSettled(( extensions.map(async (e) => {
|
|
149
|
+
try {
|
|
150
|
+
const result = await this.installExtension(e);
|
|
151
|
+
results.push(...result);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
results.push({ identifier: { id: getGalleryExtensionId(e.manifest.publisher, e.manifest.name) }, operation: 2 , source: e.extension, error });
|
|
155
|
+
}
|
|
156
|
+
})));
|
|
157
|
+
this._onDidInstallExtensions.fire(results);
|
|
158
|
+
return results;
|
|
159
|
+
}
|
|
160
|
+
async installExtension({ manifest, extension, options }) {
|
|
161
|
+
const isApplicationScoped = options.isApplicationScoped || options.isBuiltin || isApplicationScopedExtension(manifest);
|
|
162
|
+
const installExtensionTaskOptions = {
|
|
163
|
+
...options,
|
|
164
|
+
installOnlyNewlyAddedFromExtensionPack: URI.isUri(extension) ? options.installOnlyNewlyAddedFromExtensionPack : true,
|
|
165
|
+
isApplicationScoped,
|
|
166
|
+
profileLocation: isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation()
|
|
167
|
+
};
|
|
168
|
+
const getInstallExtensionTaskKey = (extension) => `${( ExtensionKey.create(extension).toString())}${installExtensionTaskOptions.profileLocation ? `-${( installExtensionTaskOptions.profileLocation.toString())}` : ''}`;
|
|
169
|
+
if (!URI.isUri(extension)) {
|
|
170
|
+
const installingExtension = this.installingExtensions.get(getInstallExtensionTaskKey(extension));
|
|
171
|
+
if (installingExtension) {
|
|
172
|
+
this.logService.info('Extensions is already requested to install', extension.identifier.id);
|
|
173
|
+
await installingExtension.task.waitUntilTaskIsFinished();
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const allInstallExtensionTasks = [];
|
|
178
|
+
const alreadyRequestedInstallations = [];
|
|
179
|
+
const installResults = [];
|
|
180
|
+
const installExtensionTask = this.createInstallExtensionTask(manifest, extension, installExtensionTaskOptions);
|
|
181
|
+
if (!URI.isUri(extension)) {
|
|
182
|
+
this.installingExtensions.set(getInstallExtensionTaskKey(extension), { task: installExtensionTask, waitingTasks: [] });
|
|
183
|
+
}
|
|
184
|
+
this._onInstallExtension.fire({ identifier: installExtensionTask.identifier, source: extension, profileLocation: installExtensionTaskOptions.profileLocation });
|
|
185
|
+
this.logService.info('Installing extension:', installExtensionTask.identifier.id);
|
|
186
|
+
allInstallExtensionTasks.push({ task: installExtensionTask, manifest });
|
|
187
|
+
let installExtensionHasDependents = false;
|
|
188
|
+
try {
|
|
189
|
+
if (installExtensionTaskOptions.donotIncludePackAndDependencies) {
|
|
190
|
+
this.logService.info('Installing the extension without checking dependencies and pack', installExtensionTask.identifier.id);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
try {
|
|
194
|
+
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(installExtensionTask.identifier, manifest, !!installExtensionTaskOptions.installOnlyNewlyAddedFromExtensionPack, !!installExtensionTaskOptions.installPreReleaseVersion, installExtensionTaskOptions.profileLocation);
|
|
195
|
+
const installed = await this.getInstalled(undefined, installExtensionTaskOptions.profileLocation);
|
|
196
|
+
const options = { ...installExtensionTaskOptions, donotIncludePackAndDependencies: true, context: { ...installExtensionTaskOptions.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } };
|
|
197
|
+
for (const { gallery, manifest } of distinct(allDepsAndPackExtensionsToInstall, ({ gallery }) => gallery.identifier.id)) {
|
|
198
|
+
installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier));
|
|
199
|
+
const key = getInstallExtensionTaskKey(gallery);
|
|
200
|
+
const existingInstallingExtension = this.installingExtensions.get(key);
|
|
201
|
+
if (existingInstallingExtension) {
|
|
202
|
+
if (this.canWaitForTask(installExtensionTask, existingInstallingExtension.task)) {
|
|
203
|
+
const identifier = existingInstallingExtension.task.identifier;
|
|
204
|
+
this.logService.info('Waiting for already requested installing extension', identifier.id, installExtensionTask.identifier.id);
|
|
205
|
+
existingInstallingExtension.waitingTasks.push(installExtensionTask);
|
|
206
|
+
alreadyRequestedInstallations.push(Event.toPromise(Event.filter(this.onDidInstallExtensions, results => ( results.some(result => areSameExtensions(result.identifier, identifier))))).then(results => {
|
|
207
|
+
this.logService.info('Finished waiting for already requested installing extension', identifier.id, installExtensionTask.identifier.id);
|
|
208
|
+
const result = results.find(result => areSameExtensions(result.identifier, identifier));
|
|
209
|
+
if (!result?.local) {
|
|
210
|
+
throw new Error(`Extension ${identifier.id} is not installed`);
|
|
211
|
+
}
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (!( installed.some(({ identifier }) => areSameExtensions(identifier, gallery.identifier)))) {
|
|
216
|
+
const task = this.createInstallExtensionTask(manifest, gallery, options);
|
|
217
|
+
this.installingExtensions.set(key, { task, waitingTasks: [installExtensionTask] });
|
|
218
|
+
this._onInstallExtension.fire({ identifier: task.identifier, source: gallery, profileLocation: installExtensionTaskOptions.profileLocation });
|
|
219
|
+
this.logService.info('Installing extension:', task.identifier.id, installExtensionTask.identifier.id);
|
|
220
|
+
allInstallExtensionTasks.push({ task, manifest });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (URI.isUri(installExtensionTask.source)) {
|
|
226
|
+
if (isNonEmptyArray(manifest.extensionDependencies)) {
|
|
227
|
+
this.logService.warn(`Cannot install dependencies of extension:`, installExtensionTask.identifier.id, error.message);
|
|
228
|
+
}
|
|
229
|
+
if (isNonEmptyArray(manifest.extensionPack)) {
|
|
230
|
+
this.logService.warn(`Cannot install packed extensions of extension:`, installExtensionTask.identifier.id, error.message);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', installExtensionTask.identifier.id);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const extensionsToInstallMap = allInstallExtensionTasks.reduce((result, { task, manifest }) => {
|
|
240
|
+
result.set(task.identifier.id.toLowerCase(), { task, manifest });
|
|
241
|
+
return result;
|
|
242
|
+
}, ( new Map()));
|
|
243
|
+
while (extensionsToInstallMap.size) {
|
|
244
|
+
let extensionsToInstall;
|
|
245
|
+
const extensionsWithoutDepsToInstall = [...( extensionsToInstallMap.values())].filter(({ manifest }) => !manifest.extensionDependencies?.some(id => ( extensionsToInstallMap.has(id.toLowerCase()))));
|
|
246
|
+
if (extensionsWithoutDepsToInstall.length) {
|
|
247
|
+
extensionsToInstall = extensionsToInstallMap.size === 1 ? extensionsWithoutDepsToInstall
|
|
248
|
+
: extensionsWithoutDepsToInstall.filter(({ task }) => !(task === installExtensionTask && !installExtensionHasDependents));
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
this.logService.info('Found extensions with circular dependencies', ( extensionsWithoutDepsToInstall.map(({ task }) => task.identifier.id)));
|
|
252
|
+
extensionsToInstall = [...( extensionsToInstallMap.values())];
|
|
253
|
+
}
|
|
254
|
+
await this.joinAllSettled(( extensionsToInstall.map(async ({ task }) => {
|
|
255
|
+
const startTime = ( new Date()).getTime();
|
|
256
|
+
try {
|
|
257
|
+
const local = await task.run();
|
|
258
|
+
await this.joinAllSettled(( this.participants.map(
|
|
259
|
+
participant => participant.postInstall(local, task.source, installExtensionTaskOptions, CancellationToken.None)
|
|
260
|
+
)));
|
|
261
|
+
if (!URI.isUri(task.source)) {
|
|
262
|
+
const isUpdate = task.operation === 3 ;
|
|
263
|
+
const durationSinceUpdate = isUpdate ? undefined : (( new Date()).getTime() - task.source.lastUpdated) / 1000;
|
|
264
|
+
reportTelemetry(this.telemetryService, isUpdate ? 'extensionGallery:update' : 'extensionGallery:install', {
|
|
265
|
+
extensionData: getGalleryExtensionTelemetryData(task.source),
|
|
266
|
+
verificationStatus: task.verificationStatus,
|
|
267
|
+
duration: ( new Date()).getTime() - startTime,
|
|
268
|
+
durationSinceUpdate
|
|
269
|
+
});
|
|
270
|
+
if (isWeb && task.operation !== 3 ) {
|
|
271
|
+
try {
|
|
272
|
+
await this.galleryService.reportStatistic(local.manifest.publisher, local.manifest.name, local.manifest.version, "install" );
|
|
273
|
+
}
|
|
274
|
+
catch (error) { }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
installResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source, context: task.options.context, profileLocation: task.profileLocation, applicationScoped: local.isApplicationScoped });
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (!URI.isUri(task.source)) {
|
|
281
|
+
reportTelemetry(this.telemetryService, task.operation === 3 ? 'extensionGallery:update' : 'extensionGallery:install', {
|
|
282
|
+
extensionData: getGalleryExtensionTelemetryData(task.source),
|
|
283
|
+
verificationStatus: task.verificationStatus,
|
|
284
|
+
duration: ( new Date()).getTime() - startTime,
|
|
285
|
+
error
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
this.logService.error('Error while installing the extension:', task.identifier.id);
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
extensionsToInstallMap.delete(task.identifier.id.toLowerCase());
|
|
293
|
+
}
|
|
294
|
+
})));
|
|
295
|
+
}
|
|
296
|
+
if (alreadyRequestedInstallations.length) {
|
|
297
|
+
await this.joinAllSettled(alreadyRequestedInstallations);
|
|
298
|
+
}
|
|
299
|
+
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
|
|
300
|
+
return installResults;
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
allInstallExtensionTasks.forEach(({ task }) => task.cancel());
|
|
304
|
+
if (installResults.length) {
|
|
305
|
+
try {
|
|
306
|
+
const result = await Promise.allSettled(( installResults.map(
|
|
307
|
+
({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation: installExtensionTaskOptions.profileLocation }).run()
|
|
308
|
+
)));
|
|
309
|
+
for (let index = 0; index < result.length; index++) {
|
|
310
|
+
const r = result[index];
|
|
311
|
+
const { identifier } = installResults[index];
|
|
312
|
+
if (r.status === 'fulfilled') {
|
|
313
|
+
this.logService.info('Rollback: Uninstalled extension', identifier.id);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
this.logService.warn('Rollback: Error while uninstalling extension', identifier.id, getErrorMessage(r.reason));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
this.logService.warn('Error while rolling back extensions', getErrorMessage(error), ( installResults.map(({ identifier }) => identifier.id)));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return ( allInstallExtensionTasks.map(
|
|
325
|
+
({ task }) => ({ identifier: task.identifier, operation: 2 , source: task.source, context: installExtensionTaskOptions.context, profileLocation: installExtensionTaskOptions.profileLocation, error })
|
|
326
|
+
));
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
for (const { task } of allInstallExtensionTasks) {
|
|
330
|
+
if (task.source && !URI.isUri(task.source)) {
|
|
331
|
+
this.installingExtensions.delete(getInstallExtensionTaskKey(task.source));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
canWaitForTask(taskToWait, taskToWaitFor) {
|
|
337
|
+
for (const [, { task, waitingTasks }] of this.installingExtensions.entries()) {
|
|
338
|
+
if (task === taskToWait) {
|
|
339
|
+
if (waitingTasks.includes(taskToWaitFor)) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
if (( waitingTasks.some(waitingTask => this.canWaitForTask(waitingTask, taskToWaitFor)))) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (task === taskToWaitFor && waitingTasks[0] && !this.canWaitForTask(taskToWait, waitingTasks[0])) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
async joinAllSettled(promises) {
|
|
353
|
+
const results = [];
|
|
354
|
+
const errors = [];
|
|
355
|
+
const promiseResults = await Promise.allSettled(promises);
|
|
356
|
+
for (const r of promiseResults) {
|
|
357
|
+
if (r.status === 'fulfilled') {
|
|
358
|
+
results.push(r.value);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
errors.push(r.reason);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (errors.length) {
|
|
365
|
+
throw joinErrors(errors);
|
|
366
|
+
}
|
|
367
|
+
return results;
|
|
368
|
+
}
|
|
369
|
+
async getAllDepsAndPackExtensions(extensionIdentifier, manifest, getOnlyNewlyAddedFromExtensionPack, installPreRelease, profile) {
|
|
370
|
+
if (!this.galleryService.isEnabled()) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
const installed = await this.getInstalled(undefined, profile);
|
|
374
|
+
const knownIdentifiers = [];
|
|
375
|
+
const allDependenciesAndPacks = [];
|
|
376
|
+
const collectDependenciesAndPackExtensionsToInstall = async (extensionIdentifier, manifest) => {
|
|
377
|
+
knownIdentifiers.push(extensionIdentifier);
|
|
378
|
+
const dependecies = manifest.extensionDependencies || [];
|
|
379
|
+
const dependenciesAndPackExtensions = [...dependecies];
|
|
380
|
+
if (manifest.extensionPack) {
|
|
381
|
+
const existing = getOnlyNewlyAddedFromExtensionPack ? installed.find(e => areSameExtensions(e.identifier, extensionIdentifier)) : undefined;
|
|
382
|
+
for (const extension of manifest.extensionPack) {
|
|
383
|
+
if (!(existing && existing.manifest.extensionPack && ( existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension }))))) {
|
|
384
|
+
if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) {
|
|
385
|
+
dependenciesAndPackExtensions.push(extension);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (dependenciesAndPackExtensions.length) {
|
|
391
|
+
const ids = dependenciesAndPackExtensions.filter(id => knownIdentifiers.every(galleryIdentifier => !areSameExtensions(galleryIdentifier, { id })));
|
|
392
|
+
if (ids.length) {
|
|
393
|
+
const galleryExtensions = await this.galleryService.getExtensions(( ids.map(id => ({ id, preRelease: installPreRelease }))), CancellationToken.None);
|
|
394
|
+
for (const galleryExtension of galleryExtensions) {
|
|
395
|
+
if (knownIdentifiers.find(identifier => areSameExtensions(identifier, galleryExtension.identifier))) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const isDependency = ( dependecies.some(id => areSameExtensions({ id }, galleryExtension.identifier)));
|
|
399
|
+
let compatible;
|
|
400
|
+
try {
|
|
401
|
+
compatible = await this.checkAndGetCompatibleVersion(galleryExtension, false, installPreRelease);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
if (!isDependency) {
|
|
405
|
+
this.logService.info('Skipping the packed extension as it cannot be installed', galleryExtension.identifier.id, getErrorMessage(error));
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
allDependenciesAndPacks.push({ gallery: compatible.extension, manifest: compatible.manifest });
|
|
413
|
+
await collectDependenciesAndPackExtensionsToInstall(compatible.extension.identifier, compatible.manifest);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
await collectDependenciesAndPackExtensionsToInstall(extensionIdentifier, manifest);
|
|
419
|
+
return allDependenciesAndPacks;
|
|
420
|
+
}
|
|
421
|
+
async checkAndGetCompatibleVersion(extension, sameVersion, installPreRelease) {
|
|
422
|
+
let compatibleExtension;
|
|
423
|
+
const extensionsControlManifest = await this.getExtensionsControlManifest();
|
|
424
|
+
if (( extensionsControlManifest.malicious.some(identifier => areSameExtensions(extension.identifier, identifier)))) {
|
|
425
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', 'malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious);
|
|
426
|
+
}
|
|
427
|
+
const deprecationInfo = extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()];
|
|
428
|
+
if (deprecationInfo?.extension?.autoMigrate) {
|
|
429
|
+
this.logService.info(`The '${extension.identifier.id}' extension is deprecated, fetching the compatible '${deprecationInfo.extension.id}' extension instead.`);
|
|
430
|
+
compatibleExtension = (await this.galleryService.getExtensions([{ id: deprecationInfo.extension.id, preRelease: deprecationInfo.extension.preRelease }], { targetPlatform: await this.getTargetPlatform(), compatible: true }, CancellationToken.None))[0];
|
|
431
|
+
if (!compatibleExtension) {
|
|
432
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', 'notFoundDeprecatedReplacementExtension', "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", extension.identifier.id, deprecationInfo.extension.id), ExtensionManagementErrorCode.Deprecated);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
if (!(await this.canInstall(extension))) {
|
|
437
|
+
const targetPlatform = await this.getTargetPlatform();
|
|
438
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', 'incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform);
|
|
439
|
+
}
|
|
440
|
+
compatibleExtension = await this.getCompatibleVersion(extension, sameVersion, installPreRelease);
|
|
441
|
+
if (!compatibleExtension) {
|
|
442
|
+
if (!installPreRelease && extension.properties.isPreReleaseVersion && (await this.galleryService.getExtensions([extension.identifier], CancellationToken.None))[0]) {
|
|
443
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', 'notFoundReleaseExtension', "Can't install release version of '{0}' extension because it has no release version.", extension.identifier.id), ExtensionManagementErrorCode.ReleaseVersionNotFound);
|
|
444
|
+
}
|
|
445
|
+
throw new ExtensionManagementError(nls.localizeWithPath('vs/platform/extensionManagement/common/abstractExtensionManagementService', '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);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
this.logService.info('Getting Manifest...', compatibleExtension.identifier.id);
|
|
449
|
+
const manifest = await this.galleryService.getManifest(compatibleExtension, CancellationToken.None);
|
|
450
|
+
if (manifest === null) {
|
|
451
|
+
throw new ExtensionManagementError(`Missing manifest for extension ${compatibleExtension.identifier.id}`, ExtensionManagementErrorCode.Invalid);
|
|
452
|
+
}
|
|
453
|
+
if (manifest.version !== compatibleExtension.version) {
|
|
454
|
+
throw new ExtensionManagementError(`Cannot install '${compatibleExtension.identifier.id}' extension because of version mismatch in Marketplace`, ExtensionManagementErrorCode.Invalid);
|
|
455
|
+
}
|
|
456
|
+
return { extension: compatibleExtension, manifest };
|
|
457
|
+
}
|
|
458
|
+
async getCompatibleVersion(extension, sameVersion, includePreRelease) {
|
|
459
|
+
const targetPlatform = await this.getTargetPlatform();
|
|
460
|
+
let compatibleExtension = null;
|
|
461
|
+
if (!sameVersion && extension.hasPreReleaseVersion && extension.properties.isPreReleaseVersion !== includePreRelease) {
|
|
462
|
+
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, preRelease: includePreRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0] || null;
|
|
463
|
+
}
|
|
464
|
+
if (!compatibleExtension && (await this.galleryService.isExtensionCompatible(extension, includePreRelease, targetPlatform))) {
|
|
465
|
+
compatibleExtension = extension;
|
|
466
|
+
}
|
|
467
|
+
if (!compatibleExtension) {
|
|
468
|
+
if (sameVersion) {
|
|
469
|
+
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, version: extension.version }], { targetPlatform, compatible: true }, CancellationToken.None))[0] || null;
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
compatibleExtension = await this.galleryService.getCompatibleExtension(extension, includePreRelease, targetPlatform);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return compatibleExtension;
|
|
476
|
+
}
|
|
477
|
+
async uninstallExtension(extension, options) {
|
|
478
|
+
const uninstallOptions = {
|
|
479
|
+
...options,
|
|
480
|
+
profileLocation: extension.isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation()
|
|
481
|
+
};
|
|
482
|
+
const getUninstallExtensionTaskKey = (identifier) => `${identifier.id.toLowerCase()}${uninstallOptions.versionOnly ? `-${extension.manifest.version}` : ''}${uninstallOptions.profileLocation ? `@${( uninstallOptions.profileLocation.toString())}` : ''}`;
|
|
483
|
+
const uninstallExtensionTask = this.uninstallingExtensions.get(getUninstallExtensionTaskKey(extension.identifier));
|
|
484
|
+
if (uninstallExtensionTask) {
|
|
485
|
+
this.logService.info('Extensions is already requested to uninstall', extension.identifier.id);
|
|
486
|
+
return uninstallExtensionTask.waitUntilTaskIsFinished();
|
|
487
|
+
}
|
|
488
|
+
const createUninstallExtensionTask = (extension) => {
|
|
489
|
+
const uninstallExtensionTask = this.createUninstallExtensionTask(extension, uninstallOptions);
|
|
490
|
+
this.uninstallingExtensions.set(getUninstallExtensionTaskKey(uninstallExtensionTask.extension.identifier), uninstallExtensionTask);
|
|
491
|
+
if (uninstallOptions.profileLocation) {
|
|
492
|
+
this.logService.info('Uninstalling extension from the profile:', `${extension.identifier.id}@${extension.manifest.version}`, ( uninstallOptions.profileLocation.toString()));
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
this.logService.info('Uninstalling extension:', `${extension.identifier.id}@${extension.manifest.version}`);
|
|
496
|
+
}
|
|
497
|
+
this._onUninstallExtension.fire({ identifier: extension.identifier, profileLocation: uninstallOptions.profileLocation, applicationScoped: extension.isApplicationScoped });
|
|
498
|
+
return uninstallExtensionTask;
|
|
499
|
+
};
|
|
500
|
+
const postUninstallExtension = (extension, error) => {
|
|
501
|
+
if (error) {
|
|
502
|
+
if (uninstallOptions.profileLocation) {
|
|
503
|
+
this.logService.error('Failed to uninstall extension from the profile:', `${extension.identifier.id}@${extension.manifest.version}`, ( uninstallOptions.profileLocation.toString()), error.message);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
this.logService.error('Failed to uninstall extension:', `${extension.identifier.id}@${extension.manifest.version}`, error.message);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
if (uninstallOptions.profileLocation) {
|
|
511
|
+
this.logService.info('Successfully uninstalled extension from the profile', `${extension.identifier.id}@${extension.manifest.version}`, ( uninstallOptions.profileLocation.toString()));
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
this.logService.info('Successfully uninstalled extension:', `${extension.identifier.id}@${extension.manifest.version}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
reportTelemetry(this.telemetryService, 'extensionGallery:uninstall', { extensionData: getLocalExtensionTelemetryData(extension), error });
|
|
518
|
+
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: error?.code, profileLocation: uninstallOptions.profileLocation, applicationScoped: extension.isApplicationScoped });
|
|
519
|
+
};
|
|
520
|
+
const allTasks = [];
|
|
521
|
+
const processedTasks = [];
|
|
522
|
+
try {
|
|
523
|
+
allTasks.push(createUninstallExtensionTask(extension));
|
|
524
|
+
const installed = await this.getInstalled(1 , uninstallOptions.profileLocation);
|
|
525
|
+
if (uninstallOptions.donotIncludePack) {
|
|
526
|
+
this.logService.info('Uninstalling the extension without including packed extension', `${extension.identifier.id}@${extension.manifest.version}`);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed);
|
|
530
|
+
for (const packedExtension of packedExtensions) {
|
|
531
|
+
if (( this.uninstallingExtensions.has(getUninstallExtensionTaskKey(packedExtension.identifier)))) {
|
|
532
|
+
this.logService.info('Extensions is already requested to uninstall', packedExtension.identifier.id);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
allTasks.push(createUninstallExtensionTask(packedExtension));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (uninstallOptions.donotCheckDependents) {
|
|
540
|
+
this.logService.info('Uninstalling the extension without checking dependents', `${extension.identifier.id}@${extension.manifest.version}`);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
this.checkForDependents(( allTasks.map(task => task.extension)), installed, extension);
|
|
544
|
+
}
|
|
545
|
+
await this.joinAllSettled(( allTasks.map(async (task) => {
|
|
546
|
+
try {
|
|
547
|
+
await task.run();
|
|
548
|
+
await this.joinAllSettled(( this.participants.map(
|
|
549
|
+
participant => participant.postUninstall(task.extension, uninstallOptions, CancellationToken.None)
|
|
550
|
+
)));
|
|
551
|
+
if (task.extension.identifier.uuid) {
|
|
552
|
+
try {
|
|
553
|
+
await this.galleryService.reportStatistic(task.extension.manifest.publisher, task.extension.manifest.name, task.extension.manifest.version, "uninstall" );
|
|
554
|
+
}
|
|
555
|
+
catch (error) { }
|
|
556
|
+
}
|
|
557
|
+
postUninstallExtension(task.extension);
|
|
558
|
+
}
|
|
559
|
+
catch (e) {
|
|
560
|
+
const error = e instanceof ExtensionManagementError ? e : ( new ExtensionManagementError(getErrorMessage(e), ExtensionManagementErrorCode.Internal));
|
|
561
|
+
postUninstallExtension(task.extension, error);
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
processedTasks.push(task);
|
|
566
|
+
}
|
|
567
|
+
})));
|
|
568
|
+
}
|
|
569
|
+
catch (e) {
|
|
570
|
+
const error = e instanceof ExtensionManagementError ? e : ( new ExtensionManagementError(getErrorMessage(e), ExtensionManagementErrorCode.Internal));
|
|
571
|
+
for (const task of allTasks) {
|
|
572
|
+
try {
|
|
573
|
+
task.cancel();
|
|
574
|
+
}
|
|
575
|
+
catch (error) { }
|
|
576
|
+
if (!processedTasks.includes(task)) {
|
|
577
|
+
postUninstallExtension(task.extension, error);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
finally {
|
|
583
|
+
for (const task of allTasks) {
|
|
584
|
+
if (!this.uninstallingExtensions.delete(getUninstallExtensionTaskKey(task.extension.identifier))) {
|
|
585
|
+
this.logService.warn('Uninstallation task is not found in the cache', task.extension.identifier.id);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
checkForDependents(extensionsToUninstall, installed, extensionToUninstall) {
|
|
591
|
+
for (const extension of extensionsToUninstall) {
|
|
592
|
+
const dependents = this.getDependents(extension, installed);
|
|
593
|
+
if (dependents.length) {
|
|
594
|
+
const remainingDependents = dependents.filter(dependent => !( extensionsToUninstall.some(e => areSameExtensions(e.identifier, dependent.identifier))));
|
|
595
|
+
if (remainingDependents.length) {
|
|
596
|
+
throw new Error(this.getDependentsErrorMessage(extension, remainingDependents, extensionToUninstall));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
getDependentsErrorMessage(dependingExtension, dependents, extensionToUninstall) {
|
|
602
|
+
if (extensionToUninstall === dependingExtension) {
|
|
603
|
+
if (dependents.length === 1) {
|
|
604
|
+
return ( nls.localizeWithPath(
|
|
605
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
606
|
+
'singleDependentError',
|
|
607
|
+
"Cannot uninstall '{0}' extension. '{1}' extension depends on this.",
|
|
608
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
609
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name
|
|
610
|
+
));
|
|
611
|
+
}
|
|
612
|
+
if (dependents.length === 2) {
|
|
613
|
+
return ( nls.localizeWithPath(
|
|
614
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
615
|
+
'twoDependentsError',
|
|
616
|
+
"Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.",
|
|
617
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
618
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name,
|
|
619
|
+
dependents[1].manifest.displayName || dependents[1].manifest.name
|
|
620
|
+
));
|
|
621
|
+
}
|
|
622
|
+
return ( nls.localizeWithPath(
|
|
623
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
624
|
+
'multipleDependentsError',
|
|
625
|
+
"Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.",
|
|
626
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
627
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name,
|
|
628
|
+
dependents[1].manifest.displayName || dependents[1].manifest.name
|
|
629
|
+
));
|
|
630
|
+
}
|
|
631
|
+
if (dependents.length === 1) {
|
|
632
|
+
return ( nls.localizeWithPath(
|
|
633
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
634
|
+
'singleIndirectDependentError',
|
|
635
|
+
"Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.",
|
|
636
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
637
|
+
dependingExtension.manifest.displayName
|
|
638
|
+
|| dependingExtension.manifest.name,
|
|
639
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name
|
|
640
|
+
));
|
|
641
|
+
}
|
|
642
|
+
if (dependents.length === 2) {
|
|
643
|
+
return ( nls.localizeWithPath(
|
|
644
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
645
|
+
'twoIndirectDependentsError',
|
|
646
|
+
"Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.",
|
|
647
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
648
|
+
dependingExtension.manifest.displayName
|
|
649
|
+
|| dependingExtension.manifest.name,
|
|
650
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name,
|
|
651
|
+
dependents[1].manifest.displayName || dependents[1].manifest.name
|
|
652
|
+
));
|
|
653
|
+
}
|
|
654
|
+
return ( nls.localizeWithPath(
|
|
655
|
+
'vs/platform/extensionManagement/common/abstractExtensionManagementService',
|
|
656
|
+
'multipleIndirectDependentsError',
|
|
657
|
+
"Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this.",
|
|
658
|
+
extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name,
|
|
659
|
+
dependingExtension.manifest.displayName
|
|
660
|
+
|| dependingExtension.manifest.name,
|
|
661
|
+
dependents[0].manifest.displayName || dependents[0].manifest.name,
|
|
662
|
+
dependents[1].manifest.displayName || dependents[1].manifest.name
|
|
663
|
+
));
|
|
664
|
+
}
|
|
665
|
+
getAllPackExtensionsToUninstall(extension, installed, checked = []) {
|
|
666
|
+
if (checked.indexOf(extension) !== -1) {
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
checked.push(extension);
|
|
670
|
+
const extensionsPack = extension.manifest.extensionPack ? extension.manifest.extensionPack : [];
|
|
671
|
+
if (extensionsPack.length) {
|
|
672
|
+
const packedExtensions = installed.filter(i => !i.isBuiltin && ( extensionsPack.some(id => areSameExtensions({ id }, i.identifier))));
|
|
673
|
+
const packOfPackedExtensions = [];
|
|
674
|
+
for (const packedExtension of packedExtensions) {
|
|
675
|
+
packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked));
|
|
676
|
+
}
|
|
677
|
+
return [...packedExtensions, ...packOfPackedExtensions];
|
|
678
|
+
}
|
|
679
|
+
return [];
|
|
680
|
+
}
|
|
681
|
+
getDependents(extension, installed) {
|
|
682
|
+
return installed.filter(e => e.manifest.extensionDependencies && ( e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))));
|
|
683
|
+
}
|
|
684
|
+
async updateControlCache() {
|
|
685
|
+
try {
|
|
686
|
+
this.logService.trace('ExtensionManagementService.refreshReportedCache');
|
|
687
|
+
const manifest = await this.galleryService.getExtensionsControlManifest();
|
|
688
|
+
this.logService.trace(`ExtensionManagementService.refreshControlCache`, manifest);
|
|
689
|
+
return manifest;
|
|
690
|
+
}
|
|
691
|
+
catch (err) {
|
|
692
|
+
this.logService.trace('ExtensionManagementService.refreshControlCache - failed to get extension control manifest');
|
|
693
|
+
return { malicious: [], deprecated: {}, search: [] };
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
AbstractExtensionManagementService = ( __decorate([
|
|
698
|
+
( __param(0, IExtensionGalleryService)),
|
|
699
|
+
( __param(1, ITelemetryService)),
|
|
700
|
+
( __param(2, IUriIdentityService)),
|
|
701
|
+
( __param(3, ILogService)),
|
|
702
|
+
( __param(4, IProductService)),
|
|
703
|
+
( __param(5, IUserDataProfilesService))
|
|
704
|
+
], AbstractExtensionManagementService));
|
|
705
|
+
function joinErrors(errorOrErrors) {
|
|
706
|
+
const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors];
|
|
707
|
+
if (errors.length === 1) {
|
|
708
|
+
return errors[0] instanceof Error ? errors[0] : ( new Error(errors[0]));
|
|
709
|
+
}
|
|
710
|
+
return errors.reduce((previousValue, currentValue) => {
|
|
711
|
+
return ( new Error(
|
|
712
|
+
`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`
|
|
713
|
+
));
|
|
714
|
+
}, ( new Error('')));
|
|
715
|
+
}
|
|
716
|
+
function toExtensionManagementError(error) {
|
|
717
|
+
if (error instanceof ExtensionManagementError) {
|
|
718
|
+
return error;
|
|
719
|
+
}
|
|
720
|
+
const e = ( new ExtensionManagementError(error.message, ExtensionManagementErrorCode.Internal));
|
|
721
|
+
e.stack = error.stack;
|
|
722
|
+
return e;
|
|
723
|
+
}
|
|
724
|
+
function reportTelemetry(telemetryService, eventName, { extensionData, verificationStatus, duration, error, durationSinceUpdate }) {
|
|
725
|
+
let errorcode;
|
|
726
|
+
let errorcodeDetail;
|
|
727
|
+
if (isDefined(verificationStatus)) {
|
|
728
|
+
if (verificationStatus === true) {
|
|
729
|
+
verificationStatus = 'Verified';
|
|
730
|
+
}
|
|
731
|
+
else if (verificationStatus === false) {
|
|
732
|
+
verificationStatus = 'Unverified';
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
errorcode = ExtensionManagementErrorCode.Signature;
|
|
736
|
+
errorcodeDetail = verificationStatus;
|
|
737
|
+
verificationStatus = 'Unverified';
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (error) {
|
|
741
|
+
if (error instanceof ExtensionManagementError) {
|
|
742
|
+
errorcode = error.code;
|
|
743
|
+
if (error.code === ExtensionManagementErrorCode.Signature) {
|
|
744
|
+
errorcodeDetail = error.message;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
errorcode = ExtensionManagementErrorCode.Internal;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
telemetryService.publicLog(eventName, { ...extensionData, verificationStatus, success: !error, duration, errorcode, errorcodeDetail, durationSinceUpdate });
|
|
752
|
+
}
|
|
753
|
+
class AbstractExtensionTask {
|
|
754
|
+
constructor() {
|
|
755
|
+
this.barrier = ( new Barrier());
|
|
756
|
+
}
|
|
757
|
+
async waitUntilTaskIsFinished() {
|
|
758
|
+
await this.barrier.wait();
|
|
759
|
+
return this.cancellablePromise;
|
|
760
|
+
}
|
|
761
|
+
async run() {
|
|
762
|
+
if (!this.cancellablePromise) {
|
|
763
|
+
this.cancellablePromise = createCancelablePromise(token => this.doRun(token));
|
|
764
|
+
}
|
|
765
|
+
this.barrier.open();
|
|
766
|
+
return this.cancellablePromise;
|
|
767
|
+
}
|
|
768
|
+
cancel() {
|
|
769
|
+
if (!this.cancellablePromise) {
|
|
770
|
+
this.cancellablePromise = createCancelablePromise(token => {
|
|
771
|
+
return ( new Promise((c, e) => {
|
|
772
|
+
const disposable = token.onCancellationRequested(() => {
|
|
773
|
+
disposable.dispose();
|
|
774
|
+
e(( new CancellationError()));
|
|
775
|
+
});
|
|
776
|
+
}));
|
|
777
|
+
});
|
|
778
|
+
this.barrier.open();
|
|
779
|
+
}
|
|
780
|
+
this.cancellablePromise.cancel();
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export { AbstractExtensionManagementService, AbstractExtensionTask, joinErrors, toExtensionManagementError };
|