dependabot-nuget 0.288.0 → 0.290.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +19 -17
  3. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +0 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +29 -2
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +25 -4
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +33 -16
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +25 -10
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementArrayConverter.cs +39 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +1 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/ShellGitCommandHandler.cs +1 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +60 -66
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscovery.cs +2 -2
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscovery.cs +2 -2
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +11 -3
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscoveryResult.cs +1 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -4
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +54 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +0 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +1 -2
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +2 -2
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +67 -58
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +15 -44
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +4 -4
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +5 -5
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +2 -10
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +38 -33
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +25 -23
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -12
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ConsoleLogger.cs +1 -1
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +19 -19
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ILogger.cs +11 -1
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +18 -17
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -17
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +17 -9
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +96 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +87 -5
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -5
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +16 -0
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +6 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +143 -36
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +184 -48
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +5 -5
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +32 -10
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +402 -102
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +342 -2
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +60 -2
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestLogger.cs +1 -1
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/BindingRedirectsTests.cs +1 -1
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  71. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +4 -14
  72. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  73. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  74. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +95 -0
  75. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1 -7
  76. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs +1 -1
  77. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/LinuxOnlyAttribute.cs +12 -0
  78. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +558 -711
  79. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +47 -2
  80. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +4 -2
  81. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  82. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  83. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  84. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  85. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  86. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  87. data/lib/dependabot/nuget/file_fetcher.rb +11 -393
  88. data/lib/dependabot/nuget/file_parser.rb +23 -61
  89. data/lib/dependabot/nuget/file_updater.rb +28 -23
  90. data/lib/dependabot/nuget/native_helpers.rb +14 -5
  91. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  92. data/lib/dependabot/nuget/update_checker.rb +116 -190
  93. metadata +20 -32
  94. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Packages.props +0 -29
  95. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  96. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +0 -73
  97. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +0 -60
  98. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  99. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  100. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -129
  101. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -171
  102. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  103. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -82
  104. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  105. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -68
  106. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  107. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -201
  108. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  109. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  110. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  111. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  112. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  113. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  114. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  115. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  116. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  117. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -29,9 +29,54 @@ public class PathHelperTests
29
29
 
30
30
  var repoRootPath = Path.Combine(temp.DirectoryPath, "src");
31
31
 
32
- var resolvedPath = PathHelper.ResolveCaseInsensitivePathInsideRepoRoot(Path.Combine(repoRootPath, "A", "PACKAGES.CONFIG"), repoRootPath);
32
+ var resolvedPath = PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(Path.Combine(repoRootPath, "A", "PACKAGES.CONFIG"), repoRootPath);
33
33
 
34
34
  var expected = Path.Combine(temp.DirectoryPath, "src", "a", "packages.config").NormalizePathToUnix();
35
- Assert.Equal(expected, resolvedPath);
35
+ Assert.Equal(expected, resolvedPath!.First());
36
+ }
37
+
38
+ [LinuxOnlyFact]
39
+ public void VerifyMultipleMatchingPathsReturnsAllPaths()
40
+ {
41
+ using var temp = new TemporaryDirectory();
42
+ Directory.CreateDirectory(Path.Combine(temp.DirectoryPath, "src", "a"));
43
+ Directory.CreateDirectory(Path.Combine(temp.DirectoryPath, "src", "A"));
44
+
45
+ File.WriteAllText(Path.Combine(temp.DirectoryPath, "src", "a", "packages.config"), "");
46
+ File.WriteAllText(Path.Combine(temp.DirectoryPath, "src", "A", "packages.config"), "");
47
+
48
+ var repoRootPath = Path.Combine(temp.DirectoryPath, "src");
49
+
50
+ var resolvedPaths = PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(Path.Combine(repoRootPath, "A", "PACKAGES.CONFIG"), repoRootPath);
51
+
52
+ var expected = new[]
53
+ {
54
+ Path.Combine(temp.DirectoryPath, "src", "A", "packages.config").NormalizePathToUnix(),
55
+ Path.Combine(temp.DirectoryPath, "src", "a", "packages.config").NormalizePathToUnix(),
56
+ };
57
+
58
+ AssertEx.Equal(expected, resolvedPaths!);
59
+ }
60
+
61
+ [LinuxOnlyFact]
62
+ public async void FilesWithDifferentlyCasedDirectoriesCanBeResolved()
63
+ {
64
+ // arrange
65
+ using var temp = new TemporaryDirectory();
66
+ var testFile1 = "src/project1/project1.csproj";
67
+ var testFile2 = "SRC/project2/project2.csproj";
68
+ var testFiles = new[] { testFile1, testFile2 };
69
+ foreach (var testFile in testFiles)
70
+ {
71
+ var fullPath = Path.Join(temp.DirectoryPath, testFile); Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
72
+ await File.WriteAllTextAsync(fullPath, "");
73
+ }
74
+
75
+ // act
76
+ var actualFile1 = PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(Path.Join(temp.DirectoryPath, testFile1), temp.DirectoryPath);
77
+ var actualFile2 = PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(Path.Join(temp.DirectoryPath, testFile2), temp.DirectoryPath);
78
+
79
+ // assert
80
+ Assert.EndsWith(testFile1, actualFile1![0]); Assert.EndsWith(testFile2, actualFile2![0]);
36
81
  }
