mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
committed by
GitHub
parent
04f7885c54
commit
f3aa9a201b
@@ -16,9 +16,9 @@ import * as nls from 'vs/nls';
|
||||
import {
|
||||
ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IGalleryExtension, ILocalExtension, InstallOperation,
|
||||
IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode,
|
||||
InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService
|
||||
InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { areSameExtensions, ExtensionKey, getGalleryExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
@@ -26,6 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
|
||||
export type ExtensionVerificationStatus = boolean | string;
|
||||
export type InstallableExtension = { readonly manifest: IExtensionManifest; extension: IGalleryExtension | URI; options: InstallOptions & InstallVSIXOptions };
|
||||
|
||||
export type InstallExtensionTaskOptions = InstallOptions & InstallVSIXOptions & { readonly profileLocation: URI };
|
||||
export interface IInstallExtensionTask {
|
||||
@@ -93,19 +94,54 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
||||
|
||||
async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise<ILocalExtension> {
|
||||
try {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal);
|
||||
const results = await this.installGalleryExtensions([{ extension, options }]);
|
||||
const result = results.find(({ identifier }) => areSameExtensions(identifier, extension.identifier));
|
||||
if (result?.local) {
|
||||
return result?.local;
|
||||
}
|
||||
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options.installGivenVersion, !!options.installPreReleaseVersion);
|
||||
return await this.installExtension(compatible.manifest, compatible.extension, options);
|
||||
if (result?.error) {
|
||||
throw result.error;
|
||||
}
|
||||
throw toExtensionManagementError(new Error(`Unknown error while installing extension ${extension.identifier.id}`));
|
||||
} catch (error) {
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', { extensionData: getGalleryExtensionTelemetryData(extension), error });
|
||||
this.logService.error(`Failed to install extension.`, extension.identifier.id);
|
||||
this.logService.error(error);
|
||||
throw toExtensionManagementError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal);
|
||||
}
|
||||
|
||||
const results: InstallExtensionResult[] = [];
|
||||
const installableExtensions: InstallableExtension[] = [];
|
||||
|
||||
await Promise.allSettled(extensions.map(async ({ extension, options }) => {
|
||||
try {
|
||||
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options?.installGivenVersion, !!options?.installPreReleaseVersion);
|
||||
installableExtensions.push({ ...compatible, options });
|
||||
} catch (error) {
|
||||
results.push({ identifier: extension.identifier, operation: InstallOperation.Install, source: extension, error });
|
||||
}
|
||||
}));
|
||||
|
||||
if (installableExtensions.length) {
|
||||
results.push(...await this.installExtensions(installableExtensions));
|
||||
}
|
||||
|
||||
for (const result of results) {
|
||||
if (result.error) {
|
||||
this.logService.error(`Failed to install extension.`, result.identifier.id);
|
||||
this.logService.error(result.error);
|
||||
if (result.source && !URI.isUri(result.source)) {
|
||||
reportTelemetry(this.telemetryService, 'extensionGallery:install', { extensionData: getGalleryExtensionTelemetryData(result.source), error: result.error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise<void> {
|
||||
this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id);
|
||||
return this.uninstallExtension(extension, options);
|
||||
@@ -126,7 +162,21 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
||||
this.participants.push(participant);
|
||||
}
|
||||
|
||||
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise<ILocalExtension> {
|
||||
protected async installExtensions(extensions: InstallableExtension[]): Promise<InstallExtensionResult[]> {
|
||||
const results: InstallExtensionResult[] = [];
|
||||
await Promise.allSettled(extensions.map(async e => {
|
||||
try {
|
||||
const result = await this.installExtension(e);
|
||||
results.push(...result);
|
||||
} catch (error) {
|
||||
results.push({ identifier: { id: getGalleryExtensionId(e.manifest.publisher, e.manifest.name) }, operation: InstallOperation.Install, source: e.extension, error });
|
||||
}
|
||||
}));
|
||||
this._onDidInstallExtensions.fire(results);
|
||||
return results;
|
||||
}
|
||||
|
||||
private async installExtension({ manifest, extension, options }: InstallableExtension): Promise<InstallExtensionResult[]> {
|
||||
|
||||
const isApplicationScoped = options.isApplicationScoped || options.isBuiltin || isApplicationScopedExtension(manifest);
|
||||
const installExtensionTaskOptions: InstallExtensionTaskOptions = {
|
||||
@@ -142,7 +192,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
||||
const installingExtension = this.installingExtensions.get(getInstallExtensionTaskKey(extension));
|
||||
if (installingExtension) {
|
||||
this.logService.info('Extensions is already requested to install', extension.identifier.id);
|
||||
return installingExtension.task.waitUntilTaskIsFinished();
|
||||
await installingExtension.task.waitUntilTaskIsFinished();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,8 +323,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
||||
}
|
||||
|
||||
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
|
||||
this._onDidInstallExtensions.fire(installResults);
|
||||
return installResults.filter(({ identifier }) => areSameExtensions(identifier, installExtensionTask.identifier))[0].local;
|
||||
return installResults;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
@@ -299,8 +349,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidInstallExtensions.fire(allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source, context: installExtensionTaskOptions.context, profileLocation: installExtensionTaskOptions.profileLocation })));
|
||||
throw error;
|
||||
return allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source, context: installExtensionTaskOptions.context, profileLocation: installExtensionTaskOptions.profileLocation, error }));
|
||||
} finally {
|
||||
// Finally, remove all the tasks from the cache
|
||||
for (const { task } of allInstallExtensionTasks) {
|
||||
@@ -678,7 +727,7 @@ export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | stri
|
||||
}, new Error(''));
|
||||
}
|
||||
|
||||
function toExtensionManagementError(error: Error): ExtensionManagementError {
|
||||
export function toExtensionManagementError(error: Error): ExtensionManagementError {
|
||||
if (error instanceof ExtensionManagementError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -377,6 +377,7 @@ export interface InstallExtensionResult {
|
||||
readonly operation: InstallOperation;
|
||||
readonly source?: URI | IGalleryExtension;
|
||||
readonly local?: ILocalExtension;
|
||||
readonly error?: Error;
|
||||
readonly context?: IStringDictionary<any>;
|
||||
readonly profileLocation?: URI;
|
||||
readonly applicationScoped?: boolean;
|
||||
@@ -450,6 +451,8 @@ export interface IExtensionManagementParticipant {
|
||||
postUninstall(local: ILocalExtension, options: UninstallOptions, token: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
export type InstallExtensionInfo = { readonly extension: IGalleryExtension; readonly options: InstallOptions };
|
||||
|
||||
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
|
||||
export interface IExtensionManagementService {
|
||||
readonly _serviceBrand: undefined;
|
||||
@@ -466,6 +469,7 @@ export interface IExtensionManagementService {
|
||||
install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension>;
|
||||
canInstall(extension: IGalleryExtension): Promise<boolean>;
|
||||
installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise<ILocalExtension>;
|
||||
installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]>;
|
||||
installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension>;
|
||||
installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]>;
|
||||
uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void>;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
|
||||
@@ -128,6 +128,10 @@ export class ExtensionManagementChannel implements IServerChannel {
|
||||
case 'installFromGallery': {
|
||||
return this.service.installFromGallery(args[0], transformIncomingOptions(args[1], uriTransformer));
|
||||
}
|
||||
case 'installGalleryExtensions': {
|
||||
const arg: InstallExtensionInfo[] = args[0];
|
||||
return this.service.installGalleryExtensions(arg.map(({ extension, options }) => ({ extension, options: transformIncomingOptions(options, uriTransformer) ?? {} })));
|
||||
}
|
||||
case 'uninstall': {
|
||||
return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));
|
||||
}
|
||||
@@ -250,6 +254,11 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
|
||||
return Promise.resolve(this.channel.call<ILocalExtension>('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null));
|
||||
}
|
||||
|
||||
async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
|
||||
const results = await this.channel.call<InstallExtensionResult[]>('installGalleryExtensions', [extensions]);
|
||||
return results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) }));
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {
|
||||
return Promise.resolve(this.channel.call<void>('uninstall', [extension!, options]));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IDownloadService } from 'vs/platform/download/common/download';
|
||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { AbstractExtensionManagementService, AbstractExtensionTask, ExtensionVerificationStatus, IInstallExtensionTask, InstallExtensionTaskOptions, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
|
||||
import { AbstractExtensionManagementService, AbstractExtensionTask, ExtensionVerificationStatus, IInstallExtensionTask, InstallExtensionTaskOptions, IUninstallExtensionTask, joinErrors, toExtensionManagementError, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
|
||||
import {
|
||||
ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOperation,
|
||||
Metadata, InstallVSIXOptions
|
||||
@@ -148,11 +148,19 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
|
||||
|
||||
try {
|
||||
const manifest = await getManifest(path.resolve(location.fsPath));
|
||||
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
|
||||
if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, this.productService.version, this.productService.date)) {
|
||||
throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", getGalleryExtensionId(manifest.publisher, manifest.name), this.productService.version));
|
||||
throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extensionId, this.productService.version));
|
||||
}
|
||||
|
||||
return await this.installExtension(manifest, location, options);
|
||||
const result = await this.installExtensions([{ manifest, extension: location, options }]);
|
||||
if (result[0]?.local) {
|
||||
return result[0]?.local;
|
||||
}
|
||||
if (result[0]?.error) {
|
||||
throw result[0].error;
|
||||
}
|
||||
throw toExtensionManagementError(new Error(`Unknown error while installing extension ${extensionId}`));
|
||||
} finally {
|
||||
await cleanup();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, ExtensionManagementError, ExtensionManagementErrorCode, IGalleryExtension, DISABLED_EXTENSIONS_STORAGE_PATH, EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, EXTENSION_INSTALL_SYNC_CONTEXT } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, ExtensionManagementError, ExtensionManagementErrorCode, IGalleryExtension, DISABLED_EXTENSIONS_STORAGE_PATH, EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, EXTENSION_INSTALL_SYNC_CONTEXT, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage';
|
||||
import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -405,7 +405,8 @@ export class LocalExtensionsProvider {
|
||||
|
||||
async updateLocalExtensions(added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], skippedExtensions: ISyncExtension[], profile: IUserDataProfile): Promise<ISyncExtension[]> {
|
||||
const syncResourceLogLabel = getSyncResourceLogLabel(SyncResource.Extensions, profile);
|
||||
const extensionsToInstall: [ISyncExtension, IGalleryExtension][] = [];
|
||||
const extensionsToInstall: InstallExtensionInfo[] = [];
|
||||
const syncExtensionsToInstall = new Map<string, ISyncExtension>();
|
||||
const removeFromSkipped: IExtensionIdentifier[] = [];
|
||||
const addToSkipped: ISyncExtension[] = [];
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled(undefined, profile.extensionsResource);
|
||||
@@ -473,7 +474,17 @@ export class LocalExtensionsProvider {
|
||||
|| (version && installedExtension.manifest.version !== version) // Install if the extension version has changed
|
||||
) {
|
||||
if (await this.extensionManagementService.canInstall(extension)) {
|
||||
extensionsToInstall.push([e, extension]);
|
||||
extensionsToInstall.push({
|
||||
extension, options: {
|
||||
isMachineScoped: false /* set isMachineScoped value to prevent install and sync dialog in web */,
|
||||
donotIncludePackAndDependencies: true,
|
||||
installGivenVersion: e.pinned && !!e.version,
|
||||
installPreReleaseVersion: e.preRelease,
|
||||
profileLocation: profile.extensionsResource,
|
||||
context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true, [EXTENSION_INSTALL_SYNC_CONTEXT]: true }
|
||||
}
|
||||
});
|
||||
syncExtensionsToInstall.set(extension.identifier.id.toLowerCase(), e);
|
||||
} else {
|
||||
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because it cannot be installed.`, extension.displayName || extension.identifier.id);
|
||||
addToSkipped.push(e);
|
||||
@@ -504,26 +515,22 @@ export class LocalExtensionsProvider {
|
||||
}
|
||||
|
||||
// 3. Install extensions at the end
|
||||
for (const [e, extension] of extensionsToInstall) {
|
||||
try {
|
||||
this.logService.trace(`${syncResourceLogLabel}: Installing extension...`, extension.identifier.id, extension.version);
|
||||
await this.extensionManagementService.installFromGallery(extension, {
|
||||
isMachineScoped: false /* set isMachineScoped value to prevent install and sync dialog in web */,
|
||||
donotIncludePackAndDependencies: true,
|
||||
installGivenVersion: e.pinned && !!e.version,
|
||||
installPreReleaseVersion: e.preRelease,
|
||||
profileLocation: profile.extensionsResource,
|
||||
context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true, [EXTENSION_INSTALL_SYNC_CONTEXT]: true }
|
||||
});
|
||||
this.logService.info(`${syncResourceLogLabel}: Installed extension.`, extension.identifier.id, extension.version);
|
||||
removeFromSkipped.push(extension.identifier);
|
||||
} catch (error) {
|
||||
addToSkipped.push(e);
|
||||
const results = await this.extensionManagementService.installGalleryExtensions(extensionsToInstall);
|
||||
for (const { identifier, local, error, source } of results) {
|
||||
const gallery = source as IGalleryExtension;
|
||||
if (local) {
|
||||
this.logService.info(`${syncResourceLogLabel}: Installed extension.`, identifier.id, gallery.version);
|
||||
removeFromSkipped.push(identifier);
|
||||
} else {
|
||||
const e = syncExtensionsToInstall.get(identifier.id.toLowerCase());
|
||||
if (e) {
|
||||
addToSkipped.push(e);
|
||||
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, gallery.displayName || gallery.identifier.id);
|
||||
}
|
||||
if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) {
|
||||
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id);
|
||||
} else {
|
||||
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, gallery.displayName || gallery.identifier.id);
|
||||
} else if (error) {
|
||||
this.logService.error(error);
|
||||
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -267,17 +267,17 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
|
||||
return createCancelablePromise<RecommendationsNotificationResult>(async token => {
|
||||
let accepted = false;
|
||||
const choices: (IPromptChoice | IPromptChoiceWithMenu)[] = [];
|
||||
const installExtensions = async (isMachineScoped?: boolean) => {
|
||||
const installExtensions = async (isMachineScoped: boolean) => {
|
||||
this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue));
|
||||
onDidInstallRecommendedExtensions(extensions);
|
||||
await Promises.settled<any>([
|
||||
Promises.settled(extensions.map(extension => this.extensionsWorkbenchService.open(extension, { pinned: true }))),
|
||||
this.extensionManagementService.installExtensions(extensions.map(e => e.gallery!), { isMachineScoped })
|
||||
this.extensionManagementService.installGalleryExtensions(extensions.map(e => ({ extension: e.gallery!, options: { isMachineScoped } })))
|
||||
]);
|
||||
};
|
||||
choices.push({
|
||||
label: localize('install', "Install"),
|
||||
run: () => installExtensions(),
|
||||
run: () => installExtensions(false),
|
||||
menu: this.userDataSyncEnablementService.isEnabled() && this.userDataSyncEnablementService.isResourceEnabled(SyncResource.Extensions) ? [{
|
||||
label: localize('install and do no sync', "Install (Do not sync)"),
|
||||
run: () => installExtensions(true)
|
||||
|
||||
@@ -1447,7 +1447,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView imple
|
||||
async installWorkspaceRecommendations(): Promise<void> {
|
||||
const installableRecommendations = await this.getInstallableWorkspaceRecommendations();
|
||||
if (installableRecommendations.length) {
|
||||
await this.extensionManagementService.installExtensions(installableRecommendations.map(i => i.gallery!));
|
||||
await this.extensionManagementService.installGalleryExtensions(installableRecommendations.map(i => ({ extension: i.gallery!, options: {} })));
|
||||
} else {
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
|
||||
@@ -58,9 +58,7 @@ export interface IWorkbenchExtensionManagementService extends IProfileAwareExten
|
||||
|
||||
installVSIX(location: URI, manifest: IExtensionManifest, installOptions?: InstallVSIXOptions): Promise<ILocalExtension>;
|
||||
installFromLocation(location: URI): Promise<ILocalExtension>;
|
||||
installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]>;
|
||||
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension>;
|
||||
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null;
|
||||
}
|
||||
|
||||
export const enum EnablementState {
|
||||
|
||||
+62
-29
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Event, EventMultiplexer } from 'vs/base/common/event';
|
||||
import {
|
||||
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata, InstallOperation, EXTENSION_INSTALL_SYNC_CONTEXT
|
||||
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata, InstallOperation, EXTENSION_INSTALL_SYNC_CONTEXT, InstallExtensionInfo
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { DidChangeProfileForServerEvent, DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -296,15 +296,56 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
|
||||
return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
|
||||
}
|
||||
|
||||
async installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise<ILocalExtension[]> {
|
||||
if (!installOptions) {
|
||||
const isMachineScoped = await this.hasToFlagExtensionsMachineScoped(extensions);
|
||||
installOptions = { isMachineScoped, isBuiltin: false };
|
||||
}
|
||||
return Promises.settled(extensions.map(extension => this.installFromGallery(extension, installOptions)));
|
||||
async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
|
||||
const results = new Map<string, InstallExtensionResult>();
|
||||
|
||||
const extensionsByServer = new Map<IExtensionManagementServer, InstallExtensionInfo[]>();
|
||||
await Promise.all(extensions.map(async ({ extension, options }) => {
|
||||
try {
|
||||
const servers = await this.validateAndGetExtensionManagementServersToInstall(extension, options);
|
||||
if (!options.isMachineScoped && this.isExtensionsSyncEnabled()) {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer) && (await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(extension))) {
|
||||
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
|
||||
}
|
||||
}
|
||||
for (const server of servers) {
|
||||
let exensions = extensionsByServer.get(server);
|
||||
if (!exensions) {
|
||||
extensionsByServer.set(server, exensions = []);
|
||||
}
|
||||
exensions.push({ extension, options });
|
||||
}
|
||||
} catch (error) {
|
||||
results.set(extension.identifier.id.toLowerCase(), { identifier: extension.identifier, source: extension, error, operation: InstallOperation.Install });
|
||||
}
|
||||
}));
|
||||
|
||||
await Promise.all([...extensionsByServer.entries()].map(async ([server, extensions]) => {
|
||||
const serverResults = await server.extensionManagementService.installGalleryExtensions(extensions);
|
||||
for (const result of serverResults) {
|
||||
results.set(result.identifier.id.toLowerCase(), result);
|
||||
}
|
||||
}));
|
||||
|
||||
return [...results.values()];
|
||||
}
|
||||
|
||||
async installFromGallery(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
|
||||
const servers = await this.validateAndGetExtensionManagementServersToInstall(gallery, installOptions);
|
||||
if (!installOptions || isUndefined(installOptions.isMachineScoped)) {
|
||||
const isMachineScoped = await this.hasToFlagExtensionsMachineScoped([gallery]);
|
||||
installOptions = { ...(installOptions || {}), isMachineScoped };
|
||||
}
|
||||
|
||||
if (!installOptions.isMachineScoped && this.isExtensionsSyncEnabled()) {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer) && (await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(gallery))) {
|
||||
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
|
||||
}
|
||||
}
|
||||
return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
|
||||
}
|
||||
|
||||
private async validateAndGetExtensionManagementServersToInstall(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise<IExtensionManagementServer[]> {
|
||||
|
||||
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
|
||||
if (!manifest) {
|
||||
@@ -323,32 +364,24 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
|
||||
}
|
||||
}
|
||||
|
||||
if (servers.length) {
|
||||
if (!installOptions || isUndefined(installOptions.isMachineScoped)) {
|
||||
const isMachineScoped = await this.hasToFlagExtensionsMachineScoped([gallery]);
|
||||
installOptions = { ...(installOptions || {}), isMachineScoped };
|
||||
}
|
||||
if (!installOptions.isMachineScoped && this.isExtensionsSyncEnabled()) {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer) && (await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(gallery))) {
|
||||
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
|
||||
}
|
||||
}
|
||||
if (!installOptions.context?.[EXTENSION_INSTALL_SYNC_CONTEXT]) {
|
||||
await this.checkForWorkspaceTrust(manifest);
|
||||
}
|
||||
if (!installOptions.donotIncludePackAndDependencies) {
|
||||
await this.checkInstallingExtensionOnWeb(gallery, manifest);
|
||||
}
|
||||
return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local);
|
||||
if (!servers.length) {
|
||||
const error = new Error(localize('cannot be installed', "Cannot install the '{0}' extension because it is not available in this setup.", gallery.displayName || gallery.name));
|
||||
error.name = ExtensionManagementErrorCode.Unsupported;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const error = new Error(localize('cannot be installed', "Cannot install the '{0}' extension because it is not available in this setup.", gallery.displayName || gallery.name));
|
||||
error.name = ExtensionManagementErrorCode.Unsupported;
|
||||
return Promise.reject(error);
|
||||
if (!installOptions?.context?.[EXTENSION_INSTALL_SYNC_CONTEXT]) {
|
||||
await this.checkForWorkspaceTrust(manifest);
|
||||
}
|
||||
|
||||
if (!installOptions?.donotIncludePackAndDependencies) {
|
||||
await this.checkInstallingExtensionOnWeb(gallery, manifest);
|
||||
}
|
||||
|
||||
return servers;
|
||||
}
|
||||
|
||||
getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null {
|
||||
|
||||
private getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null {
|
||||
// Only local server
|
||||
if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) {
|
||||
return this.extensionManagementServerService.localExtensionManagementServer;
|
||||
|
||||
+9
-2
@@ -11,7 +11,7 @@ import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionM
|
||||
import { IProfileAwareExtensionManagementService, IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, InstallExtensionTaskOptions, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
|
||||
import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, InstallExtensionTaskOptions, IUninstallExtensionTask, toExtensionManagementError, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
@@ -105,7 +105,14 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
|
||||
if (!manifest) {
|
||||
throw new Error(`Cannot find packageJSON from the location ${location.toString()}`);
|
||||
}
|
||||
return this.installExtension(manifest, location, options);
|
||||
const result = await this.installExtensions([{ manifest, extension: location, options }]);
|
||||
if (result[0]?.local) {
|
||||
return result[0]?.local;
|
||||
}
|
||||
if (result[0]?.error) {
|
||||
throw result[0].error;
|
||||
}
|
||||
throw toExtensionManagementError(new Error(`Unknown error while installing extension ${getGalleryExtensionId(manifest.publisher, manifest.name)}`));
|
||||
}
|
||||
|
||||
installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension> {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
@@ -143,27 +143,34 @@ export class ExtensionsResource implements IProfileResource {
|
||||
}
|
||||
}
|
||||
if (extensionsToInstall.length) {
|
||||
this.logService.info(`Importing Profile (${profile.name}): Started installing extensions.`);
|
||||
const galleryExtensions = await this.extensionGalleryService.getExtensions(extensionsToInstall.map(e => ({ ...e.identifier, version: e.version, hasPreRelease: e.version ? undefined : e.preRelease })), CancellationToken.None);
|
||||
const installExtensionInfos: InstallExtensionInfo[] = [];
|
||||
await Promise.all(extensionsToInstall.map(async e => {
|
||||
const extension = galleryExtensions.find(galleryExtension => areSameExtensions(galleryExtension.identifier, e.identifier));
|
||||
if (!extension) {
|
||||
return;
|
||||
}
|
||||
if (await this.extensionManagementService.canInstall(extension)) {
|
||||
this.logService.trace(`Importing Profile (${profile.name}): Installing extension...`, extension.identifier.id, extension.version);
|
||||
await this.extensionManagementService.installFromGallery(extension, {
|
||||
isMachineScoped: false,/* set isMachineScoped value to prevent install and sync dialog in web */
|
||||
donotIncludePackAndDependencies: true,
|
||||
installGivenVersion: !!e.version,
|
||||
installPreReleaseVersion: e.preRelease,
|
||||
profileLocation: profile.extensionsResource,
|
||||
context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true }
|
||||
installExtensionInfos.push({
|
||||
extension,
|
||||
options: {
|
||||
isMachineScoped: false,/* set isMachineScoped value to prevent install and sync dialog in web */
|
||||
donotIncludePackAndDependencies: true,
|
||||
installGivenVersion: !!e.version,
|
||||
installPreReleaseVersion: e.preRelease,
|
||||
profileLocation: profile.extensionsResource,
|
||||
context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true }
|
||||
}
|
||||
});
|
||||
this.logService.info(`Importing Profile (${profile.name}): Installed extension...`, extension.identifier.id, extension.version);
|
||||
} else {
|
||||
this.logService.info(`Importing Profile (${profile.name}): Skipped installing extension because it cannot be installed.`, extension.identifier.id);
|
||||
}
|
||||
}));
|
||||
if (installExtensionInfos.length) {
|
||||
await this.extensionManagementService.installGalleryExtensions(installExtensionInfos);
|
||||
}
|
||||
this.logService.info(`Importing Profile (${profile.name}): Finished installing extensions.`);
|
||||
}
|
||||
if (extensionsToUninstall.length) {
|
||||
await Promise.all(extensionsToUninstall.map(e => this.extensionManagementService.uninstall(e)));
|
||||
|
||||
@@ -161,8 +161,8 @@ import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService';
|
||||
import { IUserDataProfile, IUserDataProfilesService, toUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
|
||||
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { EnablementState, IExtensionManagementServer, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { InstallVSIXOptions, ILocalExtension, IGalleryExtension, InstallOptions, IExtensionIdentifier, UninstallOptions, IExtensionsControlManifest, IGalleryMetadata, IExtensionManagementParticipant, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { EnablementState, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { InstallVSIXOptions, ILocalExtension, IGalleryExtension, InstallOptions, IExtensionIdentifier, UninstallOptions, IExtensionsControlManifest, IGalleryMetadata, IExtensionManagementParticipant, Metadata, InstallExtensionResult, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IHoverOptions, IHoverService, IHoverWidget } from 'vs/workbench/services/hover/browser/hover';
|
||||
import { IRemoteExtensionsScannerService } from 'vs/platform/remote/common/remoteExtensionsScanner';
|
||||
@@ -1993,13 +1993,10 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens
|
||||
installFromLocation(location: URI): Promise<ILocalExtension> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions | undefined): Promise<ILocalExtension[]> {
|
||||
installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions | undefined): Promise<ILocalExtension> { return extension; }
|
||||
getExtensionManagementServerToInstall(manifest: Readonly<IRelaxedExtensionManifest>): IExtensionManagementServer | null {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
zip(extension: ILocalExtension): Promise<URI> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user