Skip to content

Commit e30e0a9

Browse files
committed
C#: Make the NuGetExeWrapper respect the CheckFeeds flag, private registries configuration and provide sources via the command line instead of creating a file.
1 parent cb7e8df commit e30e0a9

3 files changed

Lines changed: 63 additions & 159 deletions

File tree

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,20 @@ private IEnumerable<string> GetFeedsFromFolder(string folderPath) =>
8888
private IEnumerable<string> GetFeedsFromNugetConfig(string nugetConfigPath) =>
8989
GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath));
9090

91-
private string FeedsToRestoreArgument(IEnumerable<string> feeds)
91+
public string FeedsToRestoreArgument(IEnumerable<string> feeds, string sourceArgumentPrefix)
9292
{
9393
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument.
9494
if (!feeds.Any())
9595
{
96-
return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\"";
96+
return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\"";
9797
}
9898

9999
// Add package sources. If any are present, they override all sources specified in
100100
// the configuration file(s).
101101
var feedArgs = new StringBuilder();
102102
foreach (var feed in feeds)
103103
{
104-
feedArgs.Append($" -s \"{feed}\"");
104+
feedArgs.Append($" {sourceArgumentPrefix} \"{feed}\"");
105105
}
106106

107107
return feedArgs.ToString();
@@ -114,15 +114,9 @@ private string FeedsToRestoreArgument(IEnumerable<string> feeds)
114114
/// </summary>
115115
/// <param name="path">Path to project/solution</param>
116116
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
117-
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
118-
public string? MakeRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
117+
/// <returns>The list of NuGet feeds to use for this restore.</returns>
118+
public IEnumerable<string> FeedsToUse(string path, HashSet<string> reachableFeeds)
119119
{
120-
// Do not construct a set of explicit NuGet sources to use for restore.
121-
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
122-
{
123-
return null;
124-
}
125-
126120
// Find the path specific feeds.
127121
var folder = GetDirectoryName(path);
128122
var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet<string>();
@@ -136,7 +130,28 @@ private string FeedsToRestoreArgument(IEnumerable<string> feeds)
136130
? feedsToConsider.Where(reachableFeeds.Contains)
137131
: feedsToConsider;
138132

139-
return FeedsToRestoreArgument(feedsToUse);
133+
return feedsToUse;
134+
}
135+
136+
/// <summary>
137+
/// Constructs the list of NuGet sources to use for dotnet restore.
138+
/// (1) Use the feeds we get from `dotnet nuget list source`
139+
/// (2) Use private registries, if they are configured
140+
/// </summary>
141+
/// <param name="path">Path to project/solution</param>
142+
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
143+
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
144+
public string? MakeDotnetRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
145+
{
146+
// Do not construct a set of explicit NuGet sources to use for restore.
147+
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
148+
{
149+
return null;
150+
}
151+
152+
var feedsToUse = FeedsToUse(path, reachableFeeds);
153+
154+
return FeedsToRestoreArgument(feedsToUse, "-s");
140155
}
141156

142157
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)

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

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,15 @@ public HashSet<AssemblyLookupLocation> Restore()
151151

152152
try
153153
{
154-
using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager))
154+
var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, reachableFeeds);
155+
var count = packagesConfigRestore.InstallPackages();
156+
if (packagesConfigRestore.PackageCount > 0)
155157
{
156-
var count = packagesConfigRestore.InstallPackages();
157-
158-
if (packagesConfigRestore.PackageCount > 0)
159-
{
160-
compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString()));
161-
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
162-
}
158+
compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString()));
159+
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
163160
}
164161

