@@ -226,65 +226,74 @@ export abstract class DotnetTool implements IDotnetTool {
226226 return path . normalize ( workDir )
227227 }
228228
229- private async getQueryService ( ) : Promise < string | null > {
229+ private async getQueryServices ( ) : Promise < string [ ] > {
230230 // Use dotnet tool to get the first enabled nuget source.
231231 const builder = new ArgumentsBuilder ( ) . addArgument ( 'nuget' ) . addArgument ( 'list' ) . addArgument ( 'source' ) . addKeyValue ( 'format' , 'short' )
232232 const result = await this . execute ( 'dotnet' , builder . build ( ) )
233233
234234 // Each line of the output starts with either E (enabled) or D (disabled), followed by a space and index url.
235- const defaultNugetSource = / E (?< index > .+ ) / . exec ( result . stdout ?? '' ) ? .groups ? .index
235+ const nugetSources = [ ... ( result . stdout ?? '' ) . matchAll ( / ^ E (?< index > .+ ) / gm ) ] . map ( m => m . groups ! . index )
236236
237- if ( ! defaultNugetSource ) {
237+ if ( ! nugetSources . length ) {
238238 this . buildAgent . error ( 'Failed to fetch an enabled package source for dotnet.' )
239- return null
239+ return [ ]
240240 }
241241
242- // Fetch the nuget source index to obtain the query service
243- const nugetIndex = await fetch ( defaultNugetSource )
244- if ( ! nugetIndex ?. ok ) {
245- this . buildAgent . error ( `Failed to fetch data from NuGet source ${ defaultNugetSource } .` )
246- return null
247- }
242+ const sources : string [ ] = [ ]
243+ for ( const nugetSource of nugetSources ) {
244+ // Fetch the nuget source index to obtain the query service
245+ const nugetIndex = await fetch ( nugetSource )
246+ if ( ! nugetIndex ?. ok ) {
247+ this . buildAgent . warn ( `Failed to fetch data from NuGet source ${ nugetSource } .` )
248+ continue
249+ }
248250
249- // Parse the nuget service index and get the (first / primary) query service
250- const resources = ( ( await nugetIndex . json ( ) ) as NugetServiceIndex ) ?. resources
251- const serviceUrl = resources ?. find ( s => s [ '@type' ] . startsWith ( NugetServiceType . SearchQueryService ) ) ?. [ '@id' ]
251+ // Parse the nuget service index and get the (first / primary) query service
252+ const resources = ( ( await nugetIndex . json ( ) ) as NugetServiceIndex ) ?. resources
253+ const serviceUrl = resources ?. find ( s => s [ '@type' ] . startsWith ( NugetServiceType . SearchQueryService ) ) ?. [ '@id' ]
252254
253- if ( ! serviceUrl ) {
254- this . buildAgent . error ( `Could not find a ${ NugetServiceType . SearchQueryService } in NuGet source ${ defaultNugetSource } ` )
255- return null
255+ if ( ! serviceUrl ) {
256+ this . buildAgent . warn ( `Could not find a ${ NugetServiceType . SearchQueryService } in NuGet source ${ nugetSource } ` )
257+ continue
258+ }
259+ sources . push ( serviceUrl )
256260 }
257- return serviceUrl
261+ return sources
258262 }
259263
260- private async queryLatestMatch ( toolName : string , versionSpec : string , includePrerelease : boolean ) : Promise < string | null > {
261- this . buildAgent . info (
262- `Querying tool versions for ${ toolName } ${ versionSpec ? `@${ versionSpec } ` : '' } ${ includePrerelease ? 'including pre-releases' : '' } `
263- )
264-
265- const queryService = await this . getQueryService ( )
266- if ( ! queryService ) {
267- return null
268- }
269-
264+ private async queryVersionsFromNugetSource ( serviceUrl : string , toolName : string , includePrerelease : boolean ) : Promise < string [ ] > {
270265 const toolNameParam = encodeURIComponent ( toolName . toLowerCase ( ) )
271266 const prereleaseParam = includePrerelease ? 'true' : 'false'
272- const downloadPath = `${ queryService } ?q=${ toolNameParam } &prerelease=${ prereleaseParam } &semVerLevel=2.0.0&take=1`
267+ const downloadPath = `${ serviceUrl } ?q=${ toolNameParam } &prerelease=${ prereleaseParam } &semVerLevel=2.0.0&take=1`
273268
274269 const response = await fetch ( downloadPath )
275270
276271 if ( ! response || ! response . ok ) {
277- this . buildAgent . info ( `failed to query latest version for ${ toolName } from ${ downloadPath } . Status code: ${ response ? response . status : 'unknown' } ` )
278- return null
272+ this . buildAgent . warn ( `failed to query latest version for ${ toolName } from ${ downloadPath } . Status code: ${ response ? response . status : 'unknown' } ` )
273+ return [ ]
279274 }
280-
281275 const { data } = ( await response . json ( ) ) as NugetVersions
282276
283277 const versions = data [ 0 ] . versions . map ( x => x . version )
284- if ( ! versions || ! versions . length ) {
278+
279+ return versions ?? [ ]
280+ }
281+
282+ private async queryLatestMatch ( toolName : string , versionSpec : string , includePrerelease : boolean ) : Promise < string | null > {
283+ this . buildAgent . info (
284+ `Querying tool versions for ${ toolName } ${ versionSpec ? `@${ versionSpec } ` : '' } ${ includePrerelease ? 'including pre-releases' : '' } `
285+ )
286+
287+ const queryServices = await this . getQueryServices ( )
288+ if ( ! queryServices . length ) {
285289 return null
286290 }
287291
292+ let versions = (
293+ await Promise . all ( queryServices . map ( async service => await this . queryVersionsFromNugetSource ( service , toolName , includePrerelease ) ) )
294+ ) . flat ( )
295+ versions = [ ...new Set ( versions ) ] // remove duplicates
296+
288297 this . buildAgent . debug ( `got versions: ${ versions . join ( ', ' ) } ` )
289298
290299 const version = semver . maxSatisfying ( versions , versionSpec , { includePrerelease } )
0 commit comments