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