@@ -16,16 +16,17 @@ import * as nls from 'vs/nls';
16
16
import {
17
17
ExtensionManagementError , IExtensionGalleryService , IExtensionIdentifier , IExtensionManagementParticipant , IGalleryExtension , ILocalExtension , InstallOperation ,
18
18
IExtensionsControlManifest , StatisticType , isTargetPlatformCompatible , TargetPlatformToString , ExtensionManagementErrorCode ,
19
- InstallOptions , InstallVSIXOptions , UninstallOptions , Metadata , InstallExtensionEvent , DidUninstallExtensionEvent , InstallExtensionResult , UninstallExtensionEvent , IExtensionManagementService
19
+ InstallOptions , InstallVSIXOptions , UninstallOptions , Metadata , InstallExtensionEvent , DidUninstallExtensionEvent , InstallExtensionResult , UninstallExtensionEvent , IExtensionManagementService , InstallExtensionInfo
20
20
} from 'vs/platform/extensionManagement/common/extensionManagement' ;
21
- import { areSameExtensions , ExtensionKey , getGalleryExtensionTelemetryData , getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
21
+ import { areSameExtensions , ExtensionKey , getGalleryExtensionId , getGalleryExtensionTelemetryData , getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
22
22
import { ExtensionType , IExtensionManifest , isApplicationScopedExtension , TargetPlatform } from 'vs/platform/extensions/common/extensions' ;
23
23
import { ILogService } from 'vs/platform/log/common/log' ;
24
24
import { IProductService } from 'vs/platform/product/common/productService' ;
25
25
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
26
26
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' ;
27
27
28
28
export type ExtensionVerificationStatus = boolean | string ;
29
+ export type InstallableExtension = { readonly manifest : IExtensionManifest ; extension : IGalleryExtension | URI ; options : InstallOptions & InstallVSIXOptions } ;
29
30
30
31
export type InstallExtensionTaskOptions = InstallOptions & InstallVSIXOptions & { readonly profileLocation : URI } ;
31
32
export interface IInstallExtensionTask {
@@ -93,19 +94,54 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
93
94
94
95
async installFromGallery ( extension : IGalleryExtension , options : InstallOptions = { } ) : Promise < ILocalExtension > {
95
96
try {
96
- if ( ! this . galleryService . isEnabled ( ) ) {
97
- throw new ExtensionManagementError ( nls . localize ( 'MarketPlaceDisabled' , "Marketplace is not enabled" ) , ExtensionManagementErrorCode . Internal ) ;
97
+ const results = await this . installGalleryExtensions ( [ { extension, options } ] ) ;
98
+ const result = results . find ( ( { identifier } ) => areSameExtensions ( identifier , extension . identifier ) ) ;
99
+ if ( result ?. local ) {
100
+ return result ?. local ;
98
101
}
99
- const compatible = await this . checkAndGetCompatibleVersion ( extension , ! ! options . installGivenVersion , ! ! options . installPreReleaseVersion ) ;
100
- return await this . installExtension ( compatible . manifest , compatible . extension , options ) ;
102
+ if ( result ?. error ) {
103
+ throw result . error ;
104
+ }
105
+ throw toExtensionManagementError ( new Error ( `Unknown error while installing extension ${ extension . identifier . id } ` ) ) ;
101
106
} catch ( error ) {
102
- reportTelemetry ( this . telemetryService , 'extensionGallery:install' , { extensionData : getGalleryExtensionTelemetryData ( extension ) , error } ) ;
103
- this . logService . error ( `Failed to install extension.` , extension . identifier . id ) ;
104
- this . logService . error ( error ) ;
105
107
throw toExtensionManagementError ( error ) ;
106
108
}
107
109
}
108
110
111
+ async installGalleryExtensions ( extensions : InstallExtensionInfo [ ] ) : Promise < InstallExtensionResult [ ] > {
112
+ if ( ! this . galleryService . isEnabled ( ) ) {
113
+ throw new ExtensionManagementError ( nls . localize ( 'MarketPlaceDisabled' , "Marketplace is not enabled" ) , ExtensionManagementErrorCode . Internal ) ;
114
+ }
115
+
116
+ const results : InstallExtensionResult [ ] = [ ] ;
117
+ const installableExtensions : InstallableExtension [ ] = [ ] ;
118
+
119
+ await Promise . allSettled ( extensions . map ( async ( { extension, options } ) => {
120
+ try {
121
+ const compatible = await this . checkAndGetCompatibleVersion ( extension , ! ! options ?. installGivenVersion , ! ! options ?. installPreReleaseVersion ) ;
122
+ installableExtensions . push ( { ...compatible , options } ) ;
123
+ } catch ( error ) {
124
+ results . push ( { identifier : extension . identifier , operation : InstallOperation . Install , source : extension , error } ) ;
125
+ }
126
+ } ) ) ;
127
+
128
+ if ( installableExtensions . length ) {
129
+ results . push ( ...await this . installExtensions ( installableExtensions ) ) ;
130
+ }
131
+
132
+ for ( const result of results ) {
133
+ if ( result . error ) {
134
+ this . logService . error ( `Failed to install extension.` , result . identifier . id ) ;
135
+ this . logService . error ( result . error ) ;
136
+ if ( result . source && ! URI . isUri ( result . source ) ) {
137
+ reportTelemetry ( this . telemetryService , 'extensionGallery:install' , { extensionData : getGalleryExtensionTelemetryData ( result . source ) , error : result . error } ) ;
138
+ }
139
+ }
140
+ }
141
+
142
+ return results ;
143
+ }
144
+
109
145
async uninstall ( extension : ILocalExtension , options : UninstallOptions = { } ) : Promise < void > {
110
146
this . logService . trace ( 'ExtensionManagementService#uninstall' , extension . identifier . id ) ;
111
147
return this . uninstallExtension ( extension , options ) ;
@@ -126,7 +162,21 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
126
162
this . participants . push ( participant ) ;
127
163
}
128
164
129
- protected async installExtension ( manifest : IExtensionManifest , extension : URI | IGalleryExtension , options : InstallOptions & InstallVSIXOptions ) : Promise < ILocalExtension > {
165
+ protected async installExtensions ( extensions : InstallableExtension [ ] ) : Promise < InstallExtensionResult [ ] > {
166
+ const results : InstallExtensionResult [ ] = [ ] ;
167
+ await Promise . allSettled ( extensions . map ( async e => {
168
+ try {
169
+ const result = await this . installExtension ( e ) ;
170
+ results . push ( ...result ) ;
171
+ } catch ( error ) {
172
+ results . push ( { identifier : { id : getGalleryExtensionId ( e . manifest . publisher , e . manifest . name ) } , operation : InstallOperation . Install , source : e . extension , error } ) ;
173
+ }
174
+ } ) ) ;
175
+ this . _onDidInstallExtensions . fire ( results ) ;
176
+ return results ;
177
+ }
178
+
179
+ private async installExtension ( { manifest, extension, options } : InstallableExtension ) : Promise < InstallExtensionResult [ ] > {
130
180
131
181
const isApplicationScoped = options . isApplicationScoped || options . isBuiltin || isApplicationScopedExtension ( manifest ) ;
132
182
const installExtensionTaskOptions : InstallExtensionTaskOptions = {
@@ -142,7 +192,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
142
192
const installingExtension = this . installingExtensions . get ( getInstallExtensionTaskKey ( extension ) ) ;
143
193
if ( installingExtension ) {
144
194
this . logService . info ( 'Extensions is already requested to install' , extension . identifier . id ) ;
145
- return installingExtension . task . waitUntilTaskIsFinished ( ) ;
195
+ await installingExtension . task . waitUntilTaskIsFinished ( ) ;
196
+ return [ ] ;
146
197
}
147
198
}
148
199
@@ -272,8 +323,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
272
323
}
273
324
274
325
installResults . forEach ( ( { identifier } ) => this . logService . info ( `Extension installed successfully:` , identifier . id ) ) ;
275
- this . _onDidInstallExtensions . fire ( installResults ) ;
276
- return installResults . filter ( ( { identifier } ) => areSameExtensions ( identifier , installExtensionTask . identifier ) ) [ 0 ] . local ;
326
+ return installResults ;
277
327
278
328
} catch ( error ) {
279
329
@@ -299,8 +349,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
299
349
}
300
350
}
301
351
302
- this . _onDidInstallExtensions . fire ( allInstallExtensionTasks . map ( ( { task } ) => ( { identifier : task . identifier , operation : InstallOperation . Install , source : task . source , context : installExtensionTaskOptions . context , profileLocation : installExtensionTaskOptions . profileLocation } ) ) ) ;
303
- throw error ;
352
+ return allInstallExtensionTasks . map ( ( { task } ) => ( { identifier : task . identifier , operation : InstallOperation . Install , source : task . source , context : installExtensionTaskOptions . context , profileLocation : installExtensionTaskOptions . profileLocation , error } ) ) ;
304
353
} finally {
305
354
// Finally, remove all the tasks from the cache
306
355
for ( const { task } of allInstallExtensionTasks ) {
@@ -678,7 +727,7 @@ export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | stri
678
727
} , new Error ( '' ) ) ;
679
728
}
680
729
681
- function toExtensionManagementError ( error : Error ) : ExtensionManagementError {
730
+ export function toExtensionManagementError ( error : Error ) : ExtensionManagementError {
682
731
if ( error instanceof ExtensionManagementError ) {
683
732
return error ;
684
733
}
0 commit comments