dependabot-nuget 0.247.0 → 0.249.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +57 -0
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +1 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +25 -5
  5. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +1 -0
  6. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +2 -0
  7. data/lib/dependabot/nuget/file_fetcher.rb +12 -8
  8. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +1 -0
  9. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +1 -0
  10. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +1 -0
  11. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +1 -0
  12. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +2 -0
  13. data/lib/dependabot/nuget/file_parser.rb +42 -11
  14. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +1 -0
  15. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +10 -1
  16. data/lib/dependabot/nuget/requirement.rb +17 -8
  17. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +28 -7
  18. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +70 -19
  19. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +76 -8
  20. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +25 -3
  21. data/lib/dependabot/nuget/update_checker/property_updater.rb +108 -44
  22. data/lib/dependabot/nuget/update_checker/repository_finder.rb +90 -18
  23. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +32 -9
  24. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +8 -3
  25. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +51 -13
  26. data/lib/dependabot/nuget/update_checker/version_finder.rb +167 -62
  27. data/lib/dependabot/nuget/update_checker.rb +73 -29
  28. metadata +5 -5
@@ -1,9 +1,11 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
- require "zip"
5
+ require "sorbet-runtime"
6
6
  require "stringio"
7
+ require "zip"
8
+
7
9
  require "dependabot/update_checkers/base"
8
10
  require "dependabot/nuget/version"
9
11
 
@@ -11,21 +13,34 @@ module Dependabot
11
13
  module Nuget
12
14
  class UpdateChecker < Dependabot::UpdateCheckers::Base
13
15
  class DependencyFinder
16
+ extend T::Sig
17
+
14
18
  require_relative "requirements_updater"
15
19
  require_relative "nuspec_fetcher"
16
20
 
21
+ sig { returns(T::Hash[String, T.untyped]) }
17
22
  def self.transitive_dependencies_cache
18
23
  CacheManager.cache("dependency_finder_transitive_dependencies")
19
24
  end
20
25
 
26
+ sig { returns(T::Hash[String, T.untyped]) }
21
27
  def self.updated_peer_dependencies_cache
22
28
  CacheManager.cache("dependency_finder_updated_peer_dependencies")
23
29
  end
24
30
 
31
+ sig { returns(T::Hash[String, T.untyped]) }
25
32
  def self.fetch_dependencies_cache
26
33
  CacheManager.cache("dependency_finder_fetch_dependencies")
27
34
  end
28
35
 
36
+ sig do
37
+ params(
38
+ dependency: Dependabot::Dependency,
39
+ dependency_files: T::Array[Dependabot::DependencyFile],
40
+ credentials: T::Array[Dependabot::Credential],
41
+ repo_contents_path: T.nilable(String)
42
+ ).void
43
+ end
29
44
  def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
30
45
  @dependency = dependency
31
46
  @dependency_files = dependency_files
@@ -33,6 +48,7 @@ module Dependabot
33
48
  @repo_contents_path = repo_contents_path
34
49
  end
35
50
 
51
+ sig { returns(T::Array[Dependabot::Dependency]) }
36
52
  def transitive_dependencies
37
53
  key = "#{dependency.name.downcase}::#{dependency.version}"
38
54
  cache = DependencyFinder.transitive_dependencies_cache
@@ -44,7 +60,7 @@ module Dependabot
44
60
 
45
61
  cache[key] = fetch_transitive_dependencies(
46
62
  @dependency.name,
47
- @dependency.version
63
+ T.must(@dependency.version)
48
64
  ).map do |dependency_info|
49
65
  package_name = dependency_info["packageName"]
50
66
  target_version = dependency_info["version"]
@@ -65,13 +81,14 @@ module Dependabot
65
81
  cache[key]
66
82
  end
67
83
 
84
+ sig { returns(T::Array[Dependabot::Dependency]) }
68
85
  def updated_peer_dependencies
69
86
  key = "#{dependency.name.downcase}::#{dependency.version}"
