dependabot-nuget 0.246.0 → 0.248.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) 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 +89 -37
  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 +13 -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/nuget_config_credential_helpers.rb +10 -1
  22. data/lib/dependabot/nuget/requirement.rb +21 -9
  23. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
  24. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +87 -21
  25. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +25 -3
  26. data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
  27. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +32 -9
  28. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
  29. data/lib/dependabot/nuget/update_checker/version_finder.rb +178 -64
  30. data/lib/dependabot/nuget/update_checker.rb +76 -32
  31. data/lib/dependabot/nuget/version.rb +7 -2
  32. 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: 60679d8e50f3e96aa05400b03ccc9e0dd491c1a7cf340c4c7a39bcb48675c3f6
4
+ data.tar.gz: 235eb5b57423534578945568766179c2805c6c1e19b5bdb806cc4fd5a5e78dc7
5
5
  SHA512:
6
- metadata.gz: ce37d23d0440b68f8fce93364bcda68d61fb093b2435e853f956549c6c44e1bb68940ad06365e54c70ec18d667faa251455dc28f201b1b93b9410bfc1861681c
7
- data.tar.gz: '0396865c4813a9c0b357826545eb6dcbee4c350d62fceed1d97469a9de4c0f2ec751974d07862d7667b0824a24bff12453f8f5309789e6a10a1ca7c25cb0a4f6'
6
+ metadata.gz: 1976d20f2b23920eb44176098e89109ee5e969f7e12eaa959e06948e543b9383dcf08c7521eeb9f69602fab37f17ffaafc937e203cce68439327d9735590e1b9
7
+ data.tar.gz: d240a0489a0ab0dc72283ea736a55965cb13a2fe375cb7eb5a98beb473f9e227bf6ec52a07fba05f2177ee228c716197f60d5fe35d3170b1687aaa07648915eb
@@ -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 == ""