162+
165163
var nugetPackageDlls = legacyPackageDirectory.DirInfo.GetFiles("*.dll", new EnumerationOptions { RecurseSubdirectories = true });
166164
var nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet();
167165
var excludedPaths = nugetPackageDllPaths
@@ -238,7 +236,7 @@ private IEnumerable<string> RestoreSolutions(HashSet<string> reachableFeeds, out
238236
var projects = fileProvider.Solutions.SelectMany(solution =>
239237
{
240238
logger.LogInfo($"Restoring solution {solution}...");
241-
var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds);
239+
var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(solution, reachableFeeds);
242240
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
243241
if (res.Success)
244242
{
@@ -287,7 +285,7 @@ private void RestoreProjects(IEnumerable<string> projects, HashSet<string> reach
287285
foreach (var project in projectGroup)
288286
{
289287
logger.LogInfo($"Restoring project {project}...");
290-
var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds);
288+
var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(project, reachableFeeds);
291289
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
292290
assets.AddDependenciesRange(res.AssetsFilePaths);
293291
lock (sync)

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

Lines changed: 28 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Semmle.Extraction.CSharp.DependencyFetching
99
{
10-
internal interface IPackagesConfigRestore : IDisposable
10+
internal interface IPackagesConfigRestore
1111
{
1212
/// <summary>
1313
/// The number of packages.config files found in the source tree.
@@ -33,11 +33,11 @@ internal interface IPackagesConfigRestore : IDisposable
3333
/// </summary>
3434
internal class PackagesConfigRestoreFactory
3535
{
36-
public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
36+
public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
3737
{
3838
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled())
3939
{
40-
return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager);
40+
return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, reachableFeeds);
4141
}
4242

4343
return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger);
@@ -55,8 +55,6 @@ private class NugetExeWrapper : IPackagesConfigRestore
5555

5656
public int PackageCount => fileProvider.PackagesConfigs.Count;
5757

58-
private readonly string? backupNugetConfig;
59-
private readonly string? nugetConfigPath;
6058
private readonly FileProvider fileProvider;
6159

6260
/// <summary>
@@ -66,58 +64,31 @@ private class NugetExeWrapper : IPackagesConfigRestore
6664
/// </summary>
6765
private readonly DependencyDirectory packageDirectory;
6866
private readonly FeedManager feedManager;
67+
private readonly HashSet<string> reachableFeeds;
6968

7069
private bool IsWindows => SystemBuildActions.Instance.IsWindows();
7170

71+
private bool? isDefaultFeedReachable;
72+
private bool IsDefaultFeedReachable =>
73+
isDefaultFeedReachable ??= feedManager.IsDefaultFeedReachable();
74+
75+
76+
7277
/// <summary>
7378
/// Create the package manager for a specified source tree.
7479
/// </summary>
75-
public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
80+
public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
7681
{
7782
this.fileProvider = fileProvider;
7883
this.packageDirectory = packageDirectory;
7984
this.logger = logger;
8085
this.feedManager = feedManager;
86+
this.reachableFeeds = reachableFeeds;
8187

8288
if (fileProvider.PackagesConfigs.Count > 0)
8389
{
8490
logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore");
8591
nugetExe = ResolveNugetExe();
86-
if (!HasPackageSource() && feedManager.IsDefaultFeedReachable())
87-
{
88-
// We only modify or add a top level nuget.config file
89-
nugetConfigPath = Path.Join(fileProvider.SourceDir.FullName, "nuget.config");
90-
try
91-
{
92-
if (File.Exists(nugetConfigPath))
93-
{
94-
var tempFolderPath = FileUtils.GetTemporaryWorkingDirectory(out _);
95-
96-
do
97-
{
98-
backupNugetConfig = Path.Join(tempFolderPath, Path.GetRandomFileName());
99-
}
100-
while (File.Exists(backupNugetConfig));
101-
File.Copy(nugetConfigPath, backupNugetConfig, true);
102-
}
103-
else
104-
{
105-
File.WriteAllText(nugetConfigPath,
106-
"""
107-
<?xml version="1.0" encoding="utf-8"?>
108-
<configuration>
109-
<packageSources>
110-
</packageSources>
111-
</configuration>
112-
""");
113-
}
114-
AddDefaultPackageSource(nugetConfigPath);
115-
}
116-
catch (Exception e)
117-
{
118-
logger.LogError($"Failed to add default package source to {nugetConfigPath}: {e}");
119-
}
120-
}
12192
}
12293
}
12394

@@ -200,6 +171,20 @@ private bool TryRestoreNugetPackage(string packagesConfig)
200171
{
201172
logger.LogInfo($"Restoring file \"{packagesConfig}\"...");
202173

174+
var sourcesArgument = "";
175+
var feedsToUse = feedManager.FeedsToUse(packagesConfig, reachableFeeds).ToList();
176+
var useDefaultFeed = feedsToUse.Count == 0 && IsDefaultFeedReachable;
177+
178+
// Explicitly construct the sources to be used for the restore command if any of the following is true:
179+
if (feedManager.CheckNugetFeedResponsiveness || feedManager.HasPrivateRegistryFeeds || useDefaultFeed)
180+
{
181+
if (useDefaultFeed)
182+
{
183+
feedsToUse.Add(FeedManager.PublicNugetOrgFeed);
184+
}
185+
sourcesArgument = feedManager.FeedsToRestoreArgument(feedsToUse, "-Source");
186+
}
187+
203188
/* Use nuget.exe to install a package.
204189
* Note that there is a clutch of NuGet assemblies which could be used to
205190
* invoke this directly, which would arguably be nicer. However they are
@@ -210,12 +195,12 @@ private bool TryRestoreNugetPackage(string packagesConfig)
210195
if (RunWithMono)
211196
{
212197
exe = "mono";
213-
args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\"";
198+
args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
214199
}
215200
else
216201
{
217202
exe = nugetExe!;
218-
args = $"install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\"";
203+
args = $"install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
219204
}
220205

221206
var pi = new ProcessStartInfo(exe, args)
@@ -248,98 +233,6 @@ public int InstallPackages()
248233
{
249234
return fileProvider.PackagesConfigs.Count(TryRestoreNugetPackage);
250235
}
251-
252-
private bool HasPackageSource()
253-
{
254-
if (IsWindows)
255-
{
256-
return true;
257-
}
258-
259-
try
260-
{
261-
logger.LogInfo("Checking if default package source is available...");
262-
RunMonoNugetCommand("sources list -ForceEnglishOutput", out var stdout);
263-
if (stdout.All(line => line != "No sources found."))
264-
{
265-
return true;
266-
}
267-
268-
return false;
269-
}
270-
catch (Exception e)
271-
{
272-
logger.LogWarning($"Failed to check if default package source is added: {e}");
273-
return true;
274-
}
275-
}
276-
277-
private void RunMonoNugetCommand(string command, out IList<string> stdout)
278-
{
279-
string exe, args;
280-
if (RunWithMono)
281-
{
282-
exe = "mono";
283-
args = $"\"{nugetExe}\" {command}";
284-
}
285-
else
286-
{
287-
exe = nugetExe!;
288-
args = command;
289-
}
290-
291-
var pi = new ProcessStartInfo(exe, args)
292-
{
293-
RedirectStandardOutput = true,
294-
RedirectStandardError = true,
295-
UseShellExecute = false
296-
};
297-
298-
var threadId = Environment.CurrentManagedThreadId;
299-
void onOut(string s) => logger.LogDebug(s, threadId);
300-
void onError(string s) => logger.LogError(s, threadId);
301-
pi.ReadOutput(out stdout, onOut, onError);
302-
}
303-
304-
private void AddDefaultPackageSource(string nugetConfig)
305-
{
306-
logger.LogInfo("Adding default package source...");
307-
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {FeedManager.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _);
308-
}
309-
310-
public void Dispose()
311-
{
312-
if (nugetConfigPath is null)
313-
{
314-
return;
315-
}
316-
317-
try
318-
{
319-
if (backupNugetConfig is null)
320-
{
321-
logger.LogInfo("Removing nuget.config file");
322-
File.Delete(nugetConfigPath);
323-
return;
324-
}
325-
326-
logger.LogInfo("Reverting nuget.config file content");
327-
// The content of the original nuget.config file is reverted without changing the file's attributes or casing:
328-
using (var backup = File.OpenRead(backupNugetConfig))
329-
using (var current = File.OpenWrite(nugetConfigPath))
330-
{
331-
current.SetLength(0); // Truncate file
332-
backup.CopyTo(current); // Restore original content
333-
}
334-
335-
logger.LogInfo("Deleting backup nuget.config file");
336-
File.Delete(backupNugetConfig);
337-
}
338-
catch (Exception exc)
339-
{
340-
logger.LogError($"Failed to restore original nuget.config file: {exc}");
341-
}
342-
}
343236
}
344237

345238
private class NoOpPackagesConfig : IPackagesConfigRestore
@@ -363,8 +256,6 @@ public int InstallPackages()
363256
}
364257
return 0;
365258
}
366-
367-
public void Dispose() { }
368259
}
369260
}
370261
}

0 commit comments

Comments
 (0)