70
87
  cache = DependencyFinder.updated_peer_dependencies_cache
71
88
 
72
89
  cache[key] ||= fetch_transitive_dependencies(
73
90
  @dependency.name,
74
- @dependency.version
91
+ T.must(@dependency.version)
75
92
  ).filter_map do |dependency_info|
76
93
  package_name = dependency_info["packageName"]
77
94
  target_version = dependency_info["version"]
@@ -104,48 +121,79 @@ module Dependabot
104
121
 
105
122
  private
106
123
 
107
- attr_reader :dependency, :dependency_files, :credentials, :repo_contents_path
124
+ sig { returns(Dependabot::Dependency) }
125
+ attr_reader :dependency
126
+
127
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
128
+ attr_reader :dependency_files
129
+
130
+ sig { returns(T::Array[Dependabot::Credential]) }
131
+ attr_reader :credentials
108
132
 
133
+ sig { returns(T.nilable(String)) }
134
+ attr_reader :repo_contents_path
135
+
136
+ sig do
137
+ params(
138
+ dep: Dependabot::Dependency,
139
+ target_version_details: T::Hash[Symbol, T.untyped]
140
+ )
141
+ .returns(T::Array[T::Hash[String, T.untyped]])
142
+ end
109
143
  def updated_requirements(dep, target_version_details)
110
- @updated_requirements ||= {}
144
+ @updated_requirements ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
111
145
  @updated_requirements[dep.name] ||=
112
146
  RequirementsUpdater.new(
113
147
  requirements: dep.requirements,
114
148
  latest_version: target_version_details.fetch(:version).to_s,
115
- source_details: target_version_details
116
- &.slice(:nuspec_url, :repo_url, :source_url)
149
+ source_details: target_version_details.slice(:nuspec_url, :repo_url, :source_url)
117
150
  ).updated_requirements
118
151
  end
119
152
 
153
+ sig { returns(T::Array[Dependabot::Dependency]) }
120
154
  def top_level_dependencies
121
155
  @top_level_dependencies ||=
122
- Nuget::FileParser.new(
123
- dependency_files: dependency_files,
124
- source: nil
125
- ).parse.select(&:top_level?)
156
+ T.let(
157
+ Nuget::FileParser.new(
158
+ dependency_files: dependency_files,
159
+ source: nil
160
+ ).parse.select(&:top_level?),
161
+ T.nilable(T::Array[Dependabot::Dependency])
162
+ )
126
163
  end
127
164
 
165
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
128
166
  def nuget_configs
129
167
  @nuget_configs ||=
130
- @dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
168
+ T.let(
169
+ @dependency_files.select { |f| f.name.match?(/nuget\.config$/i) },
170
+ T.nilable(T::Array[Dependabot::DependencyFile])
171
+ )
131
172
  end
132
173
 
174
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
133
175
  def dependency_urls
134
176
  @dependency_urls ||=
135
- RepositoryFinder.new(
136
- dependency: @dependency,
137
- credentials: @credentials,
138
- config_files: nuget_configs
139
- ).dependency_urls
140
- .select { |url| url.fetch(:repository_type) == "v3" }
177
+ T.let(
178
+ RepositoryFinder.new(
179
+ dependency: @dependency,
180
+ credentials: @credentials,
181
+ config_files: nuget_configs
182
+ )
183
+ .dependency_urls
184
+ .select { |url| url.fetch(:repository_type) == "v3" },
185
+ T.nilable(T::Array[T::Hash[Symbol, String]])
186
+ )
141
187
  end
142
188
 
189
+ sig { params(package_id: String, package_version: String).returns(T::Array[T::Hash[String, T.untyped]]) }
143
190
  def fetch_transitive_dependencies(package_id, package_version)
144
191
  all_dependencies = {}
145
192
  fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
146
193
  all_dependencies.map { |_, dependency_info| dependency_info }
147
194
  end
148
195
 
196
+ sig { params(package_id: String, package_version: String, all_dependencies: T::Hash[String, T.untyped]).void }
149
197
  def fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
