dependabot-nuget 0.289.0 → 0.290.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +26 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +2 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +3 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -9
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +24 -4
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +2 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +13 -12
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +2 -2
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +0 -4
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +7 -31
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +340 -0
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +0 -12
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +55 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -6
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +557 -713
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +2 -2
  43. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +1 -1
  44. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  45. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  46. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  47. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  48. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  49. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  50. data/lib/dependabot/nuget/file_fetcher.rb +2 -3
  51. data/lib/dependabot/nuget/file_parser.rb +2 -3
  52. data/lib/dependabot/nuget/file_updater.rb +13 -13
  53. data/lib/dependabot/nuget/native_helpers.rb +14 -5
  54. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  55. data/lib/dependabot/nuget/update_checker.rb +116 -190
  56. metadata +18 -29
  57. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  58. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  59. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  60. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -122
  61. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -277
  62. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  63. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -104
  64. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  65. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -61
  66. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  67. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -214
  68. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  69. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  70. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  71. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  72. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  73. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  74. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  75. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  76. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  77. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -51,11 +51,11 @@ public class PathHelperTests
51
51
 
52
52
  var expected = new[]
53
53
  {
54
- Path.Combine(temp.DirectoryPath, "src", "a", "packages.config").NormalizePathToUnix(),
55
54
  Path.Combine(temp.DirectoryPath, "src", "A", "packages.config").NormalizePathToUnix(),
55
+ Path.Combine(temp.DirectoryPath, "src", "a", "packages.config").NormalizePathToUnix(),
56
56
  };
57
57
 
58
- Assert.Equal(expected, resolvedPaths!);
58
+ AssertEx.Equal(expected, resolvedPaths!);
59
59
  }
60
60
 
61
61
  [LinuxOnlyFact]
@@ -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
 
@@ -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
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "dependabot/file_fetchers"
5
5
  require "dependabot/file_fetchers/base"
6
- require "dependabot/nuget/native_discovery/native_discovery_json_reader"
6
+ require "dependabot/nuget/discovery/discovery_json_reader"
7
7
  require "dependabot/nuget/native_helpers"
8
8
  require "set"
9
9
  require "sorbet-runtime"
@@ -26,13 +26,12 @@ module Dependabot
26
26
 
27
27
  sig { override.returns(T::Array[DependencyFile]) }
28
28
  def fetch_files
29
- discovery_json_reader = NativeDiscoveryJsonReader.run_discovery_in_directory(
29
+ discovery_json_reader = DiscoveryJsonReader.run_discovery_in_directory(
30
30
  repo_contents_path: T.must(repo_contents_path),
31
31
  directory: directory,
32
32
  credentials: credentials
33
33
  )
34
34
 
35
- NativeDiscoveryJsonReader.debug_report_discovery_files(error_if_missing: false)
36
35
  discovery_json_reader.dependency_file_paths.map do |p|
37
36
  relative_path = Pathname.new(p).relative_path_from(directory).to_path
38
37
  fetch_file_from_host(relative_path)
@@ -4,7 +4,7 @@
4
4
  require "dependabot/dependency"
5
5
  require "dependabot/file_parsers"
6
6
  require "dependabot/file_parsers/base"
7
- require "dependabot/nuget/native_discovery/native_discovery_json_reader"
7
+ require "dependabot/nuget/discovery/discovery_json_reader"
8
8
  require "dependabot/nuget/native_helpers"
9
9
  require "sorbet-runtime"
10
10
 
@@ -27,9 +27,8 @@ module Dependabot
27
27
  sig { returns(T::Array[Dependabot::Dependency]) }
28
28
  def dependencies
29
29
  @dependencies ||= T.let(begin
30
- NativeDiscoveryJsonReader.debug_report_discovery_files(error_if_missing: true)
31
30
  directory = source&.directory || "/"
32
- discovery_json_reader = NativeDiscoveryJsonReader.run_discovery_in_directory(
31
+ discovery_json_reader = DiscoveryJsonReader.run_discovery_in_directory(
33
32
  repo_contents_path: T.must(repo_contents_path),
34
33
  directory: directory,
35
34
  credentials: credentials