dependabot-nuget 0.247.0 → 0.249.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 (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,