dependabot-uv 0.331.0 → 0.333.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.
@@ -1,6 +1,8 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/uv/language_version_manager"
5
7
  require "dependabot/uv/update_checker"
6
8
  require "dependabot/uv/update_checker/latest_version_finder"
@@ -10,27 +12,42 @@ module Dependabot
10
12
  module Uv
11
13
  class UpdateChecker
12
14
  class PipVersionResolver
15
+ extend T::Sig
16
+
17
+ sig do
18
+ params(
19
+ dependency: Dependabot::Dependency,
20
+ dependency_files: T::Array[Dependabot::DependencyFile],
21
+ credentials: T::Array[Dependabot::Credential],
22
+ ignored_versions: T::Array[String],
23
+ security_advisories: T::Array[Dependabot::SecurityAdvisory],
24
+ update_cooldown: T.nilable(Dependabot::Package::ReleaseCooldownOptions),
25
+ raise_on_ignored: T::Boolean
26
+ ).void
27
+ end
13
28
  def initialize(dependency:, dependency_files:, credentials:,
14
- ignored_versions:, update_cooldown: nil, raise_on_ignored: false,
15
- security_advisories:)
16
- @dependency = dependency
17
- @dependency_files = dependency_files
18
- @credentials = credentials
19
- @ignored_versions = ignored_versions
20
- @update_cooldown = update_cooldown
21
- @raise_on_ignored = raise_on_ignored
22
- @security_advisories = security_advisories
29
+ ignored_versions:, security_advisories:, update_cooldown: nil, raise_on_ignored: false)
30
+ @dependency = T.let(dependency, Dependabot::Dependency)
31
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
32
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
33
+ @ignored_versions = T.let(ignored_versions, T::Array[String])
34
+ @update_cooldown = T.let(update_cooldown, T.nilable(Dependabot::Package::ReleaseCooldownOptions))
35
+ @raise_on_ignored = T.let(raise_on_ignored, T::Boolean)
36
+ @security_advisories = T.let(security_advisories, T::Array[Dependabot::SecurityAdvisory])
23
37
  end
24
38
 
39
+ sig { returns(T.nilable(Dependabot::Version)) }
25
40
  def latest_resolvable_version
26
41
  latest_version_finder.latest_version(language_version: language_version_manager.python_version)
27
42
  end
28
43
 
44
+ sig { returns(T.nilable(Dependabot::Version)) }
29
45
  def latest_resolvable_version_with_no_unlock
30
46
  latest_version_finder
31
47
  .latest_version_with_no_unlock(language_version: language_version_manager.python_version)
32
48
  end
33
49
 
50
+ sig { returns(T.nilable(Dependabot::Version)) }
34
51
  def lowest_resolvable_security_fix_version
35
52
  latest_version_finder
36
53
  .lowest_security_fix_version(language_version: language_version_manager.python_version)
@@ -38,36 +55,55 @@ module Dependabot
38
55
 
39
56
  private
40
57
 
58
+ sig { returns(Dependabot::Dependency) }
41
59
  attr_reader :dependency
60
+
61
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
42
62
  attr_reader :dependency_files
63
+
64
+ sig { returns(T::Array[Dependabot::Credential]) }
43
65
  attr_reader :credentials
66
+
67
+ sig { returns(T::Array[String]) }
44
68
  attr_reader :ignored_versions
69
+
70
+ sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
45
71
  attr_reader :security_advisories
46
72
 
73
+ sig { returns(LatestVersionFinder) }
47
74
  def latest_version_finder
