dependabot-nuget 0.246.0 → 0.247.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +18 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +110 -0
  6. data/lib/dependabot/nuget/cache_manager.rb +9 -3
  7. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -12
  8. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +13 -3
  9. data/lib/dependabot/nuget/file_fetcher.rb +79 -31
  10. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +10 -2
  11. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +10 -2
  12. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +11 -2
  13. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +140 -41
  14. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
  15. data/lib/dependabot/nuget/file_parser.rb +3 -3
  16. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +25 -8
  17. data/lib/dependabot/nuget/file_updater.rb +74 -38
  18. data/lib/dependabot/nuget/http_response_helpers.rb +6 -1
  19. data/lib/dependabot/nuget/metadata_finder.rb +27 -3
  20. data/lib/dependabot/nuget/nuget_client.rb +23 -0
  21. data/lib/dependabot/nuget/requirement.rb +4 -1
  22. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
  23. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +11 -13
  24. data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
  25. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
  26. data/lib/dependabot/nuget/update_checker/version_finder.rb +15 -6
  27. data/lib/dependabot/nuget/update_checker.rb +4 -4
  28. data/lib/dependabot/nuget/version.rb +7 -2
  29. metadata +19 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e0fef609dc45e4b8d8f1bc8bf9c4d62ee6fd83a4fea8e28ae9022a469151875
4
- data.tar.gz: caab818c3d702d5f34c1986e7ca9dca6b7457eade3a0c5f4d4df004682193d98
3
+ metadata.gz: 9e8dda5e47b38dec152ba8797986aaaca62fde7dbed194960864b779932d6ff7
4
+ data.tar.gz: 4f1cb76b3dafc332d90ddf7acec0b39c6295d2d9749741e3229af6ad7d7e363d
5
5
  SHA512:
6
- metadata.gz: ce37d23d0440b68f8fce93364bcda68d61fb093b2435e853f956549c6c44e1bb68940ad06365e54c70ec18d667faa251455dc28f201b1b93b9410bfc1861681c
7
- data.tar.gz: '0396865c4813a9c0b357826545eb6dcbee4c350d62fceed1d97469a9de4c0f2ec751974d07862d7667b0824a24bff12453f8f5309789e6a10a1ca7c25cb0a4f6'
6
+ metadata.gz: c350131e183da643b7caf57a76acc2cb954e9d7f0a4532b340db90a064f0e1b7d735a531d2bea8efea6ee483060b3dbb8efb4a8e1567d2c9d30d88bc907848f2
7
+ data.tar.gz: 9f2a6505e1eca5a2fbe8369e8e21ce0b5eabc4c658bec9d628b5250c16f78f40a7c8b19bc0778b2b7fe6e0c3b215a71b2282407544eb0f6808bdfde3da35d8e0
@@ -17,6 +17,8 @@ using Microsoft.Build.Exceptions;
17
17
  using Microsoft.Build.Locator;
18
18
  using Microsoft.Extensions.FileSystemGlobbing;
19
19
 
20
+ using NuGet.Configuration;
21
+
20
22
  using NuGetUpdater.Core.Utilities;
21
23
 
22
24
  namespace NuGetUpdater.Core;
