11using System ;
2+ using System . Collections . Generic ;
23using System . Diagnostics ;
34using System . IO ;
45using System . Linq ;
5- using Microsoft . Build . Framework ;
66using Semmle . Util ;
77
88namespace Semmle . Extraction . CSharp . DependencyFetching
@@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
1212 /// Locates packages in a source tree and downloads all of the
1313 /// referenced assemblies to a temp folder.
1414 /// </summary>
15- internal class NugetPackages
15+ internal class NugetPackages : IDisposable
1616 {
1717 private readonly string ? nugetExe ;
1818 private readonly Util . Logging . ILogger logger ;
@@ -24,6 +24,9 @@ internal class NugetPackages
2424
2525 public int PackageCount => packageFiles . Length ;
2626
27+ private readonly string ? backupNugetConfig ;
28+ private readonly string ? nugetConfigPath ;
29+
2730 /// <summary>
2831 /// The computed packages directory.
2932 /// This will be in the Temp location
@@ -47,6 +50,41 @@ public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory, Util
4750 {
4851 logger . LogInfo ( $ "Found { packageFiles . Length } packages.config files, trying to use nuget.exe for package restore") ;
4952 nugetExe = ResolveNugetExe ( sourceDir ) ;
53+ if ( HasNoPackageSource ( ) )
54+ {
55+ // We only modify or add a top level nuget.config file
56+ nugetConfigPath = Path . Combine ( sourceDir , "nuget.config" ) ;
57+ try
58+ {
59+ if ( File . Exists ( nugetConfigPath ) )
60+ {
61+ var tempFolderPath = FileUtils . GetTemporaryWorkingDirectory ( out var _ ) ;
62+
63+ do
64+ {
65+ backupNugetConfig = Path . Combine ( tempFolderPath , Path . GetRandomFileName ( ) ) ;
66+ }
67+ while ( File . Exists ( backupNugetConfig ) ) ;
68+ File . Copy ( nugetConfigPath , backupNugetConfig , true ) ;
69+ }
70+ else
71+ {
72+ File . WriteAllText ( nugetConfigPath ,
73+ """
74+ <?xml version="1.0" encoding="utf-8"?>
75+ <configuration>
76+ <packageSources>
77+ </packageSources>
78+ </configuration>
79+ """ ) ;
80+ }
81+ AddDefaultPackageSource ( nugetConfigPath ) ;
82+ }
83+ catch ( Exception e )
84+ {
85+ logger . LogError ( $ "Failed to add default package source to { nugetConfigPath } : { e } ") ;
86+ }
87+ }
5088 }
5189 else
5290 {
@@ -118,15 +156,15 @@ private bool TryRestoreNugetPackage(string package)
118156 */
119157
120158 string exe , args ;
121- if ( Util . Win32 . IsWindows ( ) )
159+ if ( Win32 . IsWindows ( ) )
122160 {
123161 exe = nugetExe ! ;
124- args = string . Format ( "install -OutputDirectory {0 } {1}" , packageDirectory , package ) ;
162+ args = $ "install -OutputDirectory { packageDirectory } { package } " ;
125163 }
126164 else
127165 {
128166 exe = "mono" ;
129- args = string . Format ( "{0 } install -OutputDirectory {1 } {2}" , nugetExe , packageDirectory , package ) ;
167+ args = $ " { nugetExe } install -OutputDirectory { packageDirectory } { package } " ;
130168 }
131169
132170 var pi = new ProcessStartInfo ( exe , args )
@@ -159,5 +197,75 @@ public int InstallPackages()
159197 {
160198 return packageFiles . Count ( package => TryRestoreNugetPackage ( package . FullName ) ) ;
161199 }
200+
201+ private bool HasNoPackageSource ( )
202+ {
203+ if ( Win32 . IsWindows ( ) )
204+ {
205+ return false ;
206+ }
207+
208+ try
209+ {
210+ logger . LogInfo ( "Checking if default package source is available..." ) ;
211+ RunMonoNugetCommand ( "sources list -ForceEnglishOutput" , out var stdout ) ;
212+ if ( stdout . All ( line => line != "No sources found." ) )
213+ {
214+ return false ;
215+ }
216+
217+ return true ;
218+ }
219+ catch ( Exception e )
220+ {
221+ logger . LogWarning ( $ "Failed to check if default package source is added: { e } ") ;
222+ return false ;
223+ }
224+ }
225+
226+ private void RunMonoNugetCommand ( string command , out IList < string > stdout )
227+ {
228+ var exe = "mono" ;
229+ var args = $ "{ nugetExe } { command } ";
230+ var pi = new ProcessStartInfo ( exe , args )
231+ {
232+ RedirectStandardOutput = true ,
233+ RedirectStandardError = true ,
234+ UseShellExecute = false
235+ } ;
236+
237+ var threadId = Environment . CurrentManagedThreadId ;
238+ void onOut ( string s ) => logger . LogInfo ( s , threadId ) ;
239+ void onError ( string s ) => logger . LogError ( s , threadId ) ;
240+ pi . ReadOutput ( out stdout , onOut , onError ) ;
241+ }
242+
243+ private void AddDefaultPackageSource ( string nugetConfig )
244+ {
245+ logger . LogInfo ( "Adding default package source..." ) ;
246+ RunMonoNugetCommand ( $ "sources add -Name DefaultNugetOrg -Source https://api.nuget.org/v3/index.json -ConfigFile \" { nugetConfig } \" ", out var _ ) ;
247+ }
248+
249+ public void Dispose ( )
250+ {
251+ if ( nugetConfigPath is null )
252+ {
253+ return ;
254+ }
255+
256+ try
257+ {
258+ File . Delete ( nugetConfigPath ) ;
259+
260+ if ( backupNugetConfig is not null )
261+ {
262+ File . Move ( backupNugetConfig , nugetConfigPath ) ;
263+ }
264+ }
265+ catch ( Exception exc )
266+ {
267+ logger . LogError ( $ "Failed to restore original nuget.config file: { exc } ") ;
268+ }
269+ }
162270 }
163271}
0 commit comments