dependabot-uv 0.355.0 → 0.356.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.
- checksums.yaml +4 -4
- data/helpers/requirements.txt +1 -1
- data/lib/dependabot/uv/file_fetcher/workspace_fetcher.rb +3 -2
- data/lib/dependabot/uv/file_fetcher.rb +62 -350
- data/lib/dependabot/uv/file_updater/compile_file_updater.rb +1 -1
- data/lib/dependabot/uv/file_updater/lock_file_updater.rb +85 -4
- data/lib/dependabot/uv/file_updater/requirement_file_updater.rb +6 -86
- data/lib/dependabot/uv/file_updater/requirement_replacer.rb +6 -247
- data/lib/dependabot/uv/update_checker/latest_version_finder.rb +5 -26
- data/lib/dependabot/uv/update_checker/pip_compile_version_resolver.rb +1 -1
- data/lib/dependabot/uv/update_checker/pip_version_resolver.rb +5 -107
- data/lib/dependabot/uv/update_checker/requirements_updater.rb +6 -439
- data/lib/dependabot/uv/update_checker.rb +21 -180
- metadata +6 -6
|
@@ -1,449 +1,16 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "
|
|
5
|
-
|
|
6
|
-
require "dependabot/uv/requirement_parser"
|
|
7
|
-
require "dependabot/uv/requirement"
|
|
4
|
+
require "dependabot/python/update_checker/requirements_updater"
|
|
8
5
|
require "dependabot/uv/update_checker"
|
|
9
|
-
require "dependabot/uv/version"
|
|
10
|
-
require "dependabot/requirements_update_strategy"
|
|
11
6
|
|
|
12
7
|
module Dependabot
|
|
13
8
|
module Uv
|
|
14
9
|
class UpdateChecker
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
PYPROJECT_SEPARATOR = T.let(/#{PYPROJECT_OR_SEPARATOR}|,/, Regexp)
|
|
20
|
-
|
|
21
|
-
class UnfixableRequirement < StandardError; end
|
|
22
|
-
|
|
23
|
-
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
24
|
-
attr_reader :requirements
|
|
25
|
-
|
|
26
|
-
sig { returns(Dependabot::RequirementsUpdateStrategy) }
|
|
27
|
-
attr_reader :update_strategy
|
|
28
|
-
|
|
29
|
-
sig { returns(T::Boolean) }
|
|
30
|
-
attr_reader :has_lockfile
|
|
31
|
-
|
|
32
|
-
sig { returns(T.nilable(Dependabot::Uv::Version)) }
|
|
33
|
-
attr_reader :latest_resolvable_version
|
|
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
|
|
43
|
-
def initialize(
|
|
44
|
-
requirements:,
|
|
45
|
-
update_strategy:,
|
|
46
|
-
has_lockfile:,
|
|
47
|
-
latest_resolvable_version:
|
|
48
|
-
)
|
|
49
|
-
@requirements = T.let(requirements, T::Array[T::Hash[Symbol, T.untyped]])
|
|
50
|
-
@update_strategy = T.let(update_strategy, Dependabot::RequirementsUpdateStrategy)
|
|
51
|
-
@has_lockfile = T.let(has_lockfile, T::Boolean)
|
|
52
|
-
@latest_resolvable_version = T.let(nil, T.nilable(Dependabot::Uv::Version))
|
|
53
|
-
|
|
54
|
-
return unless latest_resolvable_version
|
|
55
|
-
|
|
56
|
-
@latest_resolvable_version =
|
|
57
|
-
Uv::Version.new(latest_resolvable_version)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
61
|
-
def updated_requirements
|
|
62
|
-
return requirements if update_strategy.lockfile_only?
|
|
63
|
-
|
|
64
|
-
requirements.map do |req|
|
|
65
|
-
case req[:file]
|
|
66
|
-
when /setup\.(?:py|cfg)$/ then updated_setup_requirement(req)
|
|
67
|
-
when "pyproject.toml" then updated_pyproject_requirement(req)
|
|
68
|
-
when "Pipfile" then updated_pipfile_requirement(req)
|
|
69
|
-
when /\.txt$|\.in$/ then updated_requirement(req)
|
|
70
|
-
else raise "Unexpected filename: #{req[:file]}"
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
78
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
79
|
-
def updated_setup_requirement(req)
|
|
80
|
-
return req unless latest_resolvable_version
|
|
81
|
-
return req unless req.fetch(:requirement)
|
|
82
|
-
return req if new_version_satisfies?(req)
|
|
83
|
-
|
|
84
|
-
req_strings = req[:requirement].split(",").map(&:strip)
|
|
85
|
-
|
|
86
|
-
new_requirement =
|
|
87
|
-
if req_strings.any? { |r| requirement_class.new(r).exact? }
|
|
88
|
-
find_and_update_equality_match(req_strings)
|
|
89
|
-
elsif req_strings.any? { |r| r.start_with?("~=", "==") }
|
|
90
|
-
tw_req = req_strings.find { |r| r.start_with?("~=", "==") }
|
|
91
|
-
convert_to_range(tw_req, T.must(latest_resolvable_version))
|
|
92
|
-
else
|
|
93
|
-
update_requirements_range(req_strings)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
req.merge(requirement: new_requirement)
|
|
97
|
-
rescue UnfixableRequirement
|
|
98
|
-
req.merge(requirement: :unfixable)
|
|
99
|
-
end
|
|
100
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
101
|
-
|
|
102
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
103
|
-
def updated_pipfile_requirement(req)
|
|
104
|
-
# For now, we just proxy to updated_requirement. In future this
|
|
105
|
-
# method may treat Pipfile requirements differently.
|
|
106
|
-
updated_requirement(req)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
110
|
-
def updated_pyproject_requirement(req)
|
|
111
|
-
return req unless latest_resolvable_version
|
|
112
|
-
return req unless req.fetch(:requirement)
|
|
113
|
-
return req if new_version_satisfies?(req) && !has_lockfile
|
|
114
|
-
|
|
115
|
-
# If the requirement uses || syntax then we always want to widen it
|
|
116
|
-
return widen_pyproject_requirement(req) if req.fetch(:requirement).match?(PYPROJECT_OR_SEPARATOR)
|
|
117
|
-
|
|
118
|
-
# If the requirement is a development dependency we always want to
|
|
119
|
-
# bump it
|
|
120
|
-
return update_pyproject_version(req) if req.fetch(:groups).include?("dev-dependencies")
|
|
121
|
-
|
|
122
|
-
case update_strategy
|
|
123
|
-
when RequirementsUpdateStrategy::WidenRanges then widen_pyproject_requirement(req)
|
|
124
|
-
when RequirementsUpdateStrategy::BumpVersions then update_pyproject_version(req)
|
|
125
|
-
when RequirementsUpdateStrategy::BumpVersionsIfNecessary then update_pyproject_version_if_needed(req)
|
|
126
|
-
else raise "Unexpected update strategy: #{update_strategy}"
|
|
127
|
-
end
|
|
128
|
-
rescue UnfixableRequirement
|
|
129
|
-
req.merge(requirement: :unfixable)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
133
|
-
def update_pyproject_version_if_needed(req)
|
|
134
|
-
return req if new_version_satisfies?(req)
|
|
135
|
-
|
|
136
|
-
update_pyproject_version(req)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
140
|
-
def update_pyproject_version(req)
|
|
141
|
-
requirement_strings = req[:requirement].split(",").map(&:strip)
|
|
142
|
-
|
|
143
|
-
new_requirement =
|
|
144
|
-
if requirement_strings.any? { |r| r.match?(/^=|^\d/) }
|
|
145
|
-
# If there is an equality operator, just update that. It must
|
|
146
|
-
# be binding and any other requirements will be being ignored
|
|
147
|
-
find_and_update_equality_match(requirement_strings)
|
|
148
|
-
elsif requirement_strings.any? { |r| r.start_with?("~", "^") }
|
|
149
|
-
# If a compatibility operator is being used, just bump its
|
|
150
|
-
# version (and remove any other requirements)
|
|
151
|
-
v_req = requirement_strings.find { |r| r.start_with?("~", "^") }
|
|
152
|
-
bump_version(v_req, latest_resolvable_version.to_s)
|
|
153
|
-
elsif new_version_satisfies?(req)
|
|
154
|
-
# Otherwise we're looking at a range operator. No change
|
|
155
|
-
# required if it's already satisfied
|
|
156
|
-
req.fetch(:requirement)
|
|
157
|
-
else
|
|
158
|
-
# But if it's not, update it
|
|
159
|
-
update_requirements_range(requirement_strings)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
req.merge(requirement: new_requirement)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
166
|
-
def widen_pyproject_requirement(req)
|
|
167
|
-
return req if new_version_satisfies?(req)
|
|
168
|
-
|
|
169
|
-
new_requirement =
|
|
170
|
-
if req[:requirement].match?(PYPROJECT_OR_SEPARATOR)
|
|
171
|
-
add_new_requirement_option(req[:requirement])
|
|
172
|
-
else
|
|
173
|
-
widen_requirement_range(req[:requirement])
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
req.merge(requirement: new_requirement)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
sig { params(req_string: String).returns(String) }
|
|
180
|
-
def add_new_requirement_option(req_string)
|
|
181
|
-
option_to_copy = T.must(
|
|
182
|
-
T.must(req_string.split(PYPROJECT_OR_SEPARATOR).last)
|
|
183
|
-
.split(PYPROJECT_SEPARATOR).first
|
|
184
|
-
).strip
|
|
185
|
-
operator = option_to_copy.gsub(/\d.*/, "").strip
|
|
186
|
-
|
|
187
|
-
new_option =
|
|
188
|
-
case operator
|
|
189
|
-
when "", "==", "==="
|
|
190
|
-
find_and_update_equality_match([option_to_copy])
|
|
191
|
-
when "~=", "~", "^"
|
|
192
|
-
bump_version(option_to_copy, latest_resolvable_version.to_s)
|
|
193
|
-
else
|
|
194
|
-
# We don't expect to see OR conditions used with range
|
|
195
|
-
# operators. If / when we see it, we should handle it.
|
|
196
|
-
raise "Unexpected operator: #{operator}"
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# TODO: Match source spacing
|
|
200
|
-
"#{req_string.strip} || #{new_option.strip}"
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
204
|
-
sig { params(req_string: String).returns(String) }
|
|
205
|
-
def widen_requirement_range(req_string)
|
|
206
|
-
requirement_strings = req_string.split(",").map(&:strip)
|
|
207
|
-
|
|
208
|
-
if requirement_strings.any? { |r| r.match?(/(^=|^\d)[^*]*$/) }
|
|
209
|
-
# If there is an equality operator, just update that.
|
|
210
|
-
# (i.e., assume it's being used deliberately)
|
|
211
|
-
find_and_update_equality_match(requirement_strings)
|
|
212
|
-
elsif requirement_strings.any? { |r| r.start_with?("~", "^") } ||
|
|
213
|
-
requirement_strings.any? { |r| r.include?("*") }
|
|
214
|
-
# If a compatibility operator is being used, widen its
|
|
215
|
-
# range to include the new version
|
|
216
|
-
v_req = requirement_strings
|
|
217
|
-
.find { |r| r.start_with?("~", "^") || r.include?("*") }
|
|
218
|
-
convert_to_range(T.must(v_req), T.must(latest_resolvable_version))
|
|
219
|
-
else
|
|
220
|
-
# Otherwise we have a range, and need to update the upper bound
|
|
221
|
-
update_requirements_range(requirement_strings)
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
225
|
-
|
|
226
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
227
|
-
def updated_requirement(req)
|
|
228
|
-
return req unless latest_resolvable_version
|
|
229
|
-
return req unless req.fetch(:requirement)
|
|
230
|
-
|
|
231
|
-
case update_strategy
|
|
232
|
-
when RequirementsUpdateStrategy::WidenRanges
|
|
233
|
-
widen_requirement(req)
|
|
234
|
-
when RequirementsUpdateStrategy::BumpVersions
|
|
235
|
-
update_requirement(req)
|
|
236
|
-
when RequirementsUpdateStrategy::BumpVersionsIfNecessary
|
|
237
|
-
update_requirement_if_needed(req)
|
|
238
|
-
else
|
|
239
|
-
raise "Unexpected update strategy: #{update_strategy}"
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
244
|
-
def update_requirement_if_needed(req)
|
|
245
|
-
return req if new_version_satisfies?(req)
|
|
246
|
-
|
|
247
|
-
update_requirement(req)
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
251
|
-
def update_requirement(req)
|
|
252
|
-
requirement_strings = req[:requirement].split(",").map(&:strip)
|
|
253
|
-
|
|
254
|
-
new_requirement =
|
|
255
|
-
if requirement_strings.any? { |r| r.match?(/^[=\d]/) }
|
|
256
|
-
find_and_update_equality_match(requirement_strings)
|
|
257
|
-
elsif requirement_strings.any? { |r| r.start_with?("~=") }
|
|
258
|
-
tw_req = requirement_strings.find { |r| r.start_with?("~=") }
|
|
259
|
-
bump_version(tw_req, latest_resolvable_version.to_s)
|
|
260
|
-
elsif new_version_satisfies?(req)
|
|
261
|
-
req.fetch(:requirement)
|
|
262
|
-
else
|
|
263
|
-
update_requirements_range(requirement_strings)
|
|
264
|
-
end
|
|
265
|
-
req.merge(requirement: new_requirement)
|
|
266
|
-
rescue UnfixableRequirement
|
|
267
|
-
req.merge(requirement: :unfixable)
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
271
|
-
def widen_requirement(req)
|
|
272
|
-
return req if new_version_satisfies?(req)
|
|
273
|
-
|
|
274
|
-
new_requirement = widen_requirement_range(req[:requirement])
|
|
275
|
-
|
|
276
|
-
req.merge(requirement: new_requirement)
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
|
|
280
|
-
def new_version_satisfies?(req)
|
|
281
|
-
requirement_class
|
|
282
|
-
.requirements_array(req.fetch(:requirement))
|
|
283
|
-
.any? { |r| r.satisfied_by?(T.must(latest_resolvable_version)) }
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
sig { params(requirement_strings: T::Array[String]).returns(String) }
|
|
287
|
-
def find_and_update_equality_match(requirement_strings)
|
|
288
|
-
if requirement_strings.any? { |r| requirement_class.new(r).exact? }
|
|
289
|
-
# True equality match
|
|
290
|
-
T.must(requirement_strings.find { |r| requirement_class.new(r).exact? })
|
|
291
|
-
.sub(
|
|
292
|
-
RequirementParser::VERSION,
|
|
293
|
-
T.must(latest_resolvable_version).to_s
|
|
294
|
-
)
|
|
295
|
-
else
|
|
296
|
-
# Prefix match
|
|
297
|
-
T.must(requirement_strings.find { |r| r.match?(/^(=+|\d)/) })
|
|
298
|
-
.sub(RequirementParser::VERSION) do |v|
|
|
299
|
-
at_same_precision(T.must(latest_resolvable_version).to_s, v)
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
sig { params(new_version: String, old_version: String).returns(String) }
|
|
305
|
-
def at_same_precision(new_version, old_version)
|
|
306
|
-
# return new_version unless old_version.include?("*")
|
|
307
|
-
|
|
308
|
-
count = old_version.split(".").count
|
|
309
|
-
precision = old_version.split(".").index("*") || count
|
|
310
|
-
|
|
311
|
-
new_version
|
|
312
|
-
.split(".")
|
|
313
|
-
.first(count)
|
|
314
|
-
.map.with_index { |s, i| i < precision ? s : "*" }
|
|
315
|
-
.join(".")
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
sig { params(requirement_strings: T::Array[String]).returns(String) }
|
|
319
|
-
def update_requirements_range(requirement_strings)
|
|
320
|
-
ruby_requirements =
|
|
321
|
-
requirement_strings.map { |r| requirement_class.new(r) }
|
|
322
|
-
|
|
323
|
-
updated_requirement_strings = ruby_requirements.flat_map do |r|
|
|
324
|
-
next r.to_s if r.satisfied_by?(T.must(latest_resolvable_version))
|
|
325
|
-
|
|
326
|
-
case op = r.requirements.first.first
|
|
327
|
-
when "<"
|
|
328
|
-
"<" + update_greatest_version(r.requirements.first.last, T.must(latest_resolvable_version))
|
|
329
|
-
when "<="
|
|
330
|
-
"<=" + latest_resolvable_version.to_s
|
|
331
|
-
when "!=", ">", ">="
|
|
332
|
-
raise UnfixableRequirement
|
|
333
|
-
else
|
|
334
|
-
raise "Unexpected op for unsatisfied requirement: #{op}"
|
|
335
|
-
end
|
|
336
|
-
end.compact
|
|
337
|
-
|
|
338
|
-
updated_requirement_strings
|
|
339
|
-
.sort_by { |r| requirement_class.new(r).requirements.first.last }
|
|
340
|
-
.map(&:to_s).join(",").delete(" ")
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
# Updates the version in a constraint to be the given version
|
|
344
|
-
sig { params(req_string: String, version_to_be_permitted: String).returns(String) }
|
|
345
|
-
def bump_version(req_string, version_to_be_permitted)
|
|
346
|
-
old_version = T.must(
|
|
347
|
-
T.must(
|
|
348
|
-
req_string
|
|
349
|
-
.match(/(#{RequirementParser::VERSION})/o)
|
|
350
|
-
)
|
|
351
|
-
.captures.first
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
req_string.sub(
|
|
355
|
-
old_version,
|
|
356
|
-
at_same_precision(version_to_be_permitted, old_version)
|
|
357
|
-
)
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
sig { params(req_string: String, version_to_be_permitted: Dependabot::Uv::Version).returns(String) }
|
|
361
|
-
def convert_to_range(req_string, version_to_be_permitted)
|
|
362
|
-
# Construct an upper bound at the same precision that the original
|
|
363
|
-
# requirement was at (taking into account ~ dynamics)
|
|
364
|
-
index_to_update = index_to_update_for(req_string)
|
|
365
|
-
ub_segments = T.let(version_to_be_permitted.segments, T::Array[T.any(String, Integer)])
|
|
366
|
-
ub_segments << "0" while ub_segments.count <= index_to_update
|
|
367
|
-
ub_segments = T.must(ub_segments[0..index_to_update])
|
|
368
|
-
ub_segments[index_to_update] = T.must(ub_segments[index_to_update]).to_i + 1
|
|
369
|
-
|
|
370
|
-
lb_segments = lower_bound_segments_for_req(req_string)
|
|
371
|
-
|
|
372
|
-
# Ensure versions have the same length as each other (cosmetic)
|
|
373
|
-
length = [lb_segments.count, ub_segments.count].max
|
|
374
|
-
lb_segments.fill(0, lb_segments.count...length)
|
|
375
|
-
ub_segments.fill(0, ub_segments.count...length)
|
|
376
|
-
|
|
377
|
-
">=#{lb_segments.join('.')},<#{ub_segments.join('.')}"
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
sig { params(req_string: String).returns(T::Array[Integer]) }
|
|
381
|
-
def lower_bound_segments_for_req(req_string)
|
|
382
|
-
requirement = requirement_class.new(req_string)
|
|
383
|
-
version = requirement.requirements.first.last
|
|
384
|
-
version = version.release if version.prerelease?
|
|
385
|
-
|
|
386
|
-
lb_segments = version.segments
|
|
387
|
-
lb_segments.pop while lb_segments.last.zero?
|
|
388
|
-
|
|
389
|
-
lb_segments
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
sig { params(req_string: String).returns(Integer) }
|
|
393
|
-
def index_to_update_for(req_string)
|
|
394
|
-
req = requirement_class.new(req_string.split(/[.\-]\*/).first)
|
|
395
|
-
version = req.requirements.first.last.release
|
|
396
|
-
|
|
397
|
-
if req_string.strip.start_with?("^")
|
|
398
|
-
version.segments.index { |i| i != 0 }
|
|
399
|
-
elsif req_string.include?("*")
|
|
400
|
-
version.segments.count - 1
|
|
401
|
-
elsif req_string.strip.start_with?("~=", "==")
|
|
402
|
-
version.segments.count - 2
|
|
403
|
-
elsif req_string.strip.start_with?("~")
|
|
404
|
-
req_string.split(".").one? ? 0 : 1
|
|
405
|
-
else
|
|
406
|
-
raise "Don't know how to convert #{req_string} to range"
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
# Updates the version in a "<" constraint to allow the given version
|
|
411
|
-
sig do
|
|
412
|
-
params(
|
|
413
|
-
version: Gem::Version,
|
|
414
|
-
version_to_be_permitted: T.any(String, Dependabot::Uv::Version)
|
|
415
|
-
).returns(String)
|
|
416
|
-
end
|
|
417
|
-
def update_greatest_version(version, version_to_be_permitted)
|
|
418
|
-
if version_to_be_permitted.is_a?(String)
|
|
419
|
-
version_to_be_permitted =
|
|
420
|
-
Uv::Version.new(version_to_be_permitted)
|
|
421
|
-
end
|
|
422
|
-
version = version.release if version.prerelease?
|
|
423
|
-
|
|
424
|
-
index_to_update = [
|
|
425
|
-
version.segments.map.with_index { |n, i| n.to_i.zero? ? 0 : i }.max,
|
|
426
|
-
version_to_be_permitted.segments.count - 1
|
|
427
|
-
].min
|
|
428
|
-
|
|
429
|
-
new_segments = version.segments.map.with_index do |_, index|
|
|
430
|
-
if index < index_to_update
|
|
431
|
-
version_to_be_permitted.segments[index]
|
|
432
|
-
elsif index == index_to_update
|
|
433
|
-
version_to_be_permitted.segments[index].to_i + 1
|
|
434
|
-
else
|
|
435
|
-
0
|
|
436
|
-
end
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
new_segments.join(".")
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
sig { returns(T.class_of(Dependabot::Uv::Requirement)) }
|
|
443
|
-
def requirement_class
|
|
444
|
-
Uv::Requirement
|
|
445
|
-
end
|
|
446
|
-
end
|
|
10
|
+
# UV uses the same requirements update logic as Python.
|
|
11
|
+
# Both ecosystems share Version and Requirement classes (via aliases),
|
|
12
|
+
# so we reuse Python's RequirementsUpdater implementation.
|
|
13
|
+
RequirementsUpdater = Dependabot::Python::UpdateChecker::RequirementsUpdater
|
|
447
14
|
end
|
|
448
15
|
end
|
|
449
16
|
end
|