dependabot-nuget 0.246.0 → 0.248.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +18 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +110 -0
  6. data/lib/dependabot/nuget/cache_manager.rb +9 -3
  7. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -12
  8. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +13 -3
  9. data/lib/dependabot/nuget/file_fetcher.rb +89 -37
  10. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +10 -2
  11. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +10 -2
  12. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +11 -2
  13. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +140 -41
  14. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
  15. data/lib/dependabot/nuget/file_parser.rb +13 -3
  16. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +25 -8
  17. data/lib/dependabot/nuget/file_updater.rb +74 -38
  18. data/lib/dependabot/nuget/http_response_helpers.rb +6 -1
  19. data/lib/dependabot/nuget/metadata_finder.rb +27 -3
  20. data/lib/dependabot/nuget/nuget_client.rb +23 -0
  21. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +10 -1
  22. data/lib/dependabot/nuget/requirement.rb +21 -9
  23. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
  24. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +87 -21
  25. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +25 -3
  26. data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
  27. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +32 -9
  28. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
  29. data/lib/dependabot/nuget/update_checker/version_finder.rb +178 -64
  30. data/lib/dependabot/nuget/update_checker.rb +76 -32
  31. data/lib/dependabot/nuget/version.rb +7 -2
  32. metadata +19 -5
@@ -1,6 +1,8 @@
1
- # typed: false
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/nuget/version"
5
7
  require "dependabot/nuget/requirement"
6
8
  require "dependabot/update_checkers/base"
@@ -10,16 +12,33 @@ require "dependabot/nuget/nuget_client"
10
12
  module Dependabot
11
13
  module Nuget
12
14
  class UpdateChecker < Dependabot::UpdateCheckers::Base
15
+ # rubocop:disable Metrics/ClassLength
13
16
  class VersionFinder
17
+ extend T::Sig
18
+
14
19
  require_relative "compatibility_checker"
15
20
  require_relative "repository_finder"
16
21
 
17
22
  NUGET_RANGE_REGEX = /[\(\[].*,.*[\)\]]/
18
23
 
19
- def initialize(dependency:, dependency_files:, credentials:,
20
- ignored_versions:, raise_on_ignored: false,
24
+ sig do
25
+ params(
26
+ dependency: Dependabot::Dependency,
27
+ dependency_files: T::Array[Dependabot::DependencyFile],
28
+ credentials: T::Array[Dependabot::Credential],
29
+ ignored_versions: T::Array[String],
30
+ security_advisories: T::Array[Dependabot::SecurityAdvisory],
31
+ repo_contents_path: T.nilable(String),
32
+ raise_on_ignored: T::Boolean
33
+ ).void
34
+ end
35
+ def initialize(dependency:,
36
+ dependency_files:,
37
+ credentials:,
38
+ ignored_versions:,
21
39
  security_advisories:,
22
- repo_contents_path:)
40
+ repo_contents_path:,
41
+ raise_on_ignored: false)
23
42
  @dependency = dependency
24
43
  @dependency_files = dependency_files
25
44
  @credentials = credentials
@@ -29,53 +48,89 @@ module Dependabot
29
48
  @repo_contents_path = repo_contents_path
30
49
  end
31
50
 
51
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
32
52
  def latest_version_details
33
53
  @latest_version_details ||=
34
- begin
35
- possible_versions = versions
36
- possible_versions = filter_prereleases(possible_versions)
37
- possible_versions = filter_ignored_versions(possible_versions)
38
-
39
- find_highest_compatible_version(possible_versions)
40
- end
54
+ T.let(
55
+ begin
56
+ possible_versions = versions
57
+ possible_versions = filter_prereleases(possible_versions)
58
+ possible_versions = filter_ignored_versions(possible_versions)
59
+
60
+ find_highest_compatible_version(possible_versions)
61
+ end,
62
+ T.nilable(T::Hash[Symbol, T.untyped])
63
+ )
41
64
  end
42
65
 
66
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
43
67
  def lowest_security_fix_version_details