37
82
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "dependabot/dependency"
5
5
  require "dependabot/nuget/analysis/dependency_analysis"
6
- require "dependabot/nuget/native_discovery/native_discovery_json_reader"
6
+ require "dependabot/nuget/discovery/discovery_json_reader"
7
7
  require "json"
8
8
  require "sorbet-runtime"
9
9
 
@@ -14,7 +14,9 @@ module Dependabot
14
14
 
15
15
  sig { returns(String) }
16
16
  def self.temp_directory
17
- File.join(NativeDiscoveryJsonReader.temp_directory, "analysis")
17
+ d = File.join(Dir.tmpdir, "analysis")
18
+ FileUtils.mkdir_p(d)
19
+ d
18
20
  end
19
21
 
20
22
  sig { params(dependency_name: String).returns(String) }
@@ -20,7 +20,7 @@ module Dependabot
20
20
  T::Boolean)
21
21
  updated_dependencies = T.let(json.fetch("UpdatedDependencies"),
22
22
  T::Array[T::Hash[String, T.untyped]]).map do |dep|
23
- NativeDependencyDetails.from_json(dep)
23
+ DependencyDetails.from_json(dep)
24
24
  end
25
25
 
26
26
  DependencyAnalysis.new(
@@ -35,7 +35,7 @@ module Dependabot
35
35
  params(updated_version: String,
36
36
  can_update: T::Boolean,
37
37
  version_comes_from_multi_dependency_property: T::Boolean,
38
- updated_dependencies: T::Array[NativeDependencyDetails]).void
38
+ updated_dependencies: T::Array[DependencyDetails]).void
39
39
  end
40
40
  def initialize(updated_version:, can_update:, version_comes_from_multi_dependency_property:,
41
41
  updated_dependencies:)
@@ -54,7 +54,7 @@ module Dependabot
54
54
  sig { returns(T::Boolean) }
55
55
  attr_reader :version_comes_from_multi_dependency_property
56
56
 
57
- sig { returns(T::Array[NativeDependencyDetails]) }
57
+ sig { returns(T::Array[DependencyDetails]) }
58
58
  attr_reader :updated_dependencies
59
59
 
60
60
  sig { returns(Dependabot::Nuget::Version) }
@@ -22,6 +22,7 @@ module Dependabot
22
22
  is_transitive = T.let(json.fetch("IsTransitive"), T::Boolean)
23
23
  is_override = T.let(json.fetch("IsOverride"), T::Boolean)
24
24
  is_update = T.let(json.fetch("IsUpdate"), T::Boolean)
25
+ info_url = T.let(json.fetch("InfoUrl"), T.nilable(String))
25
26
 
26
27
  DependencyDetails.new(name: name,
27
28
  version: version,
@@ -32,7 +33,8 @@ module Dependabot
32
33
  is_direct: is_direct,
33
34
  is_transitive: is_transitive,
34
35
  is_override: is_override,
35
- is_update: is_update)
36
+ is_update: is_update,
37
+ info_url: info_url)
36
38
  end
37
39
 
38
40
  sig do
@@ -45,10 +47,11 @@ module Dependabot
45
47
  is_direct: T::Boolean,
46
48
  is_transitive: T::Boolean,
47
49
  is_override: T::Boolean,
