Path: blob/main/src/vs/workbench/contrib/extensions/electron-browser/remoteExtensionsInit.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CancellationToken } from '../../../../base/common/cancellation.js';6import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';7import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';8import { EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT, IExtensionGalleryService, IExtensionManagementService, InstallExtensionInfo } from '../../../../platform/extensionManagement/common/extensionManagement.js';9import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';10import { IFileService } from '../../../../platform/files/common/files.js';11import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';12import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';13import { ILogService } from '../../../../platform/log/common/log.js';14import { REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS } from '../../../../platform/remote/common/remote.js';15import { IRemoteAuthorityResolverService } from '../../../../platform/remote/common/remoteAuthorityResolver.js';16import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js';17import { IStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';18import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';19import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';20import { AbstractExtensionsInitializer } from '../../../../platform/userDataSync/common/extensionsSync.js';21import { IIgnoredExtensionsManagementService } from '../../../../platform/userDataSync/common/ignoredExtensions.js';22import { IRemoteUserData, IUserDataSyncEnablementService, IUserDataSyncStoreManagementService, SyncResource } from '../../../../platform/userDataSync/common/userDataSync.js';23import { UserDataSyncStoreClient } from '../../../../platform/userDataSync/common/userDataSyncStoreService.js';24import { IWorkbenchContribution } from '../../../common/contributions.js';25import { IAuthenticationService } from '../../../services/authentication/common/authentication.js';26import { IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js';27import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js';28import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';29import { IExtensionsWorkbenchService } from '../common/extensions.js';3031export class InstallRemoteExtensionsContribution implements IWorkbenchContribution {32constructor(33@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,34@IRemoteExtensionsScannerService private readonly remoteExtensionsScannerService: IRemoteExtensionsScannerService,35@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,36@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,37@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,38@ILogService private readonly logService: ILogService,39@IConfigurationService private readonly configurationService: IConfigurationService40) {41this.installExtensionsIfInstalledLocallyInRemote();42this.installFailedRemoteExtensions();43}4445private async installExtensionsIfInstalledLocallyInRemote(): Promise<void> {46if (!this.remoteAgentService.getConnection()) {47return;48}4950if (!this.extensionManagementServerService.remoteExtensionManagementServer) {51this.logService.error('No remote extension management server available');52return;53}5455if (!this.extensionManagementServerService.localExtensionManagementServer) {56this.logService.error('No local extension management server available');57return;58}5960const settingValue = this.configurationService.getValue<string[]>(REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS);61if (!settingValue?.length) {62return;63}6465const alreadyInstalledLocally = await this.extensionsWorkbenchService.queryLocal(this.extensionManagementServerService.localExtensionManagementServer);66const alreadyInstalledRemotely = await this.extensionsWorkbenchService.queryLocal(this.extensionManagementServerService.remoteExtensionManagementServer);67const extensionsToInstall = alreadyInstalledLocally68.filter(ext => settingValue.some(id => areSameExtensions(ext.identifier, { id })))69.filter(ext => !alreadyInstalledRemotely.some(e => areSameExtensions(e.identifier, ext.identifier)));707172if (!extensionsToInstall.length) {73return;74}7576await Promise.allSettled(extensionsToInstall.map(ext => {77this.extensionsWorkbenchService.installInServer(ext, this.extensionManagementServerService.remoteExtensionManagementServer!, { donotIncludePackAndDependencies: true });78}));79}8081private async installFailedRemoteExtensions(): Promise<void> {82if (!this.remoteAgentService.getConnection()) {83return;84}8586const { failed } = await this.remoteExtensionsScannerService.whenExtensionsReady();87if (failed.length === 0) {88this.logService.trace('No extensions relayed from server');89return;90}9192if (!this.extensionManagementServerService.remoteExtensionManagementServer) {93this.logService.error('No remote extension management server available');94return;95}9697this.logService.info(`Installing '${failed.length}' extensions relayed from server`);98const galleryExtensions = await this.extensionGalleryService.getExtensions(failed.map(({ id }) => ({ id })), CancellationToken.None);99const installExtensionInfo: InstallExtensionInfo[] = [];100for (const { id, installOptions } of failed) {101const extension = galleryExtensions.find(e => areSameExtensions(e.identifier, { id }));102if (extension) {103installExtensionInfo.push({104extension, options: {105...installOptions,106downloadExtensionsLocally: true,107}108});109} else {110this.logService.warn(`Relayed failed extension '${id}' from server is not found in the gallery`);111}112}113114if (installExtensionInfo.length) {115await Promise.allSettled(installExtensionInfo.map(e => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(e.extension, e.options)));116}117}118}119120export class RemoteExtensionsInitializerContribution implements IWorkbenchContribution {121constructor(122@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,123@IStorageService private readonly storageService: IStorageService,124@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,125@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,126@IInstantiationService private readonly instantiationService: IInstantiationService,127@ILogService private readonly logService: ILogService,128@IAuthenticationService private readonly authenticationService: IAuthenticationService,129@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,130@IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,131) {132this.initializeRemoteExtensions();133}134135private async initializeRemoteExtensions(): Promise<void> {136const connection = this.remoteAgentService.getConnection();137const localExtensionManagementServer = this.extensionManagementServerService.localExtensionManagementServer;138const remoteExtensionManagementServer = this.extensionManagementServerService.remoteExtensionManagementServer;139// Skip: Not a remote window140if (!connection || !remoteExtensionManagementServer) {141return;142}143// Skip: Not a native window144if (!localExtensionManagementServer) {145return;146}147// Skip: No UserdataSyncStore is configured148if (!this.userDataSyncStoreManagementService.userDataSyncStore) {149return;150}151const newRemoteConnectionKey = `${IS_NEW_KEY}.${connection.remoteAuthority}`;152// Skip: Not a new remote connection153if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.APPLICATION, true)) {154this.logService.trace(`Skipping initializing remote extensions because the window with this remote authority was opened before.`);155return;156}157this.storageService.store(newRemoteConnectionKey, false, StorageScope.APPLICATION, StorageTarget.MACHINE);158// Skip: Not a new workspace159if (!this.storageService.isNew(StorageScope.WORKSPACE)) {160this.logService.trace(`Skipping initializing remote extensions because this workspace was opened before.`);161return;162}163// Skip: Settings Sync is disabled164if (!this.userDataSyncEnablementService.isEnabled()) {165return;166}167// Skip: No account is provided to initialize168const resolvedAuthority = await this.remoteAuthorityResolverService.resolveAuthority(connection.remoteAuthority);169if (!resolvedAuthority.options?.authenticationSession) {170return;171}172173const sessions = await this.authenticationService.getSessions(resolvedAuthority.options?.authenticationSession.providerId);174const session = sessions.find(s => s.id === resolvedAuthority.options?.authenticationSession?.id);175// Skip: Session is not found176if (!session) {177this.logService.info('Skipping initializing remote extensions because the account with given session id is not found', resolvedAuthority.options.authenticationSession.id);178return;179}180181const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, this.userDataSyncStoreManagementService.userDataSyncStore.url);182userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.authenticationSession.providerId);183const userData = await userDataSyncStoreClient.readResource(SyncResource.Extensions, null);184185const serviceCollection = new ServiceCollection();186serviceCollection.set(IExtensionManagementService, remoteExtensionManagementServer.extensionManagementService);187const instantiationService = this.instantiationService.createChild(serviceCollection);188const extensionsToInstallInitializer = instantiationService.createInstance(RemoteExtensionsInitializer);189190await extensionsToInstallInitializer.initialize(userData);191}192}193194class RemoteExtensionsInitializer extends AbstractExtensionsInitializer {195196constructor(197@IExtensionManagementService extensionManagementService: IExtensionManagementService,198@IIgnoredExtensionsManagementService ignoredExtensionsManagementService: IIgnoredExtensionsManagementService,199@IFileService fileService: IFileService,200@IUserDataProfilesService userDataProfilesService: IUserDataProfilesService,201@IEnvironmentService environmentService: IEnvironmentService,202@ILogService logService: ILogService,203@IUriIdentityService uriIdentityService: IUriIdentityService,204@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,205@IStorageService storageService: IStorageService,206@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,207) {208super(extensionManagementService, ignoredExtensionsManagementService, fileService, userDataProfilesService, environmentService, logService, storageService, uriIdentityService);209}210211protected override async doInitialize(remoteUserData: IRemoteUserData): Promise<void> {212const remoteExtensions = await this.parseExtensions(remoteUserData);213if (!remoteExtensions) {214this.logService.info('No synced extensions exist while initializing remote extensions.');215return;216}217const installedExtensions = await this.extensionManagementService.getInstalled();218const { newExtensions } = this.generatePreview(remoteExtensions, installedExtensions);219if (!newExtensions.length) {220this.logService.trace('No new remote extensions to install.');221return;222}223const targetPlatform = await this.extensionManagementService.getTargetPlatform();224const extensionsToInstall = await this.extensionGalleryService.getExtensions(newExtensions, { targetPlatform, compatible: true }, CancellationToken.None);225if (extensionsToInstall.length) {226await Promise.allSettled(extensionsToInstall.map(async e => {227const manifest = await this.extensionGalleryService.getManifest(e, CancellationToken.None);228if (manifest && this.extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) {229const syncedExtension = remoteExtensions.find(e => areSameExtensions(e.identifier, e.identifier));230await this.extensionManagementService.installFromGallery(e, { installPreReleaseVersion: syncedExtension?.preRelease, donotIncludePackAndDependencies: true, context: { [EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT]: true } });231}232}));233}234}235}236237238