44
68
  @lowest_security_fix_version_details ||=
45
- begin
46
- possible_versions = versions
47
- possible_versions = filter_prereleases(possible_versions)
48
- possible_versions = Dependabot::UpdateCheckers::VersionFilters.filter_vulnerable_versions(
49
- possible_versions, security_advisories
50
- )
51
- possible_versions = filter_ignored_versions(possible_versions)
52
- possible_versions = filter_lower_versions(possible_versions)
53
-
54
- find_lowest_compatible_version(possible_versions)
55
- end
69
+ T.let(
70
+ begin
71
+ possible_versions = versions
72
+ possible_versions = filter_prereleases(possible_versions)
73
+ possible_versions = Dependabot::UpdateCheckers::VersionFilters.filter_vulnerable_versions(
74
+ possible_versions, security_advisories
75
+ )
76
+ possible_versions = filter_ignored_versions(possible_versions)
77
+ possible_versions = filter_lower_versions(possible_versions)
78
+
79
+ find_lowest_compatible_version(possible_versions)
80
+ end,
81
+ T.nilable(T::Hash[Symbol, T.untyped])
82
+ )
56
83
  end
57
84
 
85
+ sig { returns(T::Array[T::Hash[Symbol, T.nilable(T.any(Dependabot::Version, String))]]) }
58
86
  def versions
59
87
  available_v3_versions + available_v2_versions
60
88
  end
61
89
 
62
- attr_reader :dependency, :dependency_files, :credentials,
63
- :ignored_versions, :security_advisories, :repo_contents_path
90
+ sig { returns(Dependabot::Dependency) }
91
+ attr_reader :dependency
92
+
93
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
94
+ attr_reader :dependency_files
95
+
96
+ sig { returns(T::Array[Dependabot::Credential]) }
97
+ attr_reader :credentials
98
+
99
+ sig { returns(T::Array[String]) }
100
+ attr_reader :ignored_versions
101
+
102
+ sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
103
+ attr_reader :security_advisories
104
+
105
+ sig { returns(T.nilable(String)) }
106
+ attr_reader :repo_contents_path
64
107
 
65
108
  private
66
109
 
110
+ sig do
111
+ params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
112
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
113
+ end
67
114
  def find_highest_compatible_version(possible_versions)
68
115
  # sorted versions descending
69
116
  sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }.reverse
70
117
  find_compatible_version(sorted_versions)
71
118
  end
72
119
 
120
+ sig do
121
+ params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
122
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
123
+ end
73
124
  def find_lowest_compatible_version(possible_versions)
74
125
  # sorted versions ascending
75
126
  sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }
76
127
  find_compatible_version(sorted_versions)
77
128
  end
78
129
 
130
+ sig do
131
+ params(sorted_versions: T::Array[T::Hash[Symbol, T.untyped]])
132
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
133
+ end
79
134
  def find_compatible_version(sorted_versions)
80
135
  # By checking the first version separately, we can avoid additional network requests
81
136
  first_version = sorted_versions.first
@@ -89,33 +144,52 @@ module Dependabot
89
144
  sorted_versions.find { |v| version_compatible?(v.fetch(:version)) }
90
145
  end
91
146
 
147
+ sig { params(version: T.nilable(T.any(Dependabot::Version, String))).returns(T::Boolean) }
92
148
  def version_compatible?(version)
93
149
  str_version_compatible?(version.to_s)
94
150
  end
95
151
 
152
+ sig { params(version: String).returns(T::Boolean) }
96
153
  def str_version_compatible?(version)
97
154
  compatibility_checker.compatible?(version)
98
155
  end
99
156
 
157
+ sig { returns(Dependabot::Nuget::CompatibilityChecker) }
100
158
  def compatibility_checker
