diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index ecf80672720..6c43f9252d3 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -19,6 +19,7 @@ export interface ResolvedAuthority { export interface ResolvedOptions { readonly extensionHostEnv?: { [key: string]: string | null }; readonly isTrusted?: boolean; + readonly initializeUsingAccount?: { providerId: string, sessionId: string }; } export interface TunnelDescription { diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index d6159097603..47af2647f89 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -21,6 +21,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -835,7 +836,7 @@ export abstract class AbstractInitializer implements IUserDataInitializer { constructor( readonly resource: SyncResource, @IEnvironmentService protected readonly environmentService: IEnvironmentService, - @IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService, + @ILogService protected readonly logService: ILogService, @IFileService protected readonly fileService: IFileService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { @@ -854,12 +855,6 @@ export abstract class AbstractInitializer implements IUserDataInitializer { return; } - const isPreviouslySynced = await this.fileService.exists(this.lastSyncResource); - if (isPreviouslySynced) { - this.logService.info('Remote content does not exist.', this.resource); - return; - } - try { await this.doInitialize({ ref, syncData }); } catch (error) { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 1bdce0eb1b6..3e56cc3875a 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -18,6 +18,7 @@ import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtension import { areSameExtensions, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -506,7 +507,7 @@ export abstract class AbstractExtensionsInitializer extends AbstractInitializer @IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, - @IUserDataSyncLogService logService: IUserDataSyncLogService, + @ILogService logService: ILogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { super(SyncResource.Extensions, environmentService, logService, fileService, uriIdentityService); diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts index 61ded27f026..55cb5746c3c 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts @@ -23,6 +23,7 @@ import { IExtensionRecommendationNotificationService } from 'vs/platform/extensi import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; import { Codicon } from 'vs/base/common/codicons'; +import { RemoteExtensionsInitializerContribution } from 'vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit'; // Running Extensions Editor Registry.as(EditorExtensions.EditorPane).registerEditorPane( @@ -60,6 +61,7 @@ class ExtensionsContributions implements IWorkbenchContribution { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); // Register Commands diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts new file mode 100644 index 00000000000..746deed82ea --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { AbstractExtensionsInitializer } from 'vs/platform/userDataSync/common/extensionsSync'; +import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { IRemoteUserData, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + +export class RemoteExtensionsInitializerContribution implements IWorkbenchContribution { + constructor( + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IStorageService private readonly storageService: IStorageService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + ) { + this.initializeRemoteExtensions(); + } + + private async initializeRemoteExtensions(): Promise { + const connection = this.remoteAgentService.getConnection(); + const localExtensionManagementServer = this.extensionManagementServerService.localExtensionManagementServer; + const remoteExtensionManagementServer = this.extensionManagementServerService.remoteExtensionManagementServer; + // Skip: Not a remote window + if (!connection || !remoteExtensionManagementServer) { + return; + } + // Skip: Not a native window + if (!localExtensionManagementServer) { + return; + } + // Skip: No UserdataSyncStore is configured + if (!this.userDataSyncStoreManagementService.userDataSyncStore) { + return; + } + const newRemoteConnectionKey = `${IS_NEW_KEY}.${connection.remoteAuthority}`; + // Skip: Not a new remote connection + if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.GLOBAL, true)) { + this.logService.trace(`Skipping initializing remote extensions because the window with this remote authority was opened before.`); + return; + } + this.storageService.store(newRemoteConnectionKey, false, StorageScope.GLOBAL, StorageTarget.MACHINE); + // Skip: Not a new workspace + if (!this.storageService.isNew(StorageScope.WORKSPACE)) { + this.logService.trace(`Skipping initializing remote extensions because this workspace was opened before.`); + return; + } + // Skip: No account is provided to initialize + const resolvedAuthority = await this.remoteAuthorityResolverService.resolveAuthority(connection.remoteAuthority); + if (!resolvedAuthority.options?.initializeUsingAccount) { + return; + } + + const sessions = await this.authenticationService.getSessions(resolvedAuthority.options?.initializeUsingAccount.providerId); + const session = sessions.find(s => s.id === resolvedAuthority.options?.initializeUsingAccount?.sessionId); + // Skip: Session is not found + if (!session) { + this.logService.info('Skipping initializing remote extensions because the account with given session id is not found', resolvedAuthority.options.initializeUsingAccount.sessionId); + return; + } + + const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, this.userDataSyncStoreManagementService.userDataSyncStore.url); + userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.initializeUsingAccount.providerId); + const userData = await userDataSyncStoreClient.read(SyncResource.Extensions, null); + + const serviceCollection = new ServiceCollection(); + serviceCollection.set(IExtensionManagementService, remoteExtensionManagementServer.extensionManagementService); + const instantiationService = this.instantiationService.createChild(serviceCollection); + const extensionsToInstallInitializer = instantiationService.createInstance(RemoteExtensionsInitializer); + + await extensionsToInstallInitializer.initialize(userData); + } +} + +class RemoteExtensionsInitializer extends AbstractExtensionsInitializer { + + constructor( + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IIgnoredExtensionsManagementService ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, + @IFileService fileService: IFileService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, + ) { + super(extensionManagementService, ignoredExtensionsManagementService, fileService, environmentService, logService, uriIdentityService); + } + + protected override async doInitialize(remoteUserData: IRemoteUserData): Promise { + const remoteExtensions = await this.parseExtensions(remoteUserData); + if (!remoteExtensions) { + this.logService.info('No synced extensions exist while initializing remote extensions.'); + return; + } + const installedExtensions = await this.extensionManagementService.getInstalled(); + const { newExtensions } = this.generatePreview(remoteExtensions, installedExtensions); + if (!newExtensions.length) { + this.logService.trace('No new remote extensions to install.'); + return; + } + const extensionsToInstall = await this.extensionGalleryService.getExtensions(newExtensions, CancellationToken.None); + if (extensionsToInstall.length) { + await Promise.allSettled(extensionsToInstall.map(async e => { + const manifest = await this.extensionGalleryService.getManifest(e, CancellationToken.None); + if (manifest && this.extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) { + await this.extensionManagementService.installFromGallery(e); + } + })); + } + } +} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 39cf934c361..dd1dd6a6864 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -122,7 +122,10 @@ import { OpenerService } from 'vs/editor/browser/services/openerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; +import { IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService); registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService); registerSingleton(IExtensionsStorageSyncService, ExtensionsStorageSyncService); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index a595a059d78..ea8d00c999b 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -72,8 +72,7 @@ import { ExtensionManagementService } from 'vs/workbench/services/extensionManag import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLog'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; -import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -94,7 +93,6 @@ registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementServi registerSingleton(IAccessibilityService, AccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ILoggerService, FileLoggerService); -registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService);