Skip to content

Commit 9987b93

Browse files
committed
C#: Make the restore sources project/solution specific.
1 parent f6f1f1a commit 9987b93

1 file changed

Lines changed: 61 additions & 33 deletions

File tree

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public HashSet<AssemblyLookupLocation> Restore()
119119
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", CheckNugetFeedResponsiveness ? "1" : "0"));
120120

121121
HashSet<string> explicitFeeds = [];
122-
string? explicitNugetSources = null;
122+
HashSet<string> reachableFeeds = [];
123123

124124
try
125125
{
@@ -137,9 +137,11 @@ public HashSet<AssemblyLookupLocation> Restore()
137137
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
138138
}
139139

140-
var timeout = CheckSpecifiedFeeds(explicitFeeds, out var reachableFeeds);
141-
var allReachable = explicitFeeds.Count == reachableFeeds.Count;
142-
EmitUnreachableFeedsDiagnostics(allReachable);
140+
var timeout = CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds);
141+
reachableFeeds.UnionWith(reachableExplicitFeeds);
142+
143+
var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count;
144+
EmitUnreachableFeedsDiagnostics(allExplicitReachable);
143145

144146
if (timeout)
145147
{
@@ -154,16 +156,6 @@ public HashSet<AssemblyLookupLocation> Restore()
154156
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
155157
CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds);
156158
reachableFeeds.UnionWith(reachableInheritedFeeds);
157-
158-
// If feed responsiveness is being checked, we only want to use the feeds that are reachable (note this set includes private
159-
// registry feeds if they are reachable).
160-
explicitNugetSources = MakeRestoreSourcesArgument(reachableFeeds);
161-
}
162-
else if (HasPrivateRegistryFeeds)
163-
{
164-
// If private registries are configured they need to be included as sources for the restore, which requires that
165-
// they are provided as source arguments for the restore. The private registries are included in the `allFeeds` set.
166-
explicitNugetSources = MakeRestoreSourcesArgument(allFeeds);
167159
}
168160

169161
using (var nuget = new NugetExeWrapper(fileProvider, legacyPackageDirectory, logger, IsDefaultFeedReachable))
@@ -206,9 +198,9 @@ public HashSet<AssemblyLookupLocation> Restore()
206198
}
207199

208200
// Restore project dependencies with `dotnet restore`.
209-
var restoredProjects = RestoreSolutions(explicitNugetSources, out var container);
201+
var restoredProjects = RestoreSolutions(reachableFeeds, out var container);
210202
var projects = fileProvider.Projects.Except(restoredProjects);
211-
RestoreProjects(projects, explicitNugetSources, out var containers);
203+
RestoreProjects(projects, reachableFeeds, out var containers);
212204

213205
var dependencies = containers.Flatten(container);
214206