48
- @latest_version_finder ||= LatestVersionFinder.new(
49
- dependency: dependency,
50
- dependency_files: dependency_files,
51
- credentials: credentials,
52
- ignored_versions: ignored_versions,
53
- raise_on_ignored: @raise_on_ignored,
54
- cooldown_options: @update_cooldown,
55
- security_advisories: security_advisories
75
+ @latest_version_finder ||= T.let(
76
+ LatestVersionFinder.new(
77
+ dependency: dependency,
78
+ dependency_files: dependency_files,
79
+ credentials: credentials,
80
+ ignored_versions: ignored_versions,
81
+ raise_on_ignored: @raise_on_ignored,
82
+ cooldown_options: @update_cooldown,
83
+ security_advisories: security_advisories
84
+ ),
85
+ T.nilable(LatestVersionFinder)
56
86
  )
57
87
  end
58
88
 
89
+ sig { returns(FileParser::PythonRequirementParser) }
59
90
  def python_requirement_parser
60
- @python_requirement_parser ||=
91
+ @python_requirement_parser ||= T.let(
61
92
  FileParser::PythonRequirementParser.new(
62
93
  dependency_files: dependency_files
63
- )
94
+ ),
95
+ T.nilable(FileParser::PythonRequirementParser)
96
+ )
64
97
  end
65
98
 
99
+ sig { returns(LanguageVersionManager) }
66
100
  def language_version_manager
67
- @language_version_manager ||=
101
+ @language_version_manager ||= T.let(
68
102
  LanguageVersionManager.new(
69
103
  python_requirement_parser: python_requirement_parser
70
- )
104
+ ),
105
+ T.nilable(LanguageVersionManager)
106
+ )
71
107
  end
72
108
  end
73
109
  end
@@ -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/uv/requirement_parser"
5
7
  require "dependabot/uv/requirement"
6
8
  require "dependabot/uv/update_checker"
@@ -11,21 +13,39 @@ module Dependabot
11
13
  module Uv
12
14
  class UpdateChecker
13
15
  class RequirementsUpdater