@@ -82,14 +84,10 @@ internal static partial class MSBuildHelper
82
84
  {
83
85
  var (resultType, tfms, errorMessage) =
84
86
  GetEvaluatedValue(targetFrameworkValue, propertyInfo, propertiesToIgnore: ["TargetFramework", "TargetFrameworks"]);
85
- if (resultType == EvaluationResultType.PropertyIgnored)
87
+ if (resultType != EvaluationResultType.Success)
86
88
  {
87
89
  continue;
88
90
  }
89
- else if (resultType != EvaluationResultType.Success)
90
- {
91
- throw new InvalidDataException(errorMessage);
92
- }
93
91
 
94
92
  if (string.IsNullOrEmpty(tfms))
95
93
  {
@@ -335,6 +333,25 @@ internal static partial class MSBuildHelper
335
333
  return projectRoot;
336
334
  }
337
335
 
336
+ private static IEnumerable<PackageSource>? LoadPackageSources(string nugetConfigPath)
337
+ {
338
+ try
339
+ {
340
+ var nugetConfigDir = Path.GetDirectoryName(nugetConfigPath);
341
+ var settings = Settings.LoadSpecificSettings(nugetConfigDir, Path.GetFileName(nugetConfigPath));
342
+ var packageSourceProvider = new PackageSourceProvider(settings);
343
+ return packageSourceProvider.LoadPackageSources();
344
+ }
345
+ catch (NuGetConfigurationException ex)
346
+ {
347
+ Console.WriteLine("Error while parsing NuGet.config");
348
+ Console.WriteLine(ex.Message);
349
+
350
+ // Nuget.config is invalid. Won't be able to do anything with specific sources.
351
+ return null;
352
+ }
353
+ }
354
+
338
355
  private static async Task<string> CreateTempProjectAsync(
339
356
  DirectoryInfo tempDir,
340
357
  string repoRoot,
@@ -345,10 +362,27 @@ internal static partial class MSBuildHelper
345
362
  var projectDirectory = Path.GetDirectoryName(projectPath);
346
363
  projectDirectory ??= repoRoot;
347
364
  var topLevelFiles = Directory.GetFiles(repoRoot);
348
- var nugetConfigPath = PathHelper.GetFileInDirectoryOrParent(projectDirectory, repoRoot, "NuGet.Config", caseSensitive: false);
365
+ var nugetConfigPath = PathHelper.GetFileInDirectoryOrParent(projectPath, repoRoot, "NuGet.Config", caseSensitive: false);
349
366
  if (nugetConfigPath is not null)
350
367
  {
368
+ // Copy nuget.config to temp project directory
351
369
  File.Copy(nugetConfigPath, Path.Combine(tempDir.FullName, "NuGet.Config"));
370
+ var nugetConfigDir = Path.GetDirectoryName(nugetConfigPath);
371
+
372
+ var packageSources = LoadPackageSources(nugetConfigPath);
373
+ if (packageSources is not null)
374
+ {
375
+ // We need to copy local package sources from the NuGet.Config file to the temp directory
376
+ foreach (var localSource in packageSources.Where(p => p.IsLocal))
377
+ {
378
+ var subDir = localSource.Source.Split(nugetConfigDir)[1];
379
+ var destPath = Path.Join(tempDir.FullName, subDir);
380
+ if (Directory.Exists(localSource.Source))
381
+ {
382
+ PathHelper.CopyDirectory(localSource.Source, destPath);
383
+ }
384
+ }
385
+ }
352
386
  }
353
387
 
354
388
  var packageReferences = string.Join(
@@ -79,4 +79,31 @@ internal static class PathHelper
79
79
 
80
80
  return null;
81
81
  }
82
+
83
+ public static void CopyDirectory(string sourceDirectory, string destinationDirectory)
84
+ {
85
+ var sourceDirInfo = new DirectoryInfo(sourceDirectory);
86
+ var destinationDirInfo = new DirectoryInfo(destinationDirectory);
87
+
88
+ if (!sourceDirInfo.Exists)
89
+ {
90
+ throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {sourceDirectory}");
91
+ }
92
+
93
+ if (!destinationDirInfo.Exists)
94
+ {
95
+ destinationDirInfo.Create();
96
+ }
97
+
98
+ foreach (var file in sourceDirInfo.EnumerateFiles())
99
+ {
100
+ file.CopyTo(Path.Combine(destinationDirectory, file.Name), true);
101
+ }
102
+
103
+ foreach (var subDir in sourceDirInfo.EnumerateDirectories())
104
+ {
105
+ var newDestinationDir = Path.Combine(destinationDirectory, subDir.Name);
106
+ CopyDirectory(subDir.FullName, newDestinationDir);
107
+ }
108
+ }
82
109
  }
@@ -2479,5 +2479,23 @@ public partial class UpdateWorkerTests
2479
2479
  </Project>
2480
2480
  """);
2481
2481
  }
2482
+
2483
+ [Fact]
2484
+ public async Task NoChange_IfTargetFrameworkCouldNotBeEvaluated()
2485
+ {
2486
+ // Make sure we don't throw if the project's TFM is an unresolvable property
2487
+ await TestNoChangeforProject("Newtonsoft.Json", "7.0.1", "13.0.1",
2488
+ projectContents: """
2489
+ <Project Sdk="Microsoft.NET.Sdk">
2490
+ <PropertyGroup>
2491
+ <TargetFramework>$(PropertyThatCannotBeResolved)</TargetFramework>
2492
+ </PropertyGroup>
2493
+ <ItemGroup>
2494
+ <PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
2495
+ </ItemGroup>
2496
+ </Project>
2497
+ """
2498
+ );
2499
+ }
2482
2500
  }
2483
2501
  }
@@ -319,6 +319,116 @@ public class MSBuildHelperTests
319
319
  Assert.Equal(expectedDependencies, actualDependencies);
320
320
  }
321
321
 
322
+ [Fact]
323
+ public async Task GetAllPackageDependencies_NugetConfigInvalid_DoesNotThrow()
324
+ {
325
+ var nugetPackagesDirectory = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
326
+ var nugetHttpCacheDirectory = Environment.GetEnvironmentVariable("NUGET_HTTP_CACHE_PATH");
327
+
328
+ try
329
+ {
330
+ using var temp = new TemporaryDirectory();
331
+
332
+ // It is important to have empty NuGet caches for this test, so override them with temp directories.
333
+ var tempNuGetPackagesDirectory = Path.Combine(temp.DirectoryPath, ".nuget", "packages");
334
+ Environment.SetEnvironmentVariable("NUGET_PACKAGES", tempNuGetPackagesDirectory);
335
+ var tempNuGetHttpCacheDirectory = Path.Combine(temp.DirectoryPath, ".nuget", "v3-cache");
336
+ Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", tempNuGetHttpCacheDirectory);
337
+
338
+ // Write the NuGet.config with a missing "/>"
339
+ await File.WriteAllTextAsync(
340
+ Path.Combine(temp.DirectoryPath, "NuGet.Config"), """
341
+ <?xml version="1.0" encoding="utf-8"?>
342
+ <configuration>
343
+ <packageSources>
344
+ <clear />
345
+ <add key="contoso" value="https://contoso.com/v3/index.json"
346
+ </packageSources>
347
+ </configuration>
348
+ """);
349
+
350
+ // Asserting it didn't throw
351
+ var actualDependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
352
+ temp.DirectoryPath,
353
+ temp.DirectoryPath,
354
+ "netstandard2.0",
355
+ [new Dependency("Newtonsoft.Json", "4.5.11", DependencyType.Unknown)]
356
+ );
357
+ }
358
+ finally
359
+ {
360
+ // Restore the NuGet caches.
361
+ Environment.SetEnvironmentVariable("NUGET_PACKAGES", nugetPackagesDirectory);
362
+ Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", nugetHttpCacheDirectory);
363
+ }
364
+ }
365
+
366
+ [Fact]
367
+ public async Task GetAllPackageDependencies_LocalNuGetRepos_AreCopiedToTempProject()
368
+ {
369
+ // If we end up using this EnvVar pattern again I think it'd be worth it to abstract it out into an IDisposable.
370
+ var nugetPackagesDirectory = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
371
+ var nugetHttpCacheDirectory = Environment.GetEnvironmentVariable("NUGET_HTTP_CACHE_PATH");
372
+ var logger = new Logger(verbose: true);
373
+ try
374
+ {
375
+ // First create a fake local nuget repository
376
+ using var restoreDir = new TemporaryDirectory();
377
+
378
+ var restoreNuGetPackagesDirectory = Path.Combine(restoreDir.DirectoryPath, ".nuget", "packages");
379
+ Environment.SetEnvironmentVariable("NUGET_PACKAGES", restoreNuGetPackagesDirectory);
380
+ var restoreNuGetHttpCacheDirectory = Path.Combine(restoreDir.DirectoryPath, ".nuget", "v3-cache");
381
+ Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", restoreNuGetHttpCacheDirectory);
382
+
383
+ using var temp = new TemporaryDirectory();
384
+ using (var restoreProjectTemp = new TemporaryDirectory())
385
+ {
386
+ // dotnet restore .csproj with things we want
387
+ await MSBuildHelper.DependenciesAreCoherentAsync(restoreProjectTemp.DirectoryPath, restoreProjectTemp.DirectoryPath, "netstandard2.0",
388
+ [new Dependency("Newtonsoft.Json", "4.5.11", DependencyType.Unknown)], logger);
389
+ Assert.True(Directory.Exists(restoreNuGetPackagesDirectory), "packages directory didn't exist");
390
+ PathHelper.CopyDirectory(restoreNuGetPackagesDirectory, Path.Combine(temp.DirectoryPath, "local_repo"));
391
+ }
392
+
393
+ // It is important to have empty NuGet caches for this test, so override them with temp directories.
394
+ var tempNuGetPackagesDirectory = Path.Combine(temp.DirectoryPath, ".nuget", "packages");
395
+ Environment.SetEnvironmentVariable("NUGET_PACKAGES", tempNuGetPackagesDirectory);
396
+ var tempNuGetHttpCacheDirectory = Path.Combine(temp.DirectoryPath, ".nuget", "v3-cache");
397
+ Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", tempNuGetHttpCacheDirectory);
398
+
399
+ // Write the NuGet.config.
400
+ await File.WriteAllTextAsync(
401
+ Path.Combine(temp.DirectoryPath, "NuGet.Config"), """
402
+ <?xml version="1.0" encoding="utf-8"?>
403
+ <configuration>
404
+ <packageSources>
405
+ <clear />
406
+ <add key="local-repo" value="local_repo" />
407
+ </packageSources>
408
+ </configuration>
409
+ """);
410
+ var expectedDependencies = new Dependency[]
411
+ {
412
+ new("Newtonsoft.Json", "4.5.11", DependencyType.Unknown),
413
+ new("NETStandard.Library", "2.0.3", DependencyType.Unknown),
414
+ };
415
+ var actualDependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
416
+ temp.DirectoryPath,
417
+ temp.DirectoryPath,
418
+ "netstandard2.0",
419
+ [new Dependency("Newtonsoft.Json", "4.5.11", DependencyType.Unknown)]
420
+ );
421
+ Assert.False(Directory.Exists(tempNuGetHttpCacheDirectory), "The .nuget/.v3-cache directory was created, meaning http was used.");
422
+ Assert.Equal(expectedDependencies, actualDependencies);
423
+ }
424
+ finally
425
+ {
426
+ // Restore the NuGet caches.
427
+ Environment.SetEnvironmentVariable("NUGET_PACKAGES", nugetPackagesDirectory);
428
+ Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", nugetHttpCacheDirectory);
429
+ }
430
+ }
431
+
322
432
  [Fact]
323
433
  public async Task AllPackageDependenciesCanBeFoundWithNuGetConfig()
324
434
  {
@@ -1,21 +1,27 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "set"
5
+ require "sorbet-runtime"
6
+
4
7
  require "dependabot/file_fetchers"
5
8
  require "dependabot/file_fetchers/base"
6
- require "set"
7
9
 
8
10
  module Dependabot
9
11
  module Nuget
10
12
  class CacheManager
13
+ extend T::Sig
14
+
15
+ sig { returns(T::Boolean) }
11
16
  def self.caching_disabled?
12
17
  ENV["DEPENDABOT_NUGET_CACHE_DISABLED"] == "true"
13
18
  end
14
19
 
20
+ sig { params(name: String).returns(T::Hash[String, T.untyped]) }
15
21
  def self.cache(name)
16
22
  return {} if caching_disabled?
17
23
 
18
- @cache ||= {}
24
+ @cache ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
19
25
  @cache[name] ||= {}
20
26
  @cache[name]
21
27
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
@@ -10,24 +10,28 @@ module Dependabot
10
10
  module Nuget
11
11
  class FileFetcher
12
12
  class ImportPathsFinder
13
+ extend T::Sig
14
+ sig { params(project_file: T.untyped).void }
13
15
  def initialize(project_file:)
14
- @project_file = project_file
16
+ @project_file = T.let(project_file, Dependabot::DependencyFile)
15
17
  end
16
18
 
19
+ sig { returns(T::Array[String]) }
17
20
  def import_paths
18
- doc = Nokogiri::XML(project_file.content)
21
+ doc = T.let(Nokogiri::XML(project_file.content), Nokogiri::XML::Document)
19
22
  doc.remove_namespaces!
20
- doc.xpath("/Project/Import").map do |import_node|
23
+ doc.xpath("/Project/Import").filter_map do |import_node|
21
24
  path = import_node.attribute("Project").value.strip.tr("\\", "/")
22
25
  path = File.join(current_dir, path) unless current_dir.nil?
23
26
  Pathname.new(path).cleanpath.to_path
24
27
  end
25
28
  end
26
29
 
30
+ sig { returns(T::Array[String]) }
27
31
  def project_reference_paths
28
- doc = Nokogiri::XML(project_file.content)
32
+ doc = T.let(Nokogiri::XML(project_file.content), Nokogiri::XML::Document)
29
33
  doc.remove_namespaces!
30
- nodes = doc.xpath("/Project/ItemGroup/ProjectReference").map do |node|
34
+ doc.xpath("/Project/ItemGroup/ProjectReference").filter_map do |node|
31
35
  attribute = node.attribute("Include")
32
36
  next unless attribute
33
37
 
@@ -35,14 +39,13 @@ module Dependabot
35
39
  path = File.join(current_dir, path) unless current_dir.nil?
36
40
  Pathname.new(path).cleanpath.to_path
37
41
  end
38
-
39
- nodes.compact
40
42
  end
41
43
 
44
+ sig { returns(T::Array[String]) }
42
45
  def project_file_paths
43
- doc = Nokogiri::XML(project_file.content)
46
+ doc = T.let(Nokogiri::XML(project_file.content), Nokogiri::XML::Document)
44
47
  doc.remove_namespaces!
45
- nodes = doc.xpath("/Project/ItemGroup/ProjectFile").map do |node|
48
+ doc.xpath("/Project/ItemGroup/ProjectFile").filter_map do |node|
46
49
  attribute = node.attribute("Include")
47
50
  next unless attribute
48
51
 
@@ -50,14 +53,14 @@ module Dependabot
50
53
  path = File.join(current_dir, path) unless current_dir.nil?
51
54
  Pathname.new(path).cleanpath.to_path
52
55
  end
53
-
54
- nodes.compact
55
56
  end
56
57
 
57
58
  private
58
59
 
60
+ sig { returns(Dependabot::DependencyFile) }
59
61
  attr_reader :project_file
60
62
 
63
+ sig { returns(T.nilable(String)) }
61
64
  def current_dir
62
65
  current_dir = project_file.name.rpartition("/").first
63
66
  current_dir = nil if current_dir == ""
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "pathname"
@@ -8,19 +8,27 @@ module Dependabot
8
8
  module Nuget
9
9
  class FileFetcher
10
10
  class SlnProjectPathsFinder
11
+ extend T::Sig
12
+
13
+ sig { params(sln_file: Dependabot::DependencyFile).void }
11
14
  def initialize(sln_file:)
12
15
  @sln_file = sln_file
13
16
  end
14
17
 
18
+ sig { returns(T::Array[String]) }
15
19
  def project_paths
16
- paths = []
17
- sln_file_lines = sln_file.content.lines
20
+ paths = T.let([], T::Array[String])
21
+ return paths unless sln_file.content
22
+
23
+ sln_file_lines = T.must(sln_file.content).lines
18
24
 
19
25
  sln_file_lines.each do |line|
20
26
  next unless line.match?(/^\s*Project\(/)
21
27
  next unless line.split('"')[5]
22
28
 
23
29
  path = line.split('"')[5]
30
+ next unless path
31
+
24
32
  path = path.tr("\\", "/")
25
33
 
26
34
  # If the path doesn't have an extension it's probably a directory
@@ -35,8 +43,10 @@ module Dependabot
35
43
 
36
44
  private
37
45
 
46
+ sig { returns(Dependabot::DependencyFile) }
38
47
  attr_reader :sln_file
39
48
 
49
+ sig { returns(T.nilable(String)) }
40
50
  def current_dir
41
51
  current_dir = sln_file.name.rpartition("/").first
42
52
  current_dir = nil if current_dir == ""