@@ -313,7 +305,7 @@ private List<string> GetReachableFallbackNugetFeeds(HashSet<string>? feedsFromNu
313305
/// Populates dependencies with the relevant dependencies from the assets files generated by the restore.
314306
/// Returns a list of projects that are up to date with respect to restore.
315307
/// </summary>
316-
private IEnumerable<string> RestoreSolutions(string? nugetSources, out DependencyContainer dependencies)
308+
private IEnumerable<string> RestoreSolutions(HashSet<string> reachableFeeds, out DependencyContainer dependencies)
317309
{
318310
var successCount = 0;
319311
var nugetSourceFailures = 0;
@@ -326,6 +318,7 @@ private IEnumerable<string> RestoreSolutions(string? nugetSources, out Dependenc
326318
var projects = fileProvider.Solutions.SelectMany(solution =>
327319
{
328320
logger.LogInfo($"Restoring solution {solution}...");
321+
var nugetSources = MakeRestoreSourcesArgument(solution, reachableFeeds);
329322
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
330323
if (res.Success)
331324
{
@@ -350,7 +343,7 @@ private IEnumerable<string> RestoreSolutions(string? nugetSources, out Dependenc
350343
return projects;
351344
}
352345

353-
private string? MakeRestoreSourcesArgument(IEnumerable<string> feeds)
346+
private string FeedsToRestoreArgument(IEnumerable<string> feeds)
354347
{
355348
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument.
356349
if (!feeds.Any())
@@ -369,14 +362,46 @@ private IEnumerable<string> RestoreSolutions(string? nugetSources, out Dependenc
369362
return feedArgs.ToString();
370363
}
371364

365+
/// <summary>
366+
/// Constructs the list of NuGet sources to use for this restore.
367+
// (1) Use the feeds we get from `dotnet nuget list source`
368+
// (2) Use private registries, if they are configured
369+
/// </summary>
370+
/// <param name="path">Path to project/solution</param>
371+
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
372+
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
373+
private string? MakeRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
374+
{
375+
// Do not construct an set of explicit NuGet sources to use for restore.
376+
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
377+
{
378+
return null;
379+
}
380+
381+
// Find the path specific feeds.
382+
var folder = GetDirectoryName(path);
383+
var feedsToConsider = folder is not null ? GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder)).ToHashSet() : [];
384+
385+
if (HasPrivateRegistryFeeds)
386+
{
387+
feedsToConsider.UnionWith(PrivateRegistryFeeds);
388+
}
389+
390+
var feedsToUse = CheckNugetFeedResponsiveness
391+
? feedsToConsider.Where(reachableFeeds.Contains)
392+
: feedsToConsider;
393+
394+
return FeedsToRestoreArgument(feedsToUse);
395+
}
396+
372397
/// <summary>
373398
/// Executes `dotnet restore` on all projects in projects.
374399
/// This is done in parallel for performance reasons.
375400
/// Populates dependencies with the relative paths to the assets files generated by the restore.
376401
/// </summary>
377402
/// <param name="projects">A list of paths to project files.</param>
378-
/// <param name="explicitRestoreSources">The explicit restore sources argument.</param>
379-
private void RestoreProjects(IEnumerable<string> projects, string? explicitRestoreSources, out ConcurrentBag<DependencyContainer> dependencies)
403+
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
404+
private void RestoreProjects(IEnumerable<string> projects, HashSet<string> reachableFeeds, out ConcurrentBag<DependencyContainer> dependencies)
380405
{
381406
var successCount = 0;
382407
var nugetSourceFailures = 0;
@@ -393,7 +418,8 @@ private void RestoreProjects(IEnumerable<string> projects, string? explicitResto
393418
foreach (var project in projectGroup)
394419
{
395420
logger.LogInfo($"Restoring project {project}...");
396-
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: explicitRestoreSources, TargetWindows: isWindows));
421+
var nugetSources = MakeRestoreSourcesArgument(project, reachableFeeds);
422+
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
397423
assets.AddDependenciesRange(res.AssetsFilePaths);
398424
lock (sync)
399425
{
@@ -898,6 +924,19 @@ private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
898924
}
899925
}
900926

927+
private string? GetDirectoryName(string path)
928+
{
929+
try
930+
{
931+
return new FileInfo(path).Directory?.FullName;
932+
}
933+
catch (Exception exc)
934+
{
935+
logger.LogWarning($"Failed to get directory of '{path}': {exc}");
936+
}
937+
return null;
938+
}
939+
901940
private (HashSet<string> explicitFeeds, HashSet<string> allFeeds) GetAllFeeds()
902941
{
903942
var nugetConfigs = fileProvider.NugetConfigs;
@@ -968,18 +1007,7 @@ private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
9681007
{
9691008
// 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.
9701009
var nugetConfigFeeds = nugetConfigs
971-
.Select(config =>
972-
{
973-
try
974-
{
975-
return new FileInfo(config).Directory?.FullName;
976-
}
977-
catch (Exception exc)
978-
{
979-
logger.LogWarning($"Failed to get directory of '{config}': {exc}");
980-
}
981-
return null;
982-
})
1010+
.Select(GetDirectoryName)
9831011
.Where(folder => folder != null)
9841012
.SelectMany(folder => GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder!)))
9851013
.ToHashSet();

0 commit comments

Comments
 (0)