@@ -30,6 +30,8 @@ internal sealed partial class NugetPackageRestorer : IDisposable
3030 private readonly DependencyDirectory missingPackageDirectory ;
3131 private readonly ILogger logger ;
3232 private readonly ICompilationInfoContainer compilationInfoContainer ;
33+ private HashSet < string > PrivateRegistryFeeds => dependabotProxy ? . RegistryURLs ?? [ ] ;
34+ private bool HasPrivateRegistryFeeds => PrivateRegistryFeeds . Any ( ) ;
3335
3436 public DependencyDirectory PackageDirectory { get ; }
3537
@@ -114,34 +116,32 @@ public HashSet<AssemblyLookupLocation> Restore()
114116 logger . LogInfo ( $ "Checking NuGet feed responsiveness: { checkNugetFeedResponsiveness } ") ;
115117 compilationInfoContainer . CompilationInfos . Add ( ( "NuGet feed responsiveness checked" , checkNugetFeedResponsiveness ? "1" : "0" ) ) ;
116118
117- HashSet < string > ? explicitFeeds = null ;
118- HashSet < string > ? allFeeds = null ;
119- HashSet < string > ? reachableFeeds = [ ] ;
119+ HashSet < string > explicitFeeds = [ ] ;
120+ string ? explicitRestoreSources = null ;
120121
121122 try
122123 {
124+ HashSet < string > reachableFeeds = [ ] ;
125+ HashSet < string > allFeeds = [ ] ;
126+
127+ // Find feeds that are configured in NuGet.config files and divide them into ones that
128+ // are explicitly configured for the project or by a private registry, and "all feeds"
129+ // (including inherited ones) from other locations on the host outside of the working directory.
130+ ( explicitFeeds , allFeeds ) = GetAllFeeds ( ) ;
131+
123132 if ( checkNugetFeedResponsiveness )
124133 {
125- // Find feeds that are configured in NuGet.config files and divide them into ones that
126- // are explicitly configured for the project, and "all feeds" (including inherited ones)
127- // from other locations on the host outside of the working directory.
128- ( explicitFeeds , allFeeds ) = GetAllFeeds ( ) ;
129134 var inheritedFeeds = allFeeds . Except ( explicitFeeds ) . ToHashSet ( ) ;
130135
131- // If private package registries are configured for C#, then consider those
132- // in addition to the ones that are configured in `nuget.config` files.
133- dependabotProxy ? . RegistryURLs . ForEach ( url => explicitFeeds . Add ( url ) ) ;
134-
135- var ( explicitFeedsReachable , reachableExplicitFeeds ) = CheckSpecifiedFeeds ( explicitFeeds ) ;
136- reachableFeeds . UnionWith ( reachableExplicitFeeds ) ;
137-
138- reachableFeeds . UnionWith ( GetReachableNuGetFeeds ( inheritedFeeds , isFallback : false ) ) ;
139-
140136 if ( inheritedFeeds . Count > 0 )
141137 {
142138 compilationInfoContainer . CompilationInfos . Add ( ( "Inherited NuGet feed count" , inheritedFeeds . Count . ToString ( ) ) ) ;
143139 }
144140
141+ var explicitFeedsReachable = CheckSpecifiedFeeds ( explicitFeeds , out var reachableExplicitFeeds ) ;
142+ reachableFeeds . UnionWith ( reachableExplicitFeeds ) ;
143+ reachableFeeds . UnionWith ( GetReachableNuGetFeeds ( inheritedFeeds , isFallback : false ) ) ;
144+
145145 if ( ! explicitFeedsReachable )
146146 {
147147 // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
@@ -150,6 +150,16 @@ public HashSet<AssemblyLookupLocation> Restore()
150150 ? [ ]
151151 : [ unresponsiveMissingPackageLocation ] ;
152152 }
153+
154+ // If feed responsiveness is being checked, we only want to use the feeds that are reachable (note this set includes private
155+ // registry feeds if they are reachable).
156+ explicitRestoreSources = MakeRestoreSourcesArgument ( reachableFeeds ) ;
157+ }
158+ else if ( HasPrivateRegistryFeeds )
159+ {
160+ // If private registries are configured they need to be included as sources for the restore, which requires that
161+ // they are provided as source arguments for the restore. The private registries are included in the `allFeeds` set.
162+ explicitRestoreSources = MakeRestoreSourcesArgument ( allFeeds ) ;
153163 }
154164
155165 using ( var nuget = new NugetExeWrapper ( fileProvider , legacyPackageDirectory , logger ) )
@@ -192,10 +202,9 @@ public HashSet<AssemblyLookupLocation> Restore()
192202 }
193203
194204 // Restore project dependencies with `dotnet restore`.
195- // TODO: We also need to respect reachable feeds for resolution restore.
196- var restoredProjects = RestoreSolutions ( out var container ) ;
205+ var restoredProjects = RestoreSolutions ( explicitRestoreSources , out var container ) ;
197206 var projects = fileProvider . Projects . Except ( restoredProjects ) ;
198- RestoreProjects ( projects , reachableFeeds , out var containers ) ;
207+ RestoreProjects ( projects , explicitRestoreSources , out var containers ) ;
199208
200209 var dependencies = containers . Flatten ( container ) ;
201210
@@ -277,7 +286,7 @@ private List<string> GetReachableFallbackNugetFeeds(HashSet<string>? feedsFromNu
277286 /// Populates dependencies with the relevant dependencies from the assets files generated by the restore.
278287 /// Returns a list of projects that are up to date with respect to restore.
279288 /// </summary>
280- private IEnumerable < string > RestoreSolutions ( out DependencyContainer dependencies )
289+ private IEnumerable < string > RestoreSolutions ( string ? explicitRestoreSources , out DependencyContainer dependencies )
281290 {
282291 var successCount = 0 ;
283292 var nugetSourceFailures = 0 ;
@@ -288,7 +297,7 @@ private IEnumerable<string> RestoreSolutions(out DependencyContainer dependencie
288297 var projects = fileProvider . Solutions . SelectMany ( solution =>
289298 {
290299 logger . LogInfo ( $ "Restoring solution { solution } ...") ;
291- var res = dotnet . Restore ( new ( solution , PackageDirectory . DirInfo . FullName , ForceDotnetRefAssemblyFetching : true , TargetWindows : isWindows ) ) ;
300+ var res = dotnet . Restore ( new ( solution , PackageDirectory . DirInfo . FullName , ForceDotnetRefAssemblyFetching : true , ExtraArgs : explicitRestoreSources , TargetWindows : isWindows ) ) ;
292301 if ( res . Success )
293302 {
294303 successCount ++ ;
@@ -307,39 +316,28 @@ private IEnumerable<string> RestoreSolutions(out DependencyContainer dependencie
307316 return projects ;
308317 }
309318
319+ private string ? MakeRestoreSourcesArgument ( IEnumerable < string > feeds )
320+ {
321+ // Add package sources. If any are present, they override all sources specified in
322+ // the configuration file(s).
323+ var feedArgs = new StringBuilder ( ) ;
324+ foreach ( var feed in feeds )
325+ {
326+ feedArgs . Append ( $ " -s { feed } ") ;
327+ }
328+
329+ return feedArgs . ToString ( ) ;
330+ }
331+
310332 /// <summary>
311333 /// Executes `dotnet restore` on all projects in projects.
312334 /// This is done in parallel for performance reasons.
313335 /// Populates dependencies with the relative paths to the assets files generated by the restore.
314336 /// </summary>
315337 /// <param name="projects">A list of paths to project files.</param>
316- private void RestoreProjects ( IEnumerable < string > projects , HashSet < string > ? configuredSources , out ConcurrentBag < DependencyContainer > dependencies )
317- {
318- // Conservatively, we only set this to a non-null value if a Dependabot proxy is enabled.
319- // This ensures that we continue to get the old behaviour where feeds are taken from
320- // `nuget.config` files instead of the command-line arguments.
321- string ? extraArgs = null ;
322-
323- if ( dependabotProxy is not null )
324- {
325- // If the Dependabot proxy is configured, then our main goal is to make `dotnet` aware
326- // of the private registry feeds. However, since providing them as command-line arguments
327- // to `dotnet` ignores other feeds that may be configured, we also need to add the feeds
328- // we have discovered from analysing `nuget.config` files.
329- var sources = configuredSources ?? new ( ) ;
330- dependabotProxy . RegistryURLs . ForEach ( url => sources . Add ( url ) ) ;
331-
332- // Add package sources. If any are present, they override all sources specified in
333- // the configuration file(s).
334- var feedArgs = new StringBuilder ( ) ;
335- foreach ( string source in sources )
336- {
337- feedArgs . Append ( $ " -s { source } ") ;
338- }
339-
340- extraArgs = feedArgs . ToString ( ) ;
341- }
342-
338+ /// <param name="explicitRestoreSources">The explicit restore sources argument.</param>
339+ private void RestoreProjects ( IEnumerable < string > projects , string ? explicitRestoreSources , out ConcurrentBag < DependencyContainer > dependencies )
340+ {
343341 var successCount = 0 ;
344342 var nugetSourceFailures = 0 ;
345343 ConcurrentBag < DependencyContainer > collectedDependencies = [ ] ;
@@ -354,7 +352,7 @@ private void RestoreProjects(IEnumerable<string> projects, HashSet<string>? conf
354352 foreach ( var project in projectGroup )
355353 {
356354 logger . LogInfo ( $ "Restoring project { project } ...") ;
357- var res = dotnet . Restore ( new ( project , PackageDirectory . DirInfo . FullName , ForceDotnetRefAssemblyFetching : true , extraArgs , TargetWindows : isWindows ) ) ;
355+ var res = dotnet . Restore ( new ( project , PackageDirectory . DirInfo . FullName , ForceDotnetRefAssemblyFetching : true , ExtraArgs : explicitRestoreSources , TargetWindows : isWindows ) ) ;
358356 assets . AddDependenciesRange ( res . AssetsFilePaths ) ;
359357 lock ( sync )
360358 {
@@ -780,20 +778,20 @@ private HashSet<string> GetExcludedFeeds()
780778 /// <param name="feeds">The set of package feeds to check.</param>
781779 /// <returns>
782780 /// True if all feeds are reachable or false otherwise.
783- /// Also returns the list of reachable feeds.
781+ /// Also returns the list of reachable feeds as an out parameter .
784782 /// </returns>
785- private ( bool , List < string > ) CheckSpecifiedFeeds ( HashSet < string > feeds )
783+ private bool CheckSpecifiedFeeds ( HashSet < string > feeds , out List < string > reachableFeeds )
786784 {
787785 // Exclude any feeds that are configured by the corresponding environment variable.
788786 var excludedFeeds = GetExcludedFeeds ( ) ;
789787
790788 var feedsToCheck = feeds . Where ( feed => ! excludedFeeds . Contains ( feed ) ) . ToHashSet ( ) ;
791- var reachableFeeds = GetReachableNuGetFeeds ( feedsToCheck , isFallback : false ) ;
789+ reachableFeeds = GetReachableNuGetFeeds ( feedsToCheck , isFallback : false ) ;
792790 var allFeedsReachable = reachableFeeds . Count == feedsToCheck . Count ;
793791
794792 EmitUnreachableFeedsDiagnostics ( allFeedsReachable ) ;
795793
796- return ( allFeedsReachable , reachableFeeds ) ;
794+ return allFeedsReachable ;
797795 }
798796
799797 /// <summary>
@@ -899,13 +897,23 @@ private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
899897 logger . LogDebug ( "No NuGet feeds found in nuget.config files." ) ;
900898 }
901899
902- // todo: this could be improved.
903- HashSet < string > ? allFeeds = null ;
900+ // If private package registries are configured for C#, then consider those
901+ // in addition to the ones that are configured in `nuget.config` files.
902+ if ( HasPrivateRegistryFeeds )
903+ {
904+ logger . LogInfo ( $ "Found { PrivateRegistryFeeds . Count } private registry feeds configured for C#: { string . Join ( ", " , PrivateRegistryFeeds . OrderBy ( f => f ) ) } ") ;
905+ explicitFeeds . UnionWith ( PrivateRegistryFeeds ) ;
906+ }
907+
908+ HashSet < string > allFeeds = [ ] ;
909+
910+ // Add all explicitFeeds to the set of all feeds.
911+ allFeeds . UnionWith ( explicitFeeds ) ;
904912
905913 if ( nugetConfigs . Count > 0 )
906914 {
907915 // We don't have to get the feeds from each of the folders from below, it would be enought to check the folders that recursively contain the others.
908- allFeeds = nugetConfigs
916+ var nugetConfigFeeds = nugetConfigs
909917 . Select ( config =>
910918 {
911919 try
@@ -922,18 +930,13 @@ private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
922930 . SelectMany ( folder => GetFeeds ( ( ) => dotnet . GetNugetFeedsFromFolder ( folder ! ) ) )
923931 . ToHashSet ( ) ;
924932
925- // If we have discovered any explicit feeds, then we also expect these to be in the set of all feeds.
926- // Normally, it is a safe assumption to make that `GetNugetFeedsFromFolder` will include the feeds configured
927- // in a NuGet configuration file in the given directory. There is one exception: on a system with case-sensitive
928- // file systems, we may discover a configuration file such as `Nuget.Config` which is not recognised by `dotnet nuget`.
929- // In that case, our call to `GetNugetFeeds` will retrieve the feeds from that file (because it is accepted when
930- // provided explicitly as `--configfile` argument), but the call to `GetNugetFeedsFromFolder` will not.
931- allFeeds . UnionWith ( explicitFeeds ) ;
933+ allFeeds . UnionWith ( nugetConfigFeeds ) ;
932934 }
933935 else
934936 {
935937 // If we haven't found any `nuget.config` files, then obtain a list of feeds from the root source directory.
936- allFeeds = GetFeeds ( ( ) => dotnet . GetNugetFeedsFromFolder ( fileProvider . SourceDir . FullName ) ) . ToHashSet ( ) ;
938+ var nugetFeedsFromRoot = GetFeeds ( ( ) => dotnet . GetNugetFeedsFromFolder ( fileProvider . SourceDir . FullName ) ) ;
939+ allFeeds . UnionWith ( nugetFeedsFromRoot ) ;
937940 }
938941
939942 logger . LogInfo ( $ "Found { allFeeds . Count } NuGet feeds (with inherited ones) in nuget.config files: { string . Join ( ", " , allFeeds . OrderBy ( f => f ) ) } ") ;
0 commit comments