48
- is_update: T::Boolean).void
50
+ is_update: T::Boolean,
51
+ info_url: T.nilable(String)).void
49
52
  end
50
53
  def initialize(name:, version:, type:, evaluation:, target_frameworks:, is_dev_dependency:, is_direct:,
51
- is_transitive:, is_override:, is_update:)
54
+ is_transitive:, is_override:, is_update:, info_url:)
52
55
  @name = name
53
56
  @version = version
54
57
  @type = type
@@ -59,6 +62,7 @@ module Dependabot
59
62
  @is_transitive = is_transitive
60
63
  @is_override = is_override
61
64
  @is_update = is_update
65
+ @info_url = info_url
62
66
  end
63
67
 
64
68
  sig { returns(String) }
@@ -90,6 +94,9 @@ module Dependabot
90
94
 
91
95
  sig { returns(T::Boolean) }
92
96
  attr_reader :is_update
97
+
98
+ sig { returns(T.nilable(String)) }
99
+ attr_reader :info_url
93
100
  end
94
101
  end
95
102
  end
@@ -9,11 +9,14 @@ module Dependabot
9
9
  class DependencyFileDiscovery
10
10
  extend T::Sig
11
11
 
12
- sig { params(json: T.nilable(T::Hash[String, T.untyped])).returns(T.nilable(DependencyFileDiscovery)) }
13
- def self.from_json(json)
12
+ sig do
13
+ params(json: T.nilable(T::Hash[String, T.untyped]),
14
+ directory: String).returns(T.nilable(DependencyFileDiscovery))
15
+ end
16
+ def self.from_json(json, directory)
14
17
  return nil if json.nil?
15
18
 
16
- file_path = T.let(json.fetch("FilePath"), String)
19
+ file_path = File.join(directory, T.let(json.fetch("FilePath"), String))
17
20
  dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).map do |dep|
18
21
  DependencyDetails.from_json(dep)
19
22
  end
@@ -38,7 +41,7 @@ module Dependabot
38
41
  attr_reader :dependencies
39
42
 
40
43
  sig { overridable.returns(Dependabot::FileParsers::Base::DependencySet) }
41
- def dependency_set # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
44
+ def dependency_set # rubocop:disable Metrics/PerceivedComplexity
42
45
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
43
46
 
44
47
  file_name = Pathname.new(file_path).cleanpath.to_path
@@ -62,14 +65,7 @@ module Dependabot
62
65
  # Exclude any dependencies which reference an item type
63
66
  next if dependency.name.include?("@(")
64
67
 
65
- dependency_file_name = file_name
66
- if dependency.type == "PackagesConfig"
67
- dir_name = File.dirname(file_name)
68
- dependency_file_name = "packages.config"
69
- dependency_file_name = File.join(dir_name, "packages.config") unless dir_name == "."
70
- end
71
-
72
- dependency_set << build_dependency(dependency_file_name, dependency)
68
+ dependency_set << build_dependency(file_name, dependency)
73
69
  end
74
70
 
75
71
  dependency_set
@@ -2,6 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/dependency"
5
+ require "dependabot/file_parsers/base/dependency_set"
6
+ require "dependabot/nuget/cache_manager"
5
7
  require "dependabot/nuget/discovery/workspace_discovery"
6
8
  require "json"
7
9
  require "sorbet-runtime"
@@ -11,37 +13,199 @@ module Dependabot
11
13
  class DiscoveryJsonReader
12
14
  extend T::Sig
13
15
 
14
- DISCOVERY_JSON_PATH = ".dependabot/discovery.json"
16
+ sig { returns(T::Hash[String, DiscoveryJsonReader]) }
17
+ def self.cache_directory_to_discovery_json_reader
18
+ CacheManager.cache("cache_directory_to_discovery_json_reader")
19
+ end
15
20
 