101
- @compatibility_checker ||= CompatibilityChecker.new(
102
- dependency_urls: dependency_urls,
103
- dependency: dependency,
104
- tfm_finder: TfmFinder.new(
105
- dependency_files: dependency_files,
106
- credentials: credentials,
107
- repo_contents_path: repo_contents_path
159
+ @compatibility_checker ||=
160
+ T.let(
161
+ CompatibilityChecker.new(
162
+ dependency_urls: dependency_urls,
163
+ dependency: dependency,
164
+ tfm_finder: TfmFinder.new(
165
+ dependency_files: dependency_files,
166
+ credentials: credentials,
167
+ repo_contents_path: repo_contents_path
168
+ )
169
+ ),
170
+ T.nilable(Dependabot::Nuget::CompatibilityChecker)
108
171
  )
109
- )
110
172
  end
111
173
 
174
+ sig do
175
+ params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
176
+ .returns(T::Array[T::Hash[Symbol, T.untyped]])
177
+ end
112
178
  def filter_prereleases(possible_versions)
113
- possible_versions.reject do |d|
179
+ filtered = possible_versions.reject do |d|
114
180
  version = d.fetch(:version)
115
181
  version.prerelease? && !related_to_current_pre?(version)
116
182
  end
183
+ if possible_versions.count > filtered.count
184
+ Dependabot.logger.info("Filtered out #{possible_versions.count - filtered.count} pre-release versions")
185
+ end
186
+ filtered
117
187
  end
118
188
 
189
+ sig do
190
+ params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
191
+ .returns(T::Array[T::Hash[Symbol, T.untyped]])
192
+ end
119
193
  def filter_ignored_versions(possible_versions)
120
194
  filtered = possible_versions
121
195
 
@@ -131,9 +205,17 @@ module Dependabot
131
205
  raise AllVersionsIgnored
132
206
  end
133
207
 
208
+ if possible_versions.count > filtered.count
209
+ Dependabot.logger.info("Filtered out #{possible_versions.count - filtered.count} ignored versions")
210
+ end
211
+
134
212
  filtered
135
213
  end
136
214
 
215
+ sig do
216
+ params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
217
+ .returns(T::Array[T::Hash[Symbol, T.untyped]])
218
+ end
137
219
  def filter_lower_versions(possible_versions)
138
220
  return possible_versions unless dependency.numeric_version
139
221
 
@@ -142,12 +224,14 @@ module Dependabot
142
224
  end
143
225
  end
144
226
 
227
+ sig { params(string: String).returns(T::Array[String]) }
145
228
  def parse_requirement_string(string)
146
229
  return [string] if string.match?(NUGET_RANGE_REGEX)
147
230
 
148
231
  string.split(",").map(&:strip)
149
232
  end
150
233
 
234
+ sig { returns(T::Array[T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)]]) }
151
235
  def available_v3_versions
152
236
  v3_nuget_listings.flat_map do |listing|
153
237
  listing
@@ -168,6 +252,7 @@ module Dependabot
168
252
  end
169
253
  end
170
254
 
255
+ sig { returns(T::Array[T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)]]) }
171
256
  def available_v2_versions
172
257
  v2_nuget_listings.flat_map do |listing|
173
258
  body = listing.fetch("xml_body", [])
@@ -187,6 +272,10 @@ module Dependabot
187
272
  end
188
273
  end
189
274
 
275
+ sig do
276
+ params(entry: Nokogiri::XML::Element)
277
+ .returns(T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)])
278
+ end
190
279
  def dependency_details_from_v2_entry(entry)
191
280
  version = entry.at_xpath("./properties/Version").content.strip
192
281
  source_urls = []
@@ -208,10 +297,11 @@ module Dependabot
208
297
  end
209
298
 
210
299
  # rubocop:disable Metrics/PerceivedComplexity
300
+ sig { params(version: Dependabot::Version).returns(T::Boolean) }
211
301
  def related_to_current_pre?(version)
212
302
  current_version = dependency.numeric_version
213
303
  if current_version&.prerelease? &&
214
- current_version&.release == version.release
304
+ current_version.release == version.release
215
305
  return true
216
306
  end
217
307
 