14
- PYPROJECT_OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s*\|+/
15
- PYPROJECT_SEPARATOR = /#{PYPROJECT_OR_SEPARATOR}|,/
16
+ extend T::Sig
17
+
18
+ PYPROJECT_OR_SEPARATOR = T.let(/(?<=[a-zA-Z0-9*])\s*\|+/, Regexp)
19
+ PYPROJECT_SEPARATOR = T.let(/#{PYPROJECT_OR_SEPARATOR}|,/, Regexp)
16
20
 
17
21
  class UnfixableRequirement < StandardError; end
18
22
 
23
+ sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
19
24
  attr_reader :requirements
25
+
26
+ sig { returns(Dependabot::RequirementsUpdateStrategy) }
20
27
  attr_reader :update_strategy
28
+
29
+ sig { returns(T::Boolean) }
21
30
  attr_reader :has_lockfile
31
+
32
+ sig { returns(T.nilable(Dependabot::Uv::Version)) }
22
33
  attr_reader :latest_resolvable_version
23
34
 
35
+ sig do
36
+ params(
37
+ requirements: T::Array[T::Hash[Symbol, T.untyped]],
38
+ update_strategy: Dependabot::RequirementsUpdateStrategy,
39
+ has_lockfile: T::Boolean,
40
+ latest_resolvable_version: T.nilable(String)
41
+ ).void
42
+ end
24
43
  def initialize(requirements:, update_strategy:, has_lockfile:,
25
44
  latest_resolvable_version:)
26
- @requirements = requirements
27
- @update_strategy = update_strategy
28
- @has_lockfile = has_lockfile
45
+ @requirements = T.let(requirements, T::Array[T::Hash[Symbol, T.untyped]])
46
+ @update_strategy = T.let(update_strategy, Dependabot::RequirementsUpdateStrategy)
47
+ @has_lockfile = T.let(has_lockfile, T::Boolean)
48
+ @latest_resolvable_version = T.let(nil, T.nilable(Dependabot::Uv::Version))
29
49
 
30
50
  return unless latest_resolvable_version
31
51
 
@@ -33,6 +53,7 @@ module Dependabot
33
53
  Uv::Version.new(latest_resolvable_version)
34
54
  end
35
55
 
56
+ sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
36
57
  def updated_requirements
37
58
  return requirements if update_strategy.lockfile_only?
38
59
 
@@ -50,6 +71,7 @@ module Dependabot
50
71
  private
51
72
 
52
73
  # rubocop:disable Metrics/PerceivedComplexity
74
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
53
75
  def updated_setup_requirement(req)
54
76
  return req unless latest_resolvable_version
55
77
  return req unless req.fetch(:requirement)
@@ -62,7 +84,7 @@ module Dependabot
62
84
  find_and_update_equality_match(req_strings)
63
85
  elsif req_strings.any? { |r| r.start_with?("~=", "==") }
64
86
  tw_req = req_strings.find { |r| r.start_with?("~=", "==") }
65
- convert_to_range(tw_req, latest_resolvable_version)
87
+ convert_to_range(tw_req, T.must(latest_resolvable_version))
66
88
  else
67
89
  update_requirements_range(req_strings)
68
90
  end
@@ -73,12 +95,14 @@ module Dependabot
73
95
  end
74
96
  # rubocop:enable Metrics/PerceivedComplexity
75
97
 
98
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
76
99
  def updated_pipfile_requirement(req)
77
100
  # For now, we just proxy to updated_requirement. In future this
78
101
  # method may treat Pipfile requirements differently.
79
102
  updated_requirement(req)
80
103
  end
81
104
 
105
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
82
106
  def updated_pyproject_requirement(req)
83
107
  return req unless latest_resolvable_version
84
108
  return req unless req.fetch(:requirement)
@@ -101,12 +125,14 @@ module Dependabot
101
125
  req.merge(requirement: :unfixable)
102
126
  end
103
127
 
128
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
104
129
  def update_pyproject_version_if_needed(req)
105
130
  return req if new_version_satisfies?(req)
106
131
 
107
132
  update_pyproject_version(req)
108
133
  end
109
134
 
135
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
110
136
  def update_pyproject_version(req)
111
137
  requirement_strings = req[:requirement].split(",").map(&:strip)
112
138
 
@@ -132,6 +158,7 @@ module Dependabot
132
158
  req.merge(requirement: new_requirement)
133
159
  end
134
160
 
161
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
135
162
  def widen_pyproject_requirement(req)
136
163
  return req if new_version_satisfies?(req)
137
164
 
@@ -145,9 +172,10 @@ module Dependabot
145
172
  req.merge(requirement: new_requirement)
146
173
  end
147
174
 
175
+ sig { params(req_string: String).returns(String) }
148
176
  def add_new_requirement_option(req_string)
149
- option_to_copy = req_string.split(PYPROJECT_OR_SEPARATOR).last
150
- .split(PYPROJECT_SEPARATOR).first.strip
177
+ option_to_copy = T.must(T.must(req_string.split(PYPROJECT_OR_SEPARATOR).last)
178
+ .split(PYPROJECT_SEPARATOR).first).strip
151
179
  operator = option_to_copy.gsub(/\d.*/, "").strip
152
180
 
153
181
  new_option =
@@ -167,6 +195,7 @@ module Dependabot
167
195
  end
168
196
 
169
197
  # rubocop:disable Metrics/PerceivedComplexity
198
+ sig { params(req_string: String).returns(String) }
170
199
  def widen_requirement_range(req_string)
171
200
  requirement_strings = req_string.split(",").map(&:strip)
172
201
 
@@ -180,7 +209,7 @@ module Dependabot
180
209
  # range to include the new version
181
210
  v_req = requirement_strings
182
211
  .find { |r| r.start_with?("~", "^") || r.include?("*") }
183
- convert_to_range(v_req, latest_resolvable_version)
212
+ convert_to_range(T.must(v_req), T.must(latest_resolvable_version))
184
213
  else
185
214
  # Otherwise we have a range, and need to update the upper bound
186
215
  update_requirements_range(requirement_strings)
@@ -188,6 +217,7 @@ module Dependabot
188
217
  end
189
218
  # rubocop:enable Metrics/PerceivedComplexity
190
219
 
220
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
191
221
  def updated_requirement(req)
192
222
  return req unless latest_resolvable_version
193
223
  return req unless req.fetch(:requirement)
@@ -204,12 +234,14 @@ module Dependabot
204
234
  end
205
235
  end
206
236
 
237
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
207
238
  def update_requirement_if_needed(req)
208
239
  return req if new_version_satisfies?(req)
209
240
 
210
241
  update_requirement(req)
211
242
  end
212
243
 
244
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
213
245
  def update_requirement(req)
214
246
  requirement_strings = req[:requirement].split(",").map(&:strip)
215
247
 
@@ -229,6 +261,7 @@ module Dependabot
229
261
  req.merge(requirement: :unfixable)
230
262
  end
231
263
 
264
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
232
265
  def widen_requirement(req)
233
266
  return req if new_version_satisfies?(req)
234
267
 
@@ -237,29 +270,32 @@ module Dependabot
237
270
  req.merge(requirement: new_requirement)
238
271
  end
239
272
 
273
+ sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
240
274
  def new_version_satisfies?(req)
241
275
  requirement_class
242
276
  .requirements_array(req.fetch(:requirement))
243
- .any? { |r| r.satisfied_by?(latest_resolvable_version) }
277
+ .any? { |r| r.satisfied_by?(T.must(latest_resolvable_version)) }
244
278
  end
245
279
 
280
+ sig { params(requirement_strings: T::Array[String]).returns(String) }
246
281
  def find_and_update_equality_match(requirement_strings)
247
282
  if requirement_strings.any? { |r| requirement_class.new(r).exact? }
248
283
  # True equality match
249
- requirement_strings.find { |r| requirement_class.new(r).exact? }
250
- .sub(
251
- RequirementParser::VERSION,
252
- latest_resolvable_version.to_s
253
- )
284
+ T.must(requirement_strings.find { |r| requirement_class.new(r).exact? })
285
+ .sub(
286
+ RequirementParser::VERSION,
287
+ T.must(latest_resolvable_version).to_s
288
+ )
254
289
  else
255
290
  # Prefix match
256
- requirement_strings.find { |r| r.match?(/^(=+|\d)/) }
257
- .sub(RequirementParser::VERSION) do |v|
258
- at_same_precision(latest_resolvable_version.to_s, v)
291
+ T.must(requirement_strings.find { |r| r.match?(/^(=+|\d)/) })
292
+ .sub(RequirementParser::VERSION) do |v|
293
+ at_same_precision(T.must(latest_resolvable_version).to_s, v)
259
294
  end
260
295
  end
261
296
  end
262
297
 
298
+ sig { params(new_version: String, old_version: String).returns(String) }
263
299
  def at_same_precision(new_version, old_version)
264
300
  # return new_version unless old_version.include?("*")
265
301
 
@@ -273,16 +309,17 @@ module Dependabot
273
309
  .join(".")
274
310
  end
275
311
 
312
+ sig { params(requirement_strings: T::Array[String]).returns(String) }
276
313
  def update_requirements_range(requirement_strings)
277
314
  ruby_requirements =
278
315
  requirement_strings.map { |r| requirement_class.new(r) }
279
316
 
280
317
  updated_requirement_strings = ruby_requirements.flat_map do |r|
281
- next r.to_s if r.satisfied_by?(latest_resolvable_version)
318
+ next r.to_s if r.satisfied_by?(T.must(latest_resolvable_version))
282
319
 
283
320
  case op = r.requirements.first.first
284
321
  when "<"
285
- "<" + update_greatest_version(r.requirements.first.last, latest_resolvable_version)
322
+ "<" + update_greatest_version(r.requirements.first.last, T.must(latest_resolvable_version))
286
323
  when "<="
287
324
  "<=" + latest_resolvable_version.to_s
288
325
  when "!=", ">", ">="
@@ -298,10 +335,11 @@ module Dependabot
298
335
  end
299
336
 
300
337
  # Updates the version in a constraint to be the given version
338
+ sig { params(req_string: String, version_to_be_permitted: String).returns(String) }
301
339
  def bump_version(req_string, version_to_be_permitted)
302
- old_version = req_string
303
- .match(/(#{RequirementParser::VERSION})/o)
304
- .captures.first
340
+ old_version = T.must(T.must(req_string
341
+ .match(/(#{RequirementParser::VERSION})/o))
342
+ .captures.first)
305
343
 
306
344
  req_string.sub(
307
345
  old_version,
@@ -309,14 +347,15 @@ module Dependabot
309
347
  )
310
348
  end
311
349
 
350
+ sig { params(req_string: String, version_to_be_permitted: Dependabot::Uv::Version).returns(String) }
312
351
  def convert_to_range(req_string, version_to_be_permitted)
313
352
  # Construct an upper bound at the same precision that the original
314
353
  # requirement was at (taking into account ~ dynamics)
315
354
  index_to_update = index_to_update_for(req_string)
316
- ub_segments = version_to_be_permitted.segments
317
- ub_segments << 0 while ub_segments.count <= index_to_update
318
- ub_segments = ub_segments[0..index_to_update]
319
- ub_segments[index_to_update] += 1
355
+ ub_segments = T.let(version_to_be_permitted.segments, T::Array[T.any(String, Integer)])
356
+ ub_segments << "0" while ub_segments.count <= index_to_update
357
+ ub_segments = T.must(ub_segments[0..index_to_update])
358
+ ub_segments[index_to_update] = T.must(ub_segments[index_to_update]).to_i + 1
320
359
 
321
360
  lb_segments = lower_bound_segments_for_req(req_string)
322
361
 
@@ -328,6 +367,7 @@ module Dependabot
328
367
  ">=#{lb_segments.join('.')},<#{ub_segments.join('.')}"
329
368
  end
330
369
 
370
+ sig { params(req_string: String).returns(T::Array[Integer]) }
331
371
  def lower_bound_segments_for_req(req_string)
332
372
  requirement = requirement_class.new(req_string)
333
373
  version = requirement.requirements.first.last
@@ -339,6 +379,7 @@ module Dependabot
339
379
  lb_segments
340
380
  end
341
381
 
382
+ sig { params(req_string: String).returns(Integer) }
342
383
  def index_to_update_for(req_string)
343
384
  req = requirement_class.new(req_string.split(/[.\-]\*/).first)
344
385
  version = req.requirements.first.last.release
@@ -350,13 +391,19 @@ module Dependabot
350
391
  elsif req_string.strip.start_with?("~=", "==")
351
392
  version.segments.count - 2
352
393
  elsif req_string.strip.start_with?("~")
353
- req_string.split(".").count == 1 ? 0 : 1
394
+ req_string.split(".").one? ? 0 : 1
354
395
  else
355
396
  raise "Don't know how to convert #{req_string} to range"
356
397
  end
357
398
  end
358
399
 
359
400
  # Updates the version in a "<" constraint to allow the given version
401
+ sig do
402
+ params(
403
+ version: Gem::Version,
404
+ version_to_be_permitted: T.any(String, Dependabot::Uv::Version)
405
+ ).returns(String)
406
+ end
360
407
  def update_greatest_version(version, version_to_be_permitted)
361
408
  if version_to_be_permitted.is_a?(String)
362
409
  version_to_be_permitted =
@@ -365,7 +412,7 @@ module Dependabot
365
412
  version = version.release if version.prerelease?
366
413
 
367
414
  index_to_update = [
368
- version.segments.map.with_index { |n, i| n.zero? ? 0 : i }.max,
415
+ version.segments.map.with_index { |n, i| n.to_i.zero? ? 0 : i }.max,
369
416
  version_to_be_permitted.segments.count - 1
370
417
  ].min
371
418
 
@@ -373,7 +420,7 @@ module Dependabot
373
420
  if index < index_to_update
374
421
  version_to_be_permitted.segments[index]
375
422
  elsif index == index_to_update
376
- version_to_be_permitted.segments[index] + 1
423
+ version_to_be_permitted.segments[index].to_i + 1
377
424
  else
378
425
  0
379
426
  end
@@ -382,6 +429,7 @@ module Dependabot
382
429
  new_segments.join(".")
383
430
  end
384
431
 
432
+ sig { returns(T.class_of(Dependabot::Uv::Requirement)) }
385
433
  def requirement_class
386
434
  Uv::Requirement
387
435
  end