16
- sig { returns(String) }
17
- private_class_method def self.temp_directory
18
- Dir.tmpdir
21
+ sig { returns(T::Hash[String, DiscoveryJsonReader]) }
22
+ def self.cache_dependency_file_paths_to_discovery_json_reader
23
+ CacheManager.cache("cache_dependency_file_paths_to_discovery_json_reader")
24
+ end
25
+
26
+ sig { returns(T::Hash[String, String]) }
27
+ def self.cache_dependency_file_paths_to_discovery_json_path
28
+ CacheManager.cache("cache_dependency_file_paths_to_discovery_json_path")
29
+ end
30
+
31
+ sig { void }
32
+ def self.testonly_clear_caches
33
+ cache_directory_to_discovery_json_reader.clear
34
+ cache_dependency_file_paths_to_discovery_json_reader.clear
35
+ cache_dependency_file_paths_to_discovery_json_path.clear
36
+ end
37
+
38
+ sig { void }
39
+ def self.testonly_clear_discovery_files
40
+ # this will get recreated when necessary
41
+ FileUtils.rm_rf(discovery_directory)
42
+ end
43
+
44
+ # Runs NuGet dependency discovery in the given directory and returns a new instance of DiscoveryJsonReader.
45
+ # The location of the resultant JSON file is saved.
46
+ sig do
47
+ params(
48
+ repo_contents_path: String,
49
+ directory: String,
50
+ credentials: T::Array[Dependabot::Credential]
51
+ ).returns(DiscoveryJsonReader)
52
+ end
53
+ def self.run_discovery_in_directory(repo_contents_path:, directory:, credentials:)
54
+ # run discovery
55
+ job_file_path = ENV.fetch("DEPENDABOT_JOB_PATH")
56
+ discovery_json_path = discovery_file_path_from_workspace_path(directory)
57
+ unless File.exist?(discovery_json_path)
58
+ NativeHelpers.run_nuget_discover_tool(job_path: job_file_path,
59
+ repo_root: repo_contents_path,
60
+ workspace_path: directory,
61
+ output_path: discovery_json_path,
62
+ credentials: credentials)
63
+
64
+ Dependabot.logger.info("Discovery JSON content: #{File.read(discovery_json_path)}")
65
+ end
66
+ load_discovery_for_directory(repo_contents_path: repo_contents_path, directory: directory)
67
+ end
68
+
69
+ # Loads NuGet dependency discovery for the given directory and returns a new instance of DiscoveryJsonReader and
70
+ # caches the resultant object.
71
+ sig { params(repo_contents_path: String, directory: String).returns(DiscoveryJsonReader) }
72
+ def self.load_discovery_for_directory(repo_contents_path:, directory:)
73
+ cache_directory_to_discovery_json_reader[directory] ||= begin
74
+ discovery_json_reader = discovery_json_reader(repo_contents_path: repo_contents_path,
75
+ workspace_path: directory)
76
+ cache_directory_to_discovery_json_reader[directory] = discovery_json_reader
77
+ dependency_file_cache_key = cache_key_from_dependency_file_paths(discovery_json_reader.dependency_file_paths)
78
+ cache_dependency_file_paths_to_discovery_json_reader[dependency_file_cache_key] = discovery_json_reader
79
+ discovery_file_path = discovery_file_path_from_workspace_path(directory)
80
+ cache_dependency_file_paths_to_discovery_json_path[dependency_file_cache_key] = discovery_file_path
81
+
82
+ discovery_json_reader
83
+ end
84
+ end
85
+
86
+ # Retrieves the cached DiscoveryJsonReader object for the given dependency file paths.
87
+ sig { params(dependency_file_paths: T::Array[String]).returns(DiscoveryJsonReader) }
88
+ def self.load_discovery_for_dependency_file_paths(dependency_file_paths)
89
+ dependency_file_cache_key = cache_key_from_dependency_file_paths(dependency_file_paths)
90
+ T.must(cache_dependency_file_paths_to_discovery_json_reader[dependency_file_cache_key])
91
+ end
92
+
93
+ # Retrieves the cached location of the discovery JSON file for the given dependency file paths.
94
+ sig { params(dependency_file_paths: T::Array[String]).returns(String) }
95
+ def self.get_discovery_json_path_for_dependency_file_paths(dependency_file_paths)
96
+ dependency_file_cache_key = cache_key_from_dependency_file_paths(dependency_file_paths)
97
+ T.must(cache_dependency_file_paths_to_discovery_json_path[dependency_file_cache_key])
98
+ end
99
+
100
+ sig { params(repo_contents_path: String, dependency_file: Dependabot::DependencyFile).returns(String) }
101
+ def self.dependency_file_path(repo_contents_path:, dependency_file:)
102
+ dep_file_path = Pathname.new(File.join(dependency_file.directory, dependency_file.name)).cleanpath.to_path
103
+ dep_file_path.delete_prefix("#{repo_contents_path}/")
19
104
  end
20
105
 
21
106
  sig { returns(String) }
22
- def self.discovery_file_path
23
- File.join(temp_directory, DISCOVERY_JSON_PATH)
107
+ def self.discovery_map_file_path
108
+ File.join(discovery_directory, "discovery_map.json")
24
109
  end
25
110
 
26
- sig { returns(T.nilable(DependencyFile)) }
27
- def self.discovery_json
28
- return unless File.exist?(discovery_file_path)
111
+ sig { params(workspace_path: String).returns(String) }
112
+ def self.discovery_file_path_from_workspace_path(workspace_path)
113
+ # Given an update directory (also known as a workspace path), this function returns the path where the discovery
114
+ # JSON file is located. This function is called both by methods that need to write the discovery JSON file and
115
+ # by methods that need to read the discovery JSON file. This function is also called by multiple processes so
116
+ # we need a way to retain the data. This is accomplished by the following steps:
117
+ # 1. Check a well-known file for a mapping of workspace_path => discovery file path. If found, return it.
118
+ # 2. If the path is not found, generate a new path, save it to the well-known file, and return the value.
119
+ discovery_map_contents = File.exist?(discovery_map_file_path) ? File.read(discovery_map_file_path) : "{}"
120
+ discovery_map = T.let(JSON.parse(discovery_map_contents), T::Hash[String, String])
121
+
122
+ discovery_json_path = discovery_map[workspace_path]
123
+ if discovery_json_path
124
+ Dependabot.logger.info("Discovery JSON path for workspace path [#{workspace_path}] found in file " \
125
+ "[#{discovery_map_file_path}] at location [#{discovery_json_path}]")
126
+ return discovery_json_path
127
+ end
128
+
129
+ # no discovery JSON path found; generate a new one, but first find a suitable location
130
+ discovery_json_counter = 1
131
+ new_discovery_json_path = ""
132
+ loop do
133
+ new_discovery_json_path = File.join(discovery_directory, "discovery.#{discovery_json_counter}.json")
134
+ break unless File.exist?(new_discovery_json_path)
29
135
 
30
- DependencyFile.new(
136
+ discovery_json_counter += 1
137
+ end
138
+
139
+ discovery_map[workspace_path] = new_discovery_json_path
140
+
141
+ File.write(discovery_map_file_path, discovery_map.to_json)
142
+ Dependabot.logger.info("Discovery JSON path for workspace path [#{workspace_path}] created for file " \
143
+ "[#{discovery_map_file_path}] at location [#{new_discovery_json_path}]")
144
+ new_discovery_json_path
145
+ end
146
+
147
+ sig { params(dependency_file_paths: T::Array[String]).returns(String) }
148
+ def self.cache_key_from_dependency_file_paths(dependency_file_paths)
149
+ dependency_file_paths.sort.join(",")
150
+ end
151
+
152
+ sig { returns(String) }
153
+ def self.discovery_directory
154
+ t = File.join(Dir.home, ".dependabot")
155
+ FileUtils.mkdir_p(t)
156
+ t
157
+ end
158
+
159
+ sig { params(repo_contents_path: String, workspace_path: String).returns(DiscoveryJsonReader) }
160
+ def self.discovery_json_reader(repo_contents_path:, workspace_path:)
161
+ discovery_file_path = discovery_file_path_from_workspace_path(workspace_path)
162
+ discovery_json = DependencyFile.new(
31
163
  name: Pathname.new(discovery_file_path).cleanpath.to_path,
32
- directory: temp_directory,
164
+ directory: discovery_directory,
33
165
  type: "file",
34
166
  content: File.read(discovery_file_path)
35
167
  )
168
+ DiscoveryJsonReader.new(repo_contents_path: repo_contents_path, discovery_json: discovery_json)
36
169
  end
37
170
 
38
- sig { params(discovery_json: DependencyFile).void }
39
- def initialize(discovery_json:)
171
+ sig { returns(T.nilable(WorkspaceDiscovery)) }
172
+ attr_reader :workspace_discovery
173
+
174
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
175
+ attr_reader :dependency_set
176
+
177
+ sig { returns(T::Array[String]) }
178
+ attr_reader :dependency_file_paths
179
+
180
+ sig { params(repo_contents_path: String, discovery_json: DependencyFile).void }
181
+ def initialize(repo_contents_path:, discovery_json:)
182
+ @repo_contents_path = repo_contents_path
40
183
  @discovery_json = discovery_json
184
+ @workspace_discovery = T.let(read_workspace_discovery, T.nilable(Dependabot::Nuget::WorkspaceDiscovery))
185
+ @dependency_set = T.let(read_dependency_set, Dependabot::FileParsers::Base::DependencySet)
186
+ @dependency_file_paths = T.let(read_dependency_file_paths, T::Array[String])
187
+ end
188
+
189
+ private
190
+
191
+ sig { returns(String) }
192
+ attr_reader :repo_contents_path
193
+
194
+ sig { returns(DependencyFile) }
195
+ attr_reader :discovery_json
196
+
197
+ sig { returns(T.nilable(WorkspaceDiscovery)) }
198
+ def read_workspace_discovery
199
+ return nil unless discovery_json.content
200
+
201
+ parsed_json = T.let(JSON.parse(T.must(discovery_json.content)), T::Hash[String, T.untyped])
202
+ WorkspaceDiscovery.from_json(parsed_json)
203
+ rescue JSON::ParserError
204
+ raise Dependabot::DependencyFileNotParseable, discovery_json.path
41
205
  end
42
206
 
43
207
  sig { returns(Dependabot::FileParsers::Base::DependencySet) }
44
- def dependency_set
208
+ def read_dependency_set
45
209
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
46
210
  return dependency_set unless workspace_discovery
47
211
 
@@ -49,9 +213,6 @@ module Dependabot
49
213
  workspace_result.projects.each do |project|
50
214
  dependency_set += project.dependency_set
51
215
  end
52
- if workspace_result.directory_packages_props
53
- dependency_set += T.must(workspace_result.directory_packages_props).dependency_set
54
- end
55
216
  if workspace_result.dotnet_tools_json
56
217
  dependency_set += T.must(workspace_result.dotnet_tools_json).dependency_set
57
218
  end
@@ -60,22 +221,46 @@ module Dependabot
60
221
  dependency_set
61
222
  end
62
223
 
63
- sig { returns(T.nilable(WorkspaceDiscovery)) }
64
- def workspace_discovery
65
- @workspace_discovery ||= T.let(begin
66
- return nil unless discovery_json.content
224
+ sig { returns(T::Array[String]) }
225
+ def read_dependency_file_paths
226
+ dependency_file_paths = T.let([], T::Array[T.nilable(String)])
227
+ dependency_file_paths << dependency_file_path_from_repo_path("global.json") if workspace_discovery&.global_json
228
+ if workspace_discovery&.dotnet_tools_json
229
+ dependency_file_paths << dependency_file_path_from_repo_path(".config/dotnet-tools.json")
230
+ end
67
231
 
68
- parsed_json = T.let(JSON.parse(T.must(discovery_json.content)), T::Hash[String, T.untyped])
69
- WorkspaceDiscovery.from_json(parsed_json)
70
- end, T.nilable(WorkspaceDiscovery))
71
- rescue JSON::ParserError
72
- raise Dependabot::DependencyFileNotParseable, discovery_json.path
232
+ projects = workspace_discovery&.projects || []
233
+ projects.each do |project|
234
+ dependency_file_paths << dependency_file_path_from_repo_path(project.file_path)
235
+ dependency_file_paths += project.imported_files.map do |f|
236
+ dependency_file_path_from_project_path(project.file_path, f)
237
+ end
238
+ dependency_file_paths += project.additional_files.map do |f|
239
+ dependency_file_path_from_project_path(project.file_path, f)
240
+ end
241
+ end
242
+
243
+ deduped_dependency_file_paths = T.let(Set.new(dependency_file_paths.compact), T::Set[String])
244
+ result = deduped_dependency_file_paths.sort
245
+ result
73
246
  end
74
247
 
75
- private
248
+ sig { params(path_parts: String).returns(T.nilable(String)) }
249
+ def dependency_file_path_from_repo_path(*path_parts)
250
+ path_parts = path_parts.map { |p| p.delete_prefix("/").delete_suffix("/") }
251
+ normalized_repo_path = Pathname.new(path_parts.join("/")).cleanpath.to_path.delete_prefix("/")
252
+ full_path = Pathname.new(File.join(repo_contents_path, normalized_repo_path)).cleanpath.to_path
253
+ return unless File.exist?(full_path)
76
254
 
77
- sig { returns(DependencyFile) }
78
- attr_reader :discovery_json
255
+ normalized_repo_path = "/#{normalized_repo_path}" unless normalized_repo_path.start_with?("/")
256
+ normalized_repo_path
257
+ end
258
+
259
+ sig { params(project_path: String, relative_file_path: String).returns(T.nilable(String)) }
260
+ def dependency_file_path_from_project_path(project_path, relative_file_path)
261
+ project_directory = File.dirname(project_path)
262
+ dependency_file_path_from_repo_path(project_directory, relative_file_path)
263
+ end
79
264
  end
80
265
  end
81
266
  end
@@ -10,41 +10,68 @@ module Dependabot
10
10
  class ProjectDiscovery < DependencyFileDiscovery
11
11
  extend T::Sig
12
12
 
13
+ # rubocop:disable Metrics/AbcSize
13
14
  sig do
14
- params(json: T.nilable(T::Hash[String, T.untyped])).returns(T.nilable(ProjectDiscovery))
15
+ override.params(json: T.nilable(T::Hash[String, T.untyped]),
16
+ directory: String).returns(T.nilable(ProjectDiscovery))
15
17
  end
16
- def self.from_json(json)
18
+ def self.from_json(json, directory)
17
19
  return nil if json.nil?
18
20
 
19
- file_path = T.let(json.fetch("FilePath"), String)
21
+ file_path = File.join(directory, T.let(json.fetch("FilePath"), String))
20
22
  properties = T.let(json.fetch("Properties"), T::Array[T::Hash[String, T.untyped]]).map do |prop|
21
23
  PropertyDetails.from_json(prop)
22
24
  end
23
25
  target_frameworks = T.let(json.fetch("TargetFrameworks"), T::Array[String])
24
26
  referenced_project_paths = T.let(json.fetch("ReferencedProjectPaths"), T::Array[String])
25
- dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).map do |dep|
26
- DependencyDetails.from_json(dep)
27
+ dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).filter_map do |dep|
28
+ details = DependencyDetails.from_json(dep)
29
+ next unless details.version # can't do anything without a version
30
+
31
+ version = T.must(details.version)
32
+ next unless version.length.positive? # can't do anything with an empty version
33
+
34
+ next if version.include? "," # can't do anything with a range
35
+
36
+ next if version.include? "*" # can't do anything with a wildcard
37
+
38
+ details
27
39
  end
40
+ imported_files = T.let(json.fetch("ImportedFiles"), T::Array[String])
41
+ additional_files = T.let(json.fetch("AdditionalFiles"), T::Array[String])
28
42
 
29
43
  ProjectDiscovery.new(file_path: file_path,
30
44
  properties: properties,
31
45
  target_frameworks: target_frameworks,
32
46
  referenced_project_paths: referenced_project_paths,
33
- dependencies: dependencies)
47
+ dependencies: dependencies,
48
+ imported_files: imported_files,
49
+ additional_files: additional_files)
34
50
  end
51
+ # rubocop:enable Metrics/AbcSize
35
52
 
36
53
  sig do
37
54
  params(file_path: String,
38
55
  properties: T::Array[PropertyDetails],
39
56
  target_frameworks: T::Array[String],
40
57
  referenced_project_paths: T::Array[String],
41
- dependencies: T::Array[DependencyDetails]).void
58
+ dependencies: T::Array[DependencyDetails],
59
+ imported_files: T::Array[String],
60
+ additional_files: T::Array[String]).void
42
61
  end
43
- def initialize(file_path:, properties:, target_frameworks:, referenced_project_paths:, dependencies:)
62
+ def initialize(file_path:,
63
+ properties:,
64
+ target_frameworks:,
65
+ referenced_project_paths:,
66
+ dependencies:,
67
+ imported_files:,
68
+ additional_files:)
44
69
  super(file_path: file_path, dependencies: dependencies)
45
70
  @properties = properties
46
71
  @target_frameworks = target_frameworks
47
72
  @referenced_project_paths = referenced_project_paths
73
+ @imported_files = imported_files
74
+ @additional_files = additional_files
48
75
  end
49
76
 
50
77
  sig { returns(T::Array[PropertyDetails]) }