@@ -229,40 +319,50 @@ module Dependabot
229
319
  false
230
320
  end
231
321
  end
232
-
233
322
  # rubocop:enable Metrics/PerceivedComplexity
234
323
 
324
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
235
325
  def v3_nuget_listings
236
- return @v3_nuget_listings unless @v3_nuget_listings.nil?
237
-
238
326
  @v3_nuget_listings ||=
239
- dependency_urls
240
- .select { |details| details.fetch(:repository_type) == "v3" }
241
- .filter_map do |url_details|
242
- versions = NugetClient.get_package_versions(dependency.name, url_details)
243
- next unless versions
244
-
245
- { "versions" => versions, "listing_details" => url_details }
246
- end
327
+ T.let(
328
+ dependency_urls
329
+ .select { |details| details.fetch(:repository_type) == "v3" }
330
+ .filter_map do |url_details|
331
+ versions = NugetClient.get_package_versions(dependency.name, url_details)
332
+ next unless versions
333
+
334
+ { "versions" => versions, "listing_details" => url_details }
335
+ end,
336
+ T.nilable(T::Array[T::Hash[String, T.untyped]])
337
+ )
247
338
  end
248
339
 
340
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
249
341
  def v2_nuget_listings
250
- return @v2_nuget_listings unless @v2_nuget_listings.nil?
251
-
252
342
  @v2_nuget_listings ||=
253
- dependency_urls
254
- .select { |details| details.fetch(:repository_type) == "v2" }
255
- .flat_map { |url_details| fetch_paginated_v2_nuget_listings(url_details) }
256
- .filter_map do |url_details, response|
257
- next unless response.status == 200
258
-
259
- {
260
- "xml_body" => response.body,
261
- "listing_details" => url_details
262
- }
263
- end
343
+ T.let(
344
+ dependency_urls
345
+ .select { |details| details.fetch(:repository_type) == "v2" }
346
+ .flat_map { |url_details| fetch_paginated_v2_nuget_listings(url_details) }
347
+ .filter_map do |url_details, response|
348
+ next unless response.status == 200
349
+
350
+ {
351
+ "xml_body" => response.body,
352
+ "listing_details" => url_details
353
+ }
354
+ end,
355
+ T.nilable(T::Array[T::Hash[String, T.untyped]])
356
+ )
264
357
  end
265
358
 
359
+ sig do
360
+ params(
361
+ url_details: T::Hash[Symbol, T.untyped],
362
+ results: T::Hash[T::Hash[Symbol, T.untyped], Excon::Response]
363
+ )
364
+ .returns(T::Array[T::Array[T.untyped]])
365
+ end
266
366
  def fetch_paginated_v2_nuget_listings(url_details, results = {})
267
367
  response = Dependabot::RegistryClient.get(
268
368
  url: url_details[:versions_url],
@@ -286,6 +386,7 @@ module Dependabot
286
386
  results.to_a
287
387
  end
288
388
 
389
+ sig { params(xml_body: String).returns(T.nilable(String)) }
289
390
  def fetch_v2_next_link_href(xml_body)
290
391
  doc = Nokogiri::XML(xml_body)
291
392
  doc.remove_namespaces!
@@ -298,32 +399,44 @@ module Dependabot
298
399
  nil
299
400
  end
300
401
 
402
+ sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
301
403
  def dependency_urls
302
404
  @dependency_urls ||=
303
- RepositoryFinder.new(
304
- dependency: dependency,
305
- credentials: credentials,
306
- config_files: nuget_configs
307
- ).dependency_urls
405
+ T.let(
406
+ RepositoryFinder.new(
407
+ dependency: dependency,
408
+ credentials: credentials,
409
+ config_files: nuget_configs
410
+ ).dependency_urls,
411
+ T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
412
+ )
308
413
  end
309
414
 
415
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
310
416
  def nuget_configs
311
417
  @nuget_configs ||=
312
- dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
418
+ T.let(
419
+ dependency_files.select { |f| f.name.match?(/nuget\.config$/i) },
420
+ T.nilable(T::Array[Dependabot::DependencyFile])
421
+ )
313
422
  end
314
423
 
424
+ sig { returns(String) }
315
425
  def sanitized_name
316
426
  dependency.name.downcase
317
427
  end
318
428
 
429
+ sig { returns(T.class_of(Gem::Version)) }
319
430
  def version_class
320
431
  dependency.version_class
321
432
  end
322
433
 
434
+ sig { returns(T.class_of(Dependabot::Requirement)) }
323
435
  def requirement_class
324
436
  dependency.requirement_class
325
437
  end
326
438
 
439
+ sig { returns(T::Hash[Symbol, Integer]) }
327
440
  def excon_options
328
441
  # For large JSON files we sometimes need a little longer than for
329
442
  # other languages. For example, see:
@@ -336,6 +449,7 @@ module Dependabot
336
449
  }
