3
3
using System . IO ;
4
4
using System . Linq ;
5
5
using System . Net . Http ;
6
+ using System . Text ;
6
7
using System . Text . RegularExpressions ;
7
8
using System . Threading ;
8
9
using System . Threading . Tasks ;
@@ -14,12 +15,13 @@ public sealed partial class DependencyManager
14
15
{
15
16
private void RestoreNugetPackages ( List < FileInfo > allNonBinaryFiles , IEnumerable < string > allProjects , IEnumerable < string > allSolutions , HashSet < AssemblyLookupLocation > dllLocations )
16
17
{
18
+ var checkNugetFeedResponsiveness = EnvironmentVariables . GetBoolean ( EnvironmentVariableNames . CheckNugetFeedResponsiveness ) ;
17
19
try
18
20
{
19
- var checkNugetFeedResponsiveness = EnvironmentVariables . GetBoolean ( EnvironmentVariableNames . CheckNugetFeedResponsiveness ) ;
20
21
if ( checkNugetFeedResponsiveness && ! CheckFeeds ( allNonBinaryFiles ) )
21
22
{
22
- DownloadMissingPackages ( allNonBinaryFiles , dllLocations , withNugetConfig : false ) ;
23
+ // 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.
24
+ DownloadMissingPackagesFromSpecificFeeds ( allNonBinaryFiles , dllLocations ) ;
23
25
return ;
24
26
}
25
27
@@ -75,7 +77,35 @@ private void RestoreNugetPackages(List<FileInfo> allNonBinaryFiles, IEnumerable<
75
77
dllLocations . UnionWith ( paths . Select ( p => new AssemblyLookupLocation ( p ) ) ) ;
76
78
77
79
LogAllUnusedPackages ( dependencies ) ;
78
- DownloadMissingPackages ( allNonBinaryFiles , dllLocations ) ;
80
+
81
+ if ( checkNugetFeedResponsiveness )
82
+ {
83
+ DownloadMissingPackagesFromSpecificFeeds ( allNonBinaryFiles , dllLocations ) ;
84
+ }
85
+ else
86
+ {
87
+ DownloadMissingPackages ( allNonBinaryFiles , dllLocations ) ;
88
+ }
89
+ }
90
+
91
+ internal const string PublicNugetFeed = "https://api.nuget.org/v3/index.json" ;
92
+
93
+ private List < string > GetReachableFallbackNugetFeeds ( )
94
+ {
95
+ var fallbackFeeds = EnvironmentVariables . GetURLs ( EnvironmentVariableNames . FallbackNugetFeeds ) . ToHashSet ( ) ;
96
+ if ( fallbackFeeds . Count == 0 )
97
+ {
98
+ fallbackFeeds . Add ( PublicNugetFeed ) ;
99
+ }
100
+
101
+ logger . LogInfo ( "Checking fallback Nuget feed reachability" ) ;
102
+ var reachableFallbackFeeds = fallbackFeeds . Where ( feed => IsFeedReachable ( feed ) ) . ToList ( ) ;
103
+ if ( reachableFallbackFeeds . Count == 0 )
104
+ {
105
+ logger . LogWarning ( "No fallback Nuget feeds are reachable. Skipping fallback Nuget package restoration." ) ;
106
+ }
107
+
108
+ return reachableFallbackFeeds ;
79
109
}
80
110
81
111
/// <summary>
@@ -148,7 +178,16 @@ private void RestoreProjects(IEnumerable<string> projects, out IEnumerable<strin
148
178
CompilationInfos . Add ( ( "Failed project restore with package source error" , nugetSourceFailures . ToString ( ) ) ) ;
149
179
}
150
180
151
- private void DownloadMissingPackages ( List < FileInfo > allFiles , ISet < AssemblyLookupLocation > dllLocations , bool withNugetConfig = true )
181
+ private void DownloadMissingPackagesFromSpecificFeeds ( List < FileInfo > allNonBinaryFiles , HashSet < AssemblyLookupLocation > dllLocations )
182
+ {
183
+ var reachableFallbackFeeds = GetReachableFallbackNugetFeeds ( ) ;
184
+ if ( reachableFallbackFeeds . Count > 0 )
185
+ {
186
+ DownloadMissingPackages ( allNonBinaryFiles , dllLocations , withNugetConfig : false , fallbackNugetFeeds : reachableFallbackFeeds ) ;
187
+ }
188
+ }
189
+
190
+ private void DownloadMissingPackages ( List < FileInfo > allFiles , HashSet < AssemblyLookupLocation > dllLocations , bool withNugetConfig = true , IEnumerable < string > ? fallbackNugetFeeds = null )
152
191
{
153
192
var alreadyDownloadedPackages = GetRestoredPackageDirectoryNames ( packageDirectory . DirInfo ) ;
154
193
var alreadyDownloadedLegacyPackages = GetRestoredLegacyPackageNames ( ) ;
@@ -181,9 +220,10 @@ private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<AssemblyLooku
181
220
}
182
221
183
222
logger . LogInfo ( $ "Found { notYetDownloadedPackages . Count } packages that are not yet restored") ;
223
+ using var tempDir = new TemporaryDirectory ( ComputeTempDirectory ( sourceDir . FullName , "nugetconfig" ) ) ;
184
224
var nugetConfig = withNugetConfig
185
225
? GetNugetConfig ( allFiles )
186
- : null ;
226
+ : CreateFallbackNugetConfig ( fallbackNugetFeeds , tempDir . DirInfo . FullName ) ;
187
227
188
228
CompilationInfos . Add ( ( "Fallback nuget restore" , notYetDownloadedPackages . Count . ToString ( ) ) ) ;
189
229
@@ -209,6 +249,33 @@ private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<AssemblyLooku
209
249
dllLocations . Add ( missingPackageDirectory . DirInfo . FullName ) ;
210
250
}
211
251
252
+ private string ? CreateFallbackNugetConfig ( IEnumerable < string > ? fallbackNugetFeeds , string folderPath )
253
+ {
254
+ if ( fallbackNugetFeeds is null )
255
+ {
256
+ // We're not overriding the inherited Nuget feeds
257
+ return null ;
258
+ }
259
+
260
+ var sb = new StringBuilder ( ) ;
261
+ fallbackNugetFeeds . ForEach ( ( feed , index ) => sb . AppendLine ( $ "<add key=\" feed{ index } \" value=\" { feed } \" />") ) ;
262
+
263
+ var nugetConfigPath = Path . Combine ( folderPath , "nuget.config" ) ;
264
+ logger . LogInfo ( $ "Creating fallback nuget.config file { nugetConfigPath } .") ;
265
+ File . WriteAllText ( nugetConfigPath ,
266
+ $ """
267
+ <?xml version="1.0" encoding="utf-8"?>
268
+ <configuration>
269
+ <packageSources>
270
+ <clear />
271
+ { sb }
272
+ </packageSources>
273
+ </configuration>
274
+ """ ) ;
275
+
276
+ return nugetConfigPath ;
277
+ }
278
+
212
279
private string [ ] GetAllNugetConfigs ( List < FileInfo > allFiles ) => allFiles . SelectFileNamesByName ( "nuget.config" ) . ToArray ( ) ;
213
280
214
281
private string ? GetNugetConfig ( List < FileInfo > allFiles )
@@ -429,18 +496,18 @@ private bool IsFeedReachable(string feed)
429
496
private bool CheckFeeds ( List < FileInfo > allFiles )
430
497
{
431
498
logger . LogInfo ( "Checking Nuget feeds..." ) ;
432
- var feeds = GetAllFeeds ( allFiles ) ;
499
+ var ( explicitFeeds , allFeeds ) = GetAllFeeds ( allFiles ) ;
500
+ var inheritedFeeds = allFeeds . Except ( explicitFeeds ) . ToHashSet ( ) ;
433
501
434
- var excludedFeeds = Environment . GetEnvironmentVariable ( EnvironmentVariableNames . ExcludedNugetFeedsFromResponsivenessCheck )
435
- ? . Split ( " " , StringSplitOptions . RemoveEmptyEntries )
502
+ var excludedFeeds = EnvironmentVariables . GetURLs ( EnvironmentVariableNames . ExcludedNugetFeedsFromResponsivenessCheck )
436
503
. ToHashSet ( ) ?? [ ] ;
437
504
438
505
if ( excludedFeeds . Count > 0 )
439
506
{
440
507
logger . LogInfo ( $ "Excluded Nuget feeds from responsiveness check: { string . Join ( ", " , excludedFeeds . OrderBy ( f => f ) ) } ") ;
441
508
}
442
509
443
- var allFeedsReachable = feeds . All ( feed => excludedFeeds . Contains ( feed ) || IsFeedReachable ( feed ) ) ;
510
+ var allFeedsReachable = explicitFeeds . All ( feed => excludedFeeds . Contains ( feed ) || IsFeedReachable ( feed ) ) ;
444
511
if ( ! allFeedsReachable )
445
512
{
446
513
logger . LogWarning ( "Found unreachable Nuget feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis." ) ;
@@ -454,13 +521,19 @@ private bool CheckFeeds(List<FileInfo> allFiles)
454
521
) ) ;
455
522
}
456
523
CompilationInfos . Add ( ( "All Nuget feeds reachable" , allFeedsReachable ? "1" : "0" ) ) ;
524
+
525
+ if ( inheritedFeeds . Count > 0 )
526
+ {
527
+ logger . LogInfo ( $ "Inherited Nuget feeds: { string . Join ( ", " , inheritedFeeds . OrderBy ( f => f ) ) } ") ;
528
+ CompilationInfos . Add ( ( "Inherited Nuget feed count" , inheritedFeeds . Count . ToString ( ) ) ) ;
529
+ }
530
+
457
531
return allFeedsReachable ;
458
532
}
459
533
460
- private IEnumerable < string > GetFeeds ( string nugetConfig )
534
+ private IEnumerable < string > GetFeeds ( Func < IList < string > > getNugetFeeds )
461
535
{
462
- logger . LogInfo ( $ "Getting Nuget feeds from '{ nugetConfig } '...") ;
463
- var results = dotnet . GetNugetFeeds ( nugetConfig ) ;
536
+ var results = getNugetFeeds ( ) ;
464
537
var regex = EnabledNugetFeed ( ) ;
465
538
foreach ( var result in results )
466
539
{
@@ -479,27 +552,63 @@ private IEnumerable<string> GetFeeds(string nugetConfig)
479
552
continue ;
480
553
}
481
554
482
- yield return url ;
555
+ if ( ! string . IsNullOrWhiteSpace ( url ) )
556
+ {
557
+ yield return url ;
558
+ }
483
559
}
484
560
}
485
561
486
- private HashSet < string > GetAllFeeds ( List < FileInfo > allFiles )
562
+ private ( HashSet < string > , HashSet < string > ) GetAllFeeds ( List < FileInfo > allFiles )
487
563
{
564
+ IList < string > GetNugetFeeds ( string nugetConfig )
565
+ {
566
+ logger . LogInfo ( $ "Getting Nuget feeds from '{ nugetConfig } '...") ;
567
+ return dotnet . GetNugetFeeds ( nugetConfig ) ;
568
+ }
569
+
570
+ IList < string > GetNugetFeedsFromFolder ( string folderPath )
571
+ {
572
+ logger . LogInfo ( $ "Getting Nuget feeds in folder '{ folderPath } '...") ;
573
+ return dotnet . GetNugetFeedsFromFolder ( folderPath ) ;
574
+ }
575
+
488
576
var nugetConfigs = GetAllNugetConfigs ( allFiles ) ;
489
- var feeds = nugetConfigs
490
- . SelectMany ( GetFeeds )
491
- . Where ( str => ! string . IsNullOrWhiteSpace ( str ) )
577
+ var explicitFeeds = nugetConfigs
578
+ . SelectMany ( config => GetFeeds ( ( ) => GetNugetFeeds ( config ) ) )
492
579
. ToHashSet ( ) ;
493
580
494
- if ( feeds . Count > 0 )
581
+ if ( explicitFeeds . Count > 0 )
495
582
{
496
- logger . LogInfo ( $ "Found { feeds . Count } Nuget feeds in nuget.config files: { string . Join ( ", " , feeds . OrderBy ( f => f ) ) } ") ;
583
+ logger . LogInfo ( $ "Found { explicitFeeds . Count } Nuget feeds in nuget.config files: { string . Join ( ", " , explicitFeeds . OrderBy ( f => f ) ) } ") ;
497
584
}
498
585
else
499
586
{
500
587
logger . LogDebug ( "No Nuget feeds found in nuget.config files." ) ;
501
588
}
502
- return feeds ;
589
+
590
+ // todo: this could be improved.
591
+ // 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.
592
+ var allFeeds = nugetConfigs
593
+ . Select ( config =>
594
+ {
595
+ try
596
+ {
597
+ return new FileInfo ( config ) . Directory ? . FullName ;
598
+ }
599
+ catch ( Exception exc )
600
+ {
601
+ logger . LogWarning ( $ "Failed to get directory of '{ config } ': { exc } ") ;
602
+ }
603
+ return null ;
604
+ } )
605
+ . Where ( folder => folder != null )
606
+ . SelectMany ( folder => GetFeeds ( ( ) => GetNugetFeedsFromFolder ( folder ! ) ) )
607
+ . ToHashSet ( ) ;
608
+
609
+ logger . LogInfo ( $ "Found { allFeeds . Count } Nuget feeds (with inherited ones) in nuget.config files: { string . Join ( ", " , allFeeds . OrderBy ( f => f ) ) } ") ;
610
+
611
+ return ( explicitFeeds , allFeeds ) ;
503
612
}
504
613
505
614
[ GeneratedRegex ( @"<TargetFramework>.*</TargetFramework>" , RegexOptions . IgnoreCase | RegexOptions . Compiled | RegexOptions . Singleline ) ]
0 commit comments