@@ -56,6 +83,12 @@ module Dependabot
56
83
  sig { returns(T::Array[String]) }
57
84
  attr_reader :referenced_project_paths
58
85
 
86
+ sig { returns(T::Array[String]) }
87
+ attr_reader :imported_files
88
+
89
+ sig { returns(T::Array[String]) }
90
+ attr_reader :additional_files
91
+
59
92
  sig { override.returns(Dependabot::FileParsers::Base::DependencySet) }
60
93
  def dependency_set
61
94
  if target_frameworks.empty? && file_path.end_with?("proj")
@@ -2,8 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/nuget/discovery/dependency_file_discovery"
5
- require "dependabot/nuget/discovery/directory_packages_props_discovery"
6
5
  require "dependabot/nuget/discovery/project_discovery"
6
+ require "dependabot/nuget/native_helpers"
7
7
  require "sorbet-runtime"
8
8
 
9
9
  module Dependabot
@@ -13,49 +13,44 @@ module Dependabot
13
13
 
14
14
  sig { params(json: T::Hash[String, T.untyped]).returns(WorkspaceDiscovery) }
15
15
  def self.from_json(json)
16
- file_path = T.let(json.fetch("FilePath"), String)
16
+ Dependabot::Nuget::NativeHelpers.ensure_no_errors(json)
17
+
18
+ path = T.let(json.fetch("Path"), String)
19
+ path = "/" + path unless path.start_with?("/")
17
20
  projects = T.let(json.fetch("Projects"), T::Array[T::Hash[String, T.untyped]]).filter_map do |project|
18
- ProjectDiscovery.from_json(project)
21
+ ProjectDiscovery.from_json(project, path)
19
22
  end
20
- directory_packages_props = DirectoryPackagesPropsDiscovery
21
- .from_json(T.let(json.fetch("DirectoryPackagesProps"),
22
- T.nilable(T::Hash[String, T.untyped])))
23
23
  global_json = DependencyFileDiscovery
24
- .from_json(T.let(json.fetch("GlobalJson"), T.nilable(T::Hash[String, T.untyped])))
24
+ .from_json(T.let(json.fetch("GlobalJson"), T.nilable(T::Hash[String, T.untyped])), path)
25
25
  dotnet_tools_json = DependencyFileDiscovery
26
- .from_json(T.let(json.fetch("DotNetToolsJson"), T.nilable(T::Hash[String, T.untyped])))
26
+ .from_json(T.let(json.fetch("DotNetToolsJson"),
27
+ T.nilable(T::Hash[String, T.untyped])), path)
27
28
 
28
- WorkspaceDiscovery.new(file_path: file_path,
29
+ WorkspaceDiscovery.new(path: path,
29
30
  projects: projects,
30
- directory_packages_props: directory_packages_props,
31
31
  global_json: global_json,
32
32
  dotnet_tools_json: dotnet_tools_json)
33
33
  end
34
34
 
35
35
  sig do
36
- params(file_path: String,
36
+ params(path: String,
37
37
  projects: T::Array[ProjectDiscovery],
38
- directory_packages_props: T.nilable(DirectoryPackagesPropsDiscovery),
39
38
  global_json: T.nilable(DependencyFileDiscovery),
40
39
  dotnet_tools_json: T.nilable(DependencyFileDiscovery)).void
41
40
  end
42
- def initialize(file_path:, projects:, directory_packages_props:, global_json:, dotnet_tools_json:)
43
- @file_path = file_path
41
+ def initialize(path:, projects:, global_json:, dotnet_tools_json:)
42
+ @path = path
44
43
  @projects = projects
45
- @directory_packages_props = directory_packages_props
46
44
  @global_json = global_json
47
45
  @dotnet_tools_json = dotnet_tools_json
48
46
  end
49
47
 
50
48
  sig { returns(String) }
51
- attr_reader :file_path
49
+ attr_reader :path
52
50
 
53
51
  sig { returns(T::Array[ProjectDiscovery]) }
54
52
  attr_reader :projects
55
53
 
56
- sig { returns(T.nilable(DirectoryPackagesPropsDiscovery)) }
57
- attr_reader :directory_packages_props
58
-
59
54
  sig { returns(T.nilable(DependencyFileDiscovery)) }
60
55
  attr_reader :global_json
61
56