337
450
  end
338
451
  end
452
+ # rubocop:enable Metrics/ClassLength
339
453
  end
340
454
  end
341
455
  end
@@ -1,55 +1,68 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/nuget/file_parser"
5
5
  require "dependabot/update_checkers"
6
6
  require "dependabot/update_checkers/base"
7
+ require "sorbet-runtime"
7
8
 
8
9
  module Dependabot
9
10
  module Nuget
10
11
  class UpdateChecker < Dependabot::UpdateCheckers::Base
12
+ extend T::Sig
13
+
11
14
  require_relative "update_checker/version_finder"
12
15
  require_relative "update_checker/property_updater"
13
16
  require_relative "update_checker/requirements_updater"
14
17
  require_relative "update_checker/dependency_finder"
15
18
 
19
+ sig { override.returns(T.nilable(String)) }
16
20
  def latest_version
17
21
  # No need to find latest version for transitive dependencies unless they have a vulnerability.
18
22
  return dependency.version if !dependency.top_level? && !vulnerable?
19
23
 
20
- @latest_version = latest_version_details&.fetch(:version)
24
+ # if no update sources have the requisite package, then we can only assume that the current version is correct
25
+ @latest_version = T.let(
26
+ latest_version_details&.fetch(:version)&.to_s || dependency.version,
27
+ T.nilable(String)
28
+ )
21
29
  end
22
30
 
31
+ sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
23
32
  def latest_resolvable_version
24
33
  # We always want a full unlock since any package update could update peer dependencies as well.
25
34
  # To force a full unlock instead of an own unlock, we return nil.
26
35
  nil
27
36
  end
28
37
 
38
+ sig { override.returns(Dependabot::Nuget::Version) }
29
39
  def lowest_security_fix_version
30
40
  lowest_security_fix_version_details&.fetch(:version)
31
41
  end
32
42
 
43
+ sig { override.returns(T.nilable(Dependabot::Version)) }
33
44
  def lowest_resolvable_security_fix_version
34
45
  return nil if version_comes_from_multi_dependency_property?
35
46
 
36
47
  lowest_security_fix_version
37
48
  end
38
49
 
50
+ sig { override.returns(NilClass) }
39
51
  def latest_resolvable_version_with_no_unlock
40
52
  # Irrelevant, since Nuget has a single dependency file
41
53
  nil
42
54
  end
43
55
 
56
+ sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
44
57
  def updated_requirements
45
58
  RequirementsUpdater.new(
46
59
  requirements: dependency.requirements,
47
- latest_version: preferred_resolvable_version_details.fetch(:version)&.to_s,
48
- source_details: preferred_resolvable_version_details
49
- &.slice(:nuspec_url, :repo_url, :source_url)
60
+ latest_version: preferred_resolvable_version_details&.fetch(:version, nil)&.to_s,
61
+ source_details: preferred_resolvable_version_details&.slice(:nuspec_url, :repo_url, :source_url)
50
62
  ).updated_requirements
51
63
  end
52
64
 
65
+ sig { returns(T::Boolean) }
53
66
  def up_to_date?