150
198
  dependencies = fetch_dependencies(package_id, package_version)
151
199
  return unless dependencies.any?
@@ -175,6 +223,7 @@ module Dependabot
175
223
  end
176
224
  end
177
225
 
226
+ sig { params(package_id: String, package_version: String).returns(T::Array[T::Hash[String, T.untyped]]) }
178
227
  def fetch_dependencies(package_id, package_version)
179
228
  key = "#{package_id.downcase}::#{package_version}"
180
229
  cache = DependencyFinder.fetch_dependencies_cache
@@ -191,6 +240,7 @@ module Dependabot
191
240
  cache[key]
192
241
  end
193
242
 
243
+ sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Array[T::Hash[String, String]]) }
194
244
  def read_dependencies_from_nuspec(nuspec_xml) # rubocop:disable Metrics/PerceivedComplexity
195
245
  # we want to exclude development dependencies from the lookup
196
246
  allowed_attributes = %w(all compile native runtime)
@@ -223,6 +273,7 @@ module Dependabot
223
273
  dependency_list
224
274
  end
225
275
 
276
+ sig { params(dep: Dependabot::Dependency).returns(Dependabot::Nuget::UpdateChecker::VersionFinder) }
226
277
  def version_finder(dep)
227
278
  VersionFinder.new(
228
279
  dependency: dep,
@@ -1,23 +1,43 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
- require "zip"
6
5
  require "stringio"
6
+ require "sorbet-runtime"
7
+ require "zip"
8
+
7
9
  require "dependabot/nuget/http_response_helpers"
8
10
 
9
11
  module Dependabot
10
12
  module Nuget
11
13
  class NupkgFetcher
14
+ extend T::Sig
15
+
12
16
  require_relative "repository_finder"
13
17
 
18
+ sig do
19
+ params(
20
+ dependency_urls: T::Array[T::Hash[Symbol, String]],
21
+ package_id: String,
22
+ package_version: String
23
+ )
24
+ .returns(T.nilable(String))
25
+ end
14
26
  def self.fetch_nupkg_buffer(dependency_urls, package_id, package_version)
15
27
  # check all repositories for the first one that has the nupkg
16
- dependency_urls.reduce(nil) do |nupkg_buffer, repository_details|
28
+ dependency_urls.reduce(T.let(nil, T.nilable(String))) do |nupkg_buffer, repository_details|
17
29
  nupkg_buffer || fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version)
18
30
  end
19
31
  end
20
32
 
33
+ sig do
34
+ params(
35
+ repository_details: T::Hash[Symbol, T.untyped],
36
+ package_id: T.nilable(String),
37
+ package_version: T.nilable(String)
38
+ )
39
+ .returns(T.nilable(String))
40
+ end
21
41
  def self.fetch_nupkg_url_from_repository(repository_details, package_id, package_version)
22
42
  return unless package_id && package_version && !package_version.empty?
23
43
 
@@ -35,6 +55,14 @@ module Dependabot
35
55
  package_url
36
56
  end
37
57
 
58
+ sig do
59
+ params(
60
+ repository_details: T::Hash[Symbol, T.untyped],
61
+ package_id: String,
62
+ package_version: String
63
+ )
64
+ .returns(T.nilable(String))
65
+ end
38
66
  def self.fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version)
39
67
  package_url = fetch_nupkg_url_from_repository(repository_details, package_id, package_version)
40
68
  return unless package_url
@@ -43,6 +71,14 @@ module Dependabot
43
71
  fetch_stream(package_url, auth_header)
44
72
  end
45
73
 
74
+ sig do
75
+ params(
76
+ repository_details: T::Hash[Symbol, T.untyped],
77
+ package_id: String,
78
+ package_version: String
79
+ )
80
+ .returns(T.nilable(String))
81
+ end
46
82
  def self.get_nuget_v3_package_url(repository_details, package_id, package_version)
47
83
  base_url = repository_details[:base_url]