54
67
  # No need to update transitive dependencies unless they have a vulnerability.
55
68
  return true if !dependency.top_level? && !vulnerable?
@@ -62,6 +75,7 @@ module Dependabot
62
75
  super
63
76
  end
64
77
 
78
+ sig { returns(T::Boolean) }
65
79
  def requirements_unlocked_or_can_be?
66
80
  # If any requirements have an uninterpolated property in them then
67
81
  # that property couldn't be found, and the requirement therefore
@@ -73,6 +87,7 @@ module Dependabot
73
87
 
74
88
  private
75
89
 
90
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
76
91
  def preferred_resolvable_version_details
77
92
  # If this dependency is vulnerable, prefer trying to update to the
78
93
  # lowest_resolvable_security_fix_version. Otherwise update all the way
@@ -82,6 +97,7 @@ module Dependabot
82
97
  latest_version_details
83
98
  end
84
99
 
100
+ sig { override.returns(T::Boolean) }
85
101
  def latest_version_resolvable_with_full_unlock?
86
102
  # We always want a full unlock since any package update could update peer dependencies as well.
87
103
  return true unless version_comes_from_multi_dependency_property?
@@ -89,6 +105,7 @@ module Dependabot
89
105
  property_updater.update_possible?
90
106
  end
91
107
 
108
+ sig { override.returns(T::Array[Dependabot::Dependency]) }
92
109
  def updated_dependencies_after_full_unlock
93
110
  return property_updater.updated_dependencies if version_comes_from_multi_dependency_property?
94
111
 
@@ -96,7 +113,7 @@ module Dependabot
96
113
 