48
84
  unless base_url
@@ -57,15 +93,23 @@ module Dependabot
57
93
 
58
94
  # rubocop:disable Metrics/CyclomaticComplexity
59
95
  # rubocop:disable Metrics/PerceivedComplexity
96
+ sig do
97
+ params(
98
+ repository_details: T::Hash[Symbol, T.untyped],
99
+ package_id: String,
100
+ package_version: String
101
+ )
102
+ .returns(T.nilable(String))
103
+ end
60
104
  def self.get_nuget_v3_package_url_from_search(repository_details, package_id, package_version)
61
105
  search_url = repository_details[:search_url]
62
106
  return nil unless search_url
63
107
 
64
108
  # get search result
65
109
  search_result_response = fetch_url(search_url, repository_details)
66
- return nil unless search_result_response.status == 200
110
+ return nil unless search_result_response&.status == 200
67
111
 
68
- search_response_body = HttpResponseHelpers.remove_wrapping_zero_width_chars(search_result_response.body)
112
+ search_response_body = HttpResponseHelpers.remove_wrapping_zero_width_chars(T.must(search_result_response).body)
69
113
  search_results = JSON.parse(search_response_body)
70
114
 
71
115
  # find matching package and version
@@ -90,15 +134,23 @@ module Dependabot
90
134
  # rubocop:enable Metrics/PerceivedComplexity
91
135
  # rubocop:enable Metrics/CyclomaticComplexity
92
136
 
137
+ sig do
138
+ params(
139
+ repository_details: T::Hash[Symbol, T.untyped],
140
+ package_id: String,
141
+ package_version: String
142
+ )
143
+ .returns(T.nilable(String))
144
+ end
93
145
  def self.get_nuget_v2_package_url(repository_details, package_id, package_version)
94
146
  # get package XML
95
147
  base_url = repository_details[:base_url].delete_suffix("/")
96
148
  package_url = "#{base_url}/Packages(Id='#{package_id}',Version='#{package_version}')"
97
149
  response = fetch_url(package_url, repository_details)
98
- return nil unless response.status == 200
150
+ return nil unless response&.status == 200
99
151
 
100
152
  # find relevant element
101
- doc = Nokogiri::XML(response.body)
153
+ doc = Nokogiri::XML(T.must(response).body)
102
154
  doc.remove_namespaces!
103
155
 
104
156
  content_element = doc.xpath("/entry/content")
@@ -106,6 +158,14 @@ module Dependabot
106
158
  nupkg_url
107
159
  end
108
160
 
161
+ sig do
162
+ params(
163
+ stream_url: String,
164
+ auth_header: T::Hash[String, String],
165
+ max_redirects: Integer
166
+ )
167
+ .returns(T.nilable(String))
168
+ end
109
169
  def self.fetch_stream(stream_url, auth_header, max_redirects = 5)
110
170
  current_url = stream_url
111
171
  current_redirects = 0
@@ -128,17 +188,25 @@ module Dependabot
128
188
  current_redirects += 1
129
189
  return nil if current_redirects > max_redirects
130
190
 
131
- current_url = response.headers["Location"]
191
+ current_url = T.must(response.headers["Location"])
132
192
  else
133
193
  return nil
134
194
  end
135
195
  end
136
196
  end
137
197
 
198
+ sig do
199
+ params(
200
+ url: String,
201
+ repository_details: T::Hash[Symbol, T.untyped]
202
+ )
203
+ .returns(T.nilable(Excon::Response))
204
+ end
138
205
  def self.fetch_url(url, repository_details)
139
206
  fetch_url_with_auth(url, repository_details.fetch(:auth_header))
140
207
  end
141
208
 
209
+ sig { params(url: String, auth_header: T::Hash[T.any(String, Symbol), T.untyped]).returns(Excon::Response) }
142
210
  def self.fetch_url_with_auth(url, auth_header)
143
211
  cache = CacheManager.cache("nupkg_fetcher_cache")