97
114
  updated_dependency = Dependency.new(
98
115
  name: dependency.name,
99
- version: latest_version&.to_s,
116
+ version: latest_version,
100
117
  requirements: updated_requirements,
101
118
  previous_version: dependency.version,
102
119
  previous_requirements: dependency.requirements,
@@ -112,47 +129,66 @@ module Dependabot
112
129
  updated_dependencies
113
130
  end
114
131
 
132
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
115
133
  def preferred_version_details
116
134
  return lowest_security_fix_version_details if vulnerable?
117
135
 
118
136
  latest_version_details
119
137
  end
120
138
 
139
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
121
140
  def latest_version_details
122
- @latest_version_details ||= version_finder.latest_version_details
141
+ @latest_version_details ||=
142
+ T.let(
143
+ version_finder.latest_version_details,
144
+ T.nilable(T::Hash[Symbol, T.untyped])
145
+ )
123
146
  end
124
147
 
148
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
125
149
  def lowest_security_fix_version_details
126
150
  @lowest_security_fix_version_details ||=
127
- version_finder.lowest_security_fix_version_details
151
+ T.let(
152
+ version_finder.lowest_security_fix_version_details,
153
+ T.nilable(T::Hash[Symbol, T.untyped])
154
+ )
128
155
  end
129
156
 
157
+ sig { returns(Dependabot::Nuget::UpdateChecker::VersionFinder) }
130
158
  def version_finder
131
159
  @version_finder ||=
132
- VersionFinder.new(
133
- dependency: dependency,
134
- dependency_files: dependency_files,
135
- credentials: credentials,
136
- ignored_versions: ignored_versions,
137
- raise_on_ignored: @raise_on_ignored,
138
- security_advisories: security_advisories,
139
- repo_contents_path: @repo_contents_path
160
+ T.let(
161
+ VersionFinder.new(
162
+ dependency: dependency,
163
+ dependency_files: dependency_files,
164
+ credentials: credentials,
165
+ ignored_versions: ignored_versions,
166
+ raise_on_ignored: @raise_on_ignored,
167
+ security_advisories: security_advisories,
168
+ repo_contents_path: @repo_contents_path
169
+ ),
170
+ T.nilable(Dependabot::Nuget::UpdateChecker::VersionFinder)
140
171
  )
141
172
  end
142
173
 
174
+ sig { returns(Dependabot::Nuget::UpdateChecker::PropertyUpdater) }
143
175
  def property_updater
144
176
  @property_updater ||=
145
- PropertyUpdater.new(
146
- dependency: dependency,
147
- dependency_files: dependency_files,
148
- target_version_details: latest_version_details,
149
- credentials: credentials,
150
- ignored_versions: ignored_versions,
151
- raise_on_ignored: @raise_on_ignored,
152
- repo_contents_path: @repo_contents_path
177
+ T.let(
178
+ PropertyUpdater.new(
179
+ dependency: dependency,
180
+ dependency_files: dependency_files,
181
+ target_version_details: latest_version_details,
182
+ credentials: credentials,
183
+ ignored_versions: ignored_versions,
184
+ raise_on_ignored: @raise_on_ignored,
185
+ repo_contents_path: @repo_contents_path
186
+ ),
187
+ T.nilable(Dependabot::Nuget::UpdateChecker::PropertyUpdater)
153
188
  )
154
189
  end
155
190
 
191
+ sig { returns(T::Boolean) }
156
192
  def version_comes_from_multi_dependency_property?
157
193
  declarations_using_a_property.any? do |requirement|
158
194
  property_name = requirement.fetch(:metadata).fetch(:property_name)
@@ -167,20 +203,28 @@ module Dependabot
167
203
  end
168
204
  end
169
205
 
206
+ sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
170
207
  def declarations_using_a_property
171
208
  @declarations_using_a_property ||=
172
- dependency.requirements
173
- .select { |req| req.dig(:metadata, :property_name) }
209
+ T.let(
210
+ dependency.requirements
211
+ .select { |req| req.dig(:metadata, :property_name) },
212
+ T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
213
+ )
174
214
  end
175
215
 
216
+ sig { returns(T::Array[Dependabot::Dependency]) }
176
217
  def all_property_based_dependencies
177
218
  @all_property_based_dependencies ||=
178
- Nuget::FileParser.new(
179
- dependency_files: dependency_files,
180
- source: nil
181
- ).parse.select do |dep|
182
- dep.requirements.any? { |req| req.dig(:metadata, :property_name) }
183
- end
219
+ T.let(
220
+ Nuget::FileParser.new(
221
+ dependency_files: dependency_files,
222
+ source: nil
223
+ ).parse.select do |dep|
224
+ dep.requirements.any? { |req| req.dig(:metadata, :property_name) }
225
+ end,
226
+ T.nilable(T::Array[Dependabot::Dependency])
227
+ )
184
228
  end
185
229
  end
186
230
  end
@@ -17,14 +17,14 @@ module Dependabot
17
17
  VERSION_PATTERN = T.let(Gem::Version::VERSION_PATTERN + '(\+[0-9a-zA-Z\-.]+)?', String)
18
18
  ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/
19
19
 
20
- sig { override.params(version: T.nilable(T.any(String, Integer, Float, Gem::Version))).returns(T::Boolean) }
20
+ sig { override.params(version: VersionParameter).returns(T::Boolean) }
21
21
  def self.correct?(version)
22
22
  return false if version.nil?
23
23
 
24
24
  version.to_s.match?(ANCHORED_VERSION_PATTERN)
25
25
  end
26
26
 
27
- sig { override.params(version: T.nilable(T.any(String, Integer, Float, Gem::Version))).void }
27
+ sig { override.params(version: VersionParameter).void }
28
28
  def initialize(version)
29
29
  version = version.to_s.split("+").first || ""
30
30
  @version_string = T.let(version, String)
@@ -32,6 +32,11 @@ module Dependabot
32
32
  super
33
33
  end
34
34
 
35
+ sig { override.params(version: VersionParameter).returns(Dependabot::Nuget::Version) }
36
+ def self.new(version)
37
+ T.cast(super, Dependabot::Nuget::Version)
38
+ end
39
+
35
40
  sig { returns(String) }
36
41
  def to_s
37
42
  @version_string