144
212
  cache[url] ||= Dependabot::RegistryClient.get(
@@ -1,23 +1,42 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
- require "zip"
6
5
  require "stringio"
6
+ require "sorbet-runtime"
7
+ require "zip"
7
8
 
8
9
  module Dependabot
9
10
  module Nuget
10
11
  class NuspecFetcher
12
+ extend T::Sig
13
+
11
14
  require_relative "nupkg_fetcher"
12
15
  require_relative "repository_finder"
13
16
 
17
+ sig do
18
+ params(
19
+ dependency_urls: T::Array[T::Hash[Symbol, String]],
20
+ package_id: String,
21
+ package_version: T.nilable(String)
22
+ )
23
+ .returns(T.nilable(Nokogiri::XML::Document))
24
+ end
14
25
  def self.fetch_nuspec(dependency_urls, package_id, package_version)
15
26
  # check all repositories for the first one that has the nuspec
16
- dependency_urls.reduce(nil) do |nuspec_xml, repository_details|
27
+ dependency_urls.reduce(T.let(nil, T.nilable(Nokogiri::XML::Document))) do |nuspec_xml, repository_details|
17
28
  nuspec_xml || fetch_nuspec_from_repository(repository_details, package_id, package_version)
18
29
  end
19
30
  end
20
31
 
32
+ sig do
33
+ params(
34
+ repository_details: T::Hash[Symbol, T.untyped],
35
+ package_id: T.nilable(String),
36
+ package_version: T.nilable(String)
37
+ )
38
+ .returns(T.nilable(Nokogiri::XML::Document))
39
+ end
21
40
  def self.fetch_nuspec_from_repository(repository_details, package_id, package_version)
22
41
  return unless package_id && package_version && !package_version.empty?
23
42
 
@@ -55,6 +74,7 @@ module Dependabot
55
74
  nuspec_xml
56
75
  end
57
76
 
77
+ sig { params(feed_url: String).returns(T::Boolean) }
58
78
  def self.feed_supports_nuspec_download?(feed_url)
59
79
  feed_regexs = [
60
80
  # nuget
@@ -67,6 +87,7 @@ module Dependabot
67
87
  feed_regexs.any? { |reg| reg.match(feed_url) }
68
88
  end
69
89
 
90
+ sig { params(zip_stream: String, package_id: String).returns(T.nilable(String)) }
70
91
  def self.extract_nuspec(zip_stream, package_id)
71
92
  Zip::File.open_buffer(zip_stream) do |zip|
72
93
  nuspec_entry = zip.find { |entry| entry.name == "#{package_id}.nuspec" }
@@ -75,6 +96,7 @@ module Dependabot
75
96
  nil
76
97
  end
77
98
 
99
+ sig { params(string: String).returns(String) }
78
100
  def self.remove_invalid_characters(string)
79
101
  string.dup
80
102
  .force_encoding(Encoding::UTF_8)
@@ -1,6 +1,8 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/update_checkers/base"
5
7
  require "dependabot/nuget/file_parser"
6
8
 
@@ -8,28 +10,47 @@ module Dependabot
8
10
  module Nuget
9
11
  class UpdateChecker < Dependabot::UpdateCheckers::Base
10
12
  class PropertyUpdater
13
+ extend T::Sig
14
+
11
15
  require_relative "version_finder"
12
16
  require_relative "requirements_updater"
13
17
  require_relative "dependency_finder"
14
18
 
19
+ sig do
20
+ params(
21
+ dependency: Dependabot::Dependency,
22
+ dependency_files: T::Array[Dependabot::DependencyFile],
23
+ credentials: T::Array[Dependabot::Credential],
24
+ target_version_details: T.nilable(T::Hash[Symbol, String]),
25
+ ignored_versions: T::Array[String],
26
+ repo_contents_path: T.nilable(String),
27
+ raise_on_ignored: T::Boolean
28
+ ).void
29
+ end
15
30
  def initialize(dependency:, dependency_files:, credentials:,
16
31
  target_version_details:, ignored_versions:,
17
- raise_on_ignored: false, repo_contents_path:)
32
+ repo_contents_path:, raise_on_ignored: false)
18
33
  @dependency = dependency
19
34
  @dependency_files = dependency_files
20
35
  @credentials = credentials
21
36
  @ignored_versions = ignored_versions
22
37
  @raise_on_ignored = raise_on_ignored
23
- @target_version = target_version_details&.fetch(:version)
24
- @source_details = target_version_details
25
- &.slice(:nuspec_url, :repo_url, :source_url)
38
+ @target_version = T.let(
39
+ target_version_details&.fetch(:version),
40
+ T.nilable(T.any(String, Dependabot::Nuget::Version))
41
+ )
42
+ @source_details = T.let(
43
+ target_version_details&.slice(:nuspec_url, :repo_url, :source_url),
44
+ T.nilable(T::Hash[Symbol, String])
45
+ )
26
46
  @repo_contents_path = repo_contents_path
27
47
  end
28
48
 
49
+ sig { returns(T::Boolean) }
29
50
  def update_possible?
30
51
  return false unless target_version
31
52
 
32
- @update_possible ||=
53
+ @update_possible ||= T.let(
33
54
  dependencies_using_property.all? do |dep|
34
55
  versions = VersionFinder.new(
35
56
  dependency: dep,
@@ -42,42 +63,73 @@ module Dependabot
42
63
  ).versions.map { |v| v.fetch(:version) }
43
64
 
44
65
  versions.include?(target_version) || versions.none?
45
- end
66
+ end,
67
+ T.nilable(T::Boolean)
68
+ )
46
69
  end
47
70
 
71
+ sig { returns(T::Array[Dependabot::Dependency]) }
48
72
  def updated_dependencies
49
73
  raise "Update not possible!" unless update_possible?
50
74
 
51
- @updated_dependencies ||= begin
52
- dependencies = {}
53
-
54
- dependencies_using_property.each do |dep|
55
- # Only keep one copy of each dependency, the one with the highest target version.
56
- visited_dependency = dependencies[dep.name.downcase]
57
- next unless visited_dependency.nil? || visited_dependency.numeric_version < target_version
58
-
59
- updated_dependency = Dependency.new(
60
- name: dep.name,
61
- version: target_version.to_s,
62
- requirements: updated_requirements(dep),
63
- previous_version: dep.version,
64
- previous_requirements: dep.requirements,
65
- package_manager: dep.package_manager
66
- )
67
- dependencies[updated_dependency.name.downcase] = updated_dependency
68
- # Add peer dependencies to the list of updated dependencies.
69
- process_updated_peer_dependencies(updated_dependency, dependencies)
70
- end
75
+ @updated_dependencies ||= T.let(
76
+ begin
77
+ dependencies = T.let({}, T::Hash[String, Dependabot::Dependency])
78
+
79
+ dependencies_using_property.each do |dep|
80
+ # Only keep one copy of each dependency, the one with the highest target version.
81
+ visited_dependency = dependencies[dep.name.downcase]
82
+ next unless visited_dependency.nil? || T.must(visited_dependency.numeric_version) < target_version
83
+
84
+ updated_dependency = Dependency.new(
85
+ name: dep.name,
86
+ version: target_version.to_s,
87
+ requirements: updated_requirements(dep),
88
+ previous_version: dep.version,
89
+ previous_requirements: dep.requirements,
90
+ package_manager: dep.package_manager
91
+ )
92
+ dependencies[updated_dependency.name.downcase] = updated_dependency
93
+ # Add peer dependencies to the list of updated dependencies.
94
+ process_updated_peer_dependencies(updated_dependency, dependencies)
95
+ end
71
96
 
72
- dependencies.map { |_, dependency| dependency }
73
- end
97
+ dependencies.map { |_, dependency| dependency }
98
+ end,
99
+ T.nilable(T::Array[Dependabot::Dependency])
100
+ )
74
101
  end
75
102
 
76
103
  private
77
104
 
78
- attr_reader :dependency, :dependency_files, :target_version,
79
- :source_details, :credentials, :ignored_versions, :repo_contents_path
105
+ sig { returns(Dependabot::Dependency) }
106
+ attr_reader :dependency
107
+
108
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
109
+ attr_reader :dependency_files
110
+
111
+ sig { returns(T.nilable(T.any(String, Dependabot::Nuget::Version))) }
112
+ attr_reader :target_version
80
113
 
114
+ sig { returns(T.nilable(T::Hash[Symbol, String])) }
115
+ attr_reader :source_details
116
+
117
+ sig { returns(T::Array[Dependabot::Credential]) }
118
+ attr_reader :credentials
119
+
120
+ sig { returns(T::Array[String]) }
121
+ attr_reader :ignored_versions
122
+
123
+ sig { returns(T.nilable(String)) }
124
+ attr_reader :repo_contents_path
125
+
126
+ sig do
127
+ params(
128
+ dependency: Dependabot::Dependency,
129
+ dependencies: T::Hash[String, Dependabot::Dependency]
130
+ )
131
+ .returns(T::Array[Dependabot::Dependency])
132
+ end
81
133
  def process_updated_peer_dependencies(dependency, dependencies)
82
134
  DependencyFinder.new(
83
135
  dependency: dependency,
@@ -87,36 +139,48 @@ module Dependabot
87
139
  ).updated_peer_dependencies.each do |peer_dependency|
88
140
  # Only keep one copy of each dependency, the one with the highest target version.
89
141
  visited_dependency = dependencies[peer_dependency.name.downcase]
90
- next unless visited_dependency.nil? || visited_dependency.numeric_version < peer_dependency.numeric_version
142
+ unless visited_dependency.nil? ||
143
+ T.must(visited_dependency.numeric_version) < peer_dependency.numeric_version
144
+ next
145
+ end
91
146
 
92
147
  dependencies[peer_dependency.name.downcase] = peer_dependency
93
148
  end
94
149
  end
95
150
 
151
+ sig { returns(T::Array[Dependabot::Dependency]) }
96
152
  def dependencies_using_property
97
153
  @dependencies_using_property ||=
98
- Nuget::FileParser.new(
99
- dependency_files: dependency_files,
100
- source: nil
101
- ).parse.select do |dep|
102
- dep.requirements.any? do |r|
103
- r.dig(:metadata, :property_name) == property_name
104
- end
105
- end
154
+ T.let(
155
+ Nuget::FileParser.new(
156
+ dependency_files: dependency_files,
157
+ source: nil
158
+ ).parse.select do |dep|
159
+ dep.requirements.any? do |r|
160
+ r.dig(:metadata, :property_name) == property_name
161
+ end
162
+ end,
163
+ T.nilable(T::Array[Dependabot::Dependency])
164
+ )
106
165
  end
107
166
 
167
+ sig { returns(String) }
108
168
  def property_name
109
- @property_name ||= dependency.requirements
110
- .find { |r| r.dig(:metadata, :property_name) }
111
- &.dig(:metadata, :property_name)
169
+ @property_name ||= T.let(
170
+ dependency.requirements
171
+ .find { |r| r.dig(:metadata, :property_name) }
172
+ &.dig(:metadata, :property_name),
173
+ T.nilable(String)
174
+ )
112
175
 
113
176
  raise "No requirement with a property name!" unless @property_name
114
177
 
115
178
  @property_name
116
179
  end
117
180
 
181
+ sig { params(dep: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
118
182
  def updated_requirements(dep)
119
- @updated_requirements ||= {}
183
+ @updated_requirements ||= T.let({}, T.nilable(T::Hash[String, T::Array[T::Hash[Symbol, T.untyped]]]))
120
184
  @updated_requirements[dep.name] ||=
121
185
  RequirementsUpdater.new(
122
186
  requirements: dep.requirements,