dependabot-uv 0.393.0 → 0.394.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f9eee59202ca318e612e23d7303309e498d51999cda642a1660d41390a2bd4a8
|
|
4
|
+
data.tar.gz: 3b3bb2ea60f190db5d8c9bd36f1ff01531097788c3755c6638d5a4e8e3ebf923
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f6d9c0f1ea2c05bab8acea5563a4a74c016ab9b8bd77639afe9468a8f7b8cb18d238585b5bdc11b255bf8e8d0af01d7d470c07ab9b83792d8001156bb73a199
|
|
7
|
+
data.tar.gz: '09c83e67c0b94adf7ad3f848f17521b57e282ef33db9d4ac0b5991d0ea8135024fc9eb5e78a1515a7f9f4c826f8db3347e25b24ed842ed85681a0b3dd8289e94'
|
data/helpers/requirements.txt
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
pip==26.
|
|
2
|
-
pip-tools==7.
|
|
1
|
+
pip==26.2.1
|
|
2
|
+
pip-tools==7.6.1
|
|
3
3
|
flake8==7.3.0
|
|
4
4
|
hashin==1.0.5
|
|
5
5
|
pipenv==2024.4.1
|
|
6
6
|
plette==2.2.1
|
|
7
|
-
poetry==2.
|
|
7
|
+
poetry==2.4.1
|
|
8
8
|
# TODO: Replace 3p package `tomli` with 3.11's new stdlib `tomllib` once we drop support for Python 3.10.
|
|
9
9
|
tomli==2.4.1
|
|
10
|
-
uv==0.12.
|
|
10
|
+
uv==0.12.5
|
|
11
11
|
|
|
12
12
|
# Some dependencies will only install if Cython is present
|
|
13
|
-
Cython==3.
|
|
13
|
+
Cython==3.3.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "toml-rb"
|
|
@@ -9,6 +9,9 @@ require "dependabot/uv/file_parser"
|
|
|
9
9
|
require "dependabot/uv/requirement"
|
|
10
10
|
require "dependabot/errors"
|
|
11
11
|
require "dependabot/uv/name_normaliser"
|
|
12
|
+
require "dependabot/python/file_parser/pep_dependency"
|
|
13
|
+
require "dependabot/python/file_parser/poetry_lock"
|
|
14
|
+
require "dependabot/python/file_parser/pyproject_document"
|
|
12
15
|
|
|
13
16
|
module Dependabot
|
|
14
17
|
module Uv
|
|
@@ -16,6 +19,10 @@ module Dependabot
|
|
|
16
19
|
class PyprojectFilesParser
|
|
17
20
|
extend T::Sig
|
|
18
21
|
|
|
22
|
+
PepDependency = Dependabot::Python::FileParser::PepDependency
|
|
23
|
+
PoetryLock = Dependabot::Python::FileParser::PoetryLock
|
|
24
|
+
PyprojectDocument = Dependabot::Python::FileParser::PyprojectDocument
|
|
25
|
+
|
|
19
26
|
POETRY_DEPENDENCY_TYPES = %w(dependencies dev-dependencies).freeze
|
|
20
27
|
|
|
21
28
|
# https://python-poetry.org/docs/dependency-specification/
|
|
@@ -24,6 +31,8 @@ module Dependabot
|
|
|
24
31
|
sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).void }
|
|
25
32
|
def initialize(dependency_files:)
|
|
26
33
|
@dependency_files = dependency_files
|
|
34
|
+
@pyproject_document = T.let(nil, T.nilable(PyprojectDocument))
|
|
35
|
+
@poetry_lock_document = T.let(nil, T.nilable(PoetryLock))
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
|
@@ -61,13 +70,11 @@ module Dependabot
|
|
|
61
70
|
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
|
62
71
|
|
|
63
72
|
POETRY_DEPENDENCY_TYPES.each do |type|
|
|
64
|
-
|
|
65
|
-
dependencies += parse_poetry_dependency_group(type, deps_hash)
|
|
73
|
+
dependencies += parse_poetry_dependency_group(type, pyproject_document.poetry_dependencies(type))
|
|
66
74
|
end
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
dependencies += parse_poetry_dependency_group(group, group_spec["dependencies"])
|
|
76
|
+
pyproject_document.poetry_groups.each do |group, dependencies_by_name|
|
|
77
|
+
dependencies += parse_poetry_dependency_group(group, dependencies_by_name)
|
|
71
78
|
end
|
|
72
79
|
dependencies
|
|
73
80
|
end
|
|
@@ -85,20 +92,20 @@ module Dependabot
|
|
|
85
92
|
parse_pep621_pep735_dependencies(pyproject_file).each do |dep|
|
|
86
93
|
# If a requirement has a `<` or `<=` marker then updating it is
|
|
87
94
|
# probably blocked. Ignore it.
|
|
88
|
-
next if dep
|
|
95
|
+
next if dep.markers&.include?("<")
|
|
89
96
|
|
|
90
97
|
# In uv no constraint means any version is acceptable
|
|
91
|
-
requirement_value = dep
|
|
98
|
+
requirement_value = dep.requirement == "" ? "*" : dep.requirement
|
|
92
99
|
|
|
93
100
|
dependencies <<
|
|
94
101
|
Dependency.new(
|
|
95
|
-
name: normalised_name(dep
|
|
96
|
-
version: dep
|
|
102
|
+
name: normalised_name(dep.name, dep.extras),
|
|
103
|
+
version: dep.version&.include?("*") ? nil : dep.version,
|
|
97
104
|
requirements: [{
|
|
98
105
|
requirement: requirement_value,
|
|
99
|
-
file: Pathname.new(dep
|
|
106
|
+
file: Pathname.new(dep.file).cleanpath.to_path,
|
|
100
107
|
source: nil,
|
|
101
|
-
groups: [dep
|
|
108
|
+
groups: [dep.requirement_type].compact
|
|
102
109
|
}],
|
|
103
110
|
package_manager: "uv"
|
|
104
111
|
)
|
|
@@ -139,8 +146,7 @@ module Dependabot
|
|
|
139
146
|
def workspace_globs(key)
|
|
140
147
|
return [] unless pyproject
|
|
141
148
|
|
|
142
|
-
|
|
143
|
-
globs.is_a?(Array) ? globs.grep(String) : []
|
|
149
|
+
pyproject_document.workspace_globs(key)
|
|
144
150
|
end
|
|
145
151
|
|
|
146
152
|
sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
|
|
@@ -172,17 +178,16 @@ module Dependabot
|
|
|
172
178
|
sig do
|
|
173
179
|
params(
|
|
174
180
|
type: String,
|
|
175
|
-
deps_hash:
|
|
176
|
-
T.untyped]
|
|
181
|
+
deps_hash: PyprojectDocument::PoetryDependencyMap
|
|
177
182
|
).returns(Dependabot::FileParsers::Base::DependencySet)
|
|
178
183
|
end
|
|
179
184
|
def parse_poetry_dependency_group(type, deps_hash)
|
|
180
185
|
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
|
181
186
|
|
|
182
|
-
deps_hash.each do |name,
|
|
187
|
+
deps_hash.each do |name, requirements_data|
|
|
183
188
|
next if normalise(name) == "python"
|
|
184
189
|
|
|
185
|
-
requirements = parse_requirements_from(
|
|
190
|
+
requirements = parse_requirements_from(requirements_data, type)
|
|
186
191
|
next if requirements.empty?
|
|
187
192
|
|
|
188
193
|
dependencies << Dependency.new(
|
|
@@ -200,57 +205,62 @@ module Dependabot
|
|
|
200
205
|
NameNormaliser.normalise_including_extras(name, extras)
|
|
201
206
|
end
|
|
202
207
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
sig do
|
|
209
|
+
params(
|
|
210
|
+
requirements_data: T::Array[PyprojectDocument::PoetryRequirement],
|
|
211
|
+
type: String
|
|
212
|
+
).returns(T::Array[Dependabot::Dependency::RequirementInput])
|
|
213
|
+
end
|
|
214
|
+
def parse_requirements_from(requirements_data, type)
|
|
215
|
+
requirements_data.filter_map { |requirement| poetry_requirement(requirement, type) }
|
|
216
|
+
end
|
|
208
217
|
|
|
218
|
+
sig do
|
|
219
|
+
params(
|
|
220
|
+
requirement: PyprojectDocument::PoetryRequirement,
|
|
221
|
+
type: String
|
|
222
|
+
).returns(T.nilable(Dependabot::Dependency::RequirementInput))
|
|
223
|
+
end
|
|
224
|
+
def poetry_requirement(requirement, type)
|
|
225
|
+
if requirement.is_a?(String)
|
|
209
226
|
check_requirements(requirement)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
groups: [type]
|
|
217
|
-
}
|
|
218
|
-
else
|
|
219
|
-
{
|
|
220
|
-
requirement: requirement["version"],
|
|
221
|
-
file: T.must(pyproject).name,
|
|
222
|
-
source: requirement.fetch("source", nil),
|
|
223
|
-
groups: [type]
|
|
224
|
-
}
|
|
225
|
-
end
|
|
227
|
+
return {
|
|
228
|
+
requirement: requirement,
|
|
229
|
+
file: T.must(pyproject).name,
|
|
230
|
+
source: nil,
|
|
231
|
+
groups: [type]
|
|
232
|
+
}
|
|
226
233
|
end
|
|
234
|
+
|
|
235
|
+
return if UNSUPPORTED_DEPENDENCY_TYPES.intersect?(requirement.keys)
|
|
236
|
+
|
|
237
|
+
check_requirements(requirement)
|
|
238
|
+
{
|
|
239
|
+
requirement: requirement_string(requirement, "version"),
|
|
240
|
+
file: T.must(pyproject).name,
|
|
241
|
+
source: poetry_source(requirement["source"]),
|
|
242
|
+
groups: [type]
|
|
243
|
+
}
|
|
227
244
|
end
|
|
228
245
|
|
|
229
|
-
sig { returns(T
|
|
246
|
+
sig { returns(T::Boolean) }
|
|
230
247
|
def using_poetry?
|
|
231
|
-
|
|
248
|
+
pyproject_document.poetry?
|
|
232
249
|
end
|
|
233
250
|
|
|
234
251
|
sig { returns(T::Boolean) }
|
|
235
252
|
def using_pep621?
|
|
236
|
-
|
|
237
|
-
!parsed_pyproject.dig("project", "optional-dependencies").nil? ||
|
|
238
|
-
!parsed_pyproject.dig("build-system", "requires").nil?
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
|
242
|
-
def poetry_root
|
|
243
|
-
parsed_pyproject.dig("tool", "poetry")
|
|
253
|
+
pyproject_document.pep621?
|
|
244
254
|
end
|
|
245
255
|
|
|
246
256
|
sig { returns(T::Boolean) }
|
|
247
257
|
def using_pep735?
|
|
248
|
-
|
|
258
|
+
pyproject_document.pep735?
|
|
249
259
|
end
|
|
250
260
|
|
|
251
|
-
sig { returns(T
|
|
261
|
+
sig { returns(T::Boolean) }
|
|
252
262
|
def using_pdm?
|
|
253
|
-
using_pep621? && pdm_lock
|
|
263
|
+
using_pep621? && !pdm_lock.nil?
|
|
254
264
|
end
|
|
255
265
|
|
|
256
266
|
# Create a DependencySet where each element has no requirement. Any
|
|
@@ -261,15 +271,15 @@ module Dependabot
|
|
|
261
271
|
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
|
262
272
|
|
|
263
273
|
source_types = %w(directory git url)
|
|
264
|
-
|
|
265
|
-
next if source_types.include?(
|
|
274
|
+
poetry_lock_document.packages.each do |package|
|
|
275
|
+
next if source_types.include?(package.source_type)
|
|
266
276
|
|
|
267
|
-
name = normalise(
|
|
277
|
+
name = normalise(package.name)
|
|
268
278
|
|
|
269
279
|
dependencies <<
|
|
270
280
|
Dependency.new(
|
|
271
281
|
name: name,
|
|
272
|
-
version:
|
|
282
|
+
version: package.version,
|
|
273
283
|
requirements: [],
|
|
274
284
|
package_manager: "uv",
|
|
275
285
|
subdependency_metadata: [{
|
|
@@ -292,8 +302,10 @@ module Dependabot
|
|
|
292
302
|
sig { returns(T::Array[T.nilable(String)]) }
|
|
293
303
|
def parse_production_dependency_names
|
|
294
304
|
SharedHelpers.in_a_temporary_directory do
|
|
295
|
-
|
|
296
|
-
|
|
305
|
+
pyproject_file = T.must(pyproject)
|
|
306
|
+
lockfile_file = T.must(lockfile)
|
|
307
|
+
File.write(pyproject_file.name, pyproject_file.content)
|
|
308
|
+
File.write(lockfile_file.name, lockfile_file.content)
|
|
297
309
|
|
|
298
310
|
begin
|
|
299
311
|
output = SharedHelpers.run_shell_command("pyenv exec poetry show --only main")
|
|
@@ -312,18 +324,19 @@ module Dependabot
|
|
|
312
324
|
end
|
|
313
325
|
end
|
|
314
326
|
|
|
315
|
-
sig { params(dep_name: String).returns(T.
|
|
327
|
+
sig { params(dep_name: String).returns(T.nilable(String)) }
|
|
316
328
|
def version_from_lockfile(dep_name)
|
|
317
|
-
return unless
|
|
329
|
+
return unless poetry_lock
|
|
318
330
|
|
|
319
|
-
|
|
320
|
-
.find { |p| normalise(p.fetch("name")) == normalise(dep_name) }
|
|
321
|
-
&.fetch("version", nil)
|
|
331
|
+
poetry_lock_document.version_for(dep_name) { |name| normalise(name) }
|
|
322
332
|
end
|
|
323
333
|
|
|
324
|
-
sig
|
|
334
|
+
sig do
|
|
335
|
+
params(req: PyprojectDocument::PoetryRequirement)
|
|
336
|
+
.returns(T::Array[Dependabot::Uv::Requirement])
|
|
337
|
+
end
|
|
325
338
|
def check_requirements(req)
|
|
326
|
-
requirement = req.is_a?(String) ? req : req
|
|
339
|
+
requirement = req.is_a?(String) ? req : requirement_string(req, "version")
|
|
327
340
|
Uv::Requirement.requirements_array(requirement)
|
|
328
341
|
rescue Gem::Requirement::BadRequirementError => e
|
|
329
342
|
raise Dependabot::DependencyFileNotEvaluatable, e.message
|
|
@@ -334,21 +347,14 @@ module Dependabot
|
|
|
334
347
|
NameNormaliser.normalise(name)
|
|
335
348
|
end
|
|
336
349
|
|
|
337
|
-
sig { returns(
|
|
338
|
-
def
|
|
339
|
-
@
|
|
340
|
-
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
|
341
|
-
raise Dependabot::DependencyFileNotParseable, T.must(pyproject).path
|
|
350
|
+
sig { returns(PyprojectDocument) }
|
|
351
|
+
def pyproject_document
|
|
352
|
+
@pyproject_document ||= PyprojectDocument.from_file(T.must(pyproject))
|
|
342
353
|
end
|
|
343
354
|
|
|
344
|
-
sig { returns(
|
|
345
|
-
def
|
|
346
|
-
@
|
|
347
|
-
TomlRB.parse(T.must(poetry_lock).content),
|
|
348
|
-
T.nilable(T::Hash[String, T.untyped])
|
|
349
|
-
)
|
|
350
|
-
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
|
351
|
-
raise Dependabot::DependencyFileNotParseable, T.must(poetry_lock).path
|
|
355
|
+
sig { returns(PoetryLock) }
|
|
356
|
+
def poetry_lock_document
|
|
357
|
+
@poetry_lock_document ||= PoetryLock.from_file(T.must(poetry_lock))
|
|
352
358
|
end
|
|
353
359
|
|
|
354
360
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
@@ -359,20 +365,22 @@ module Dependabot
|
|
|
359
365
|
)
|
|
360
366
|
end
|
|
361
367
|
|
|
362
|
-
sig { returns(T.
|
|
368
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
363
369
|
def lockfile
|
|
364
370
|
poetry_lock
|
|
365
371
|
end
|
|
366
372
|
|
|
367
|
-
sig { params(pyproject_file: Dependabot::DependencyFile).returns(T
|
|
373
|
+
sig { params(pyproject_file: Dependabot::DependencyFile).returns(T::Array[PepDependency]) }
|
|
368
374
|
def parse_pep621_pep735_dependencies(pyproject_file)
|
|
369
375
|
SharedHelpers.in_a_temporary_directory do
|
|
370
376
|
write_temporary_pyproject(pyproject_file)
|
|
371
377
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
378
|
+
PepDependency.from_helper_result(
|
|
379
|
+
SharedHelpers.run_helper_subprocess(
|
|
380
|
+
command: "pyenv exec python3 #{NativeHelpers.python_helper_path}",
|
|
381
|
+
function: "parse_pep621_pep735_dependencies",
|
|
382
|
+
args: [pyproject_file.name]
|
|
383
|
+
)
|
|
376
384
|
)
|
|
377
385
|
end
|
|
378
386
|
end
|
|
@@ -384,11 +392,6 @@ module Dependabot
|
|
|
384
392
|
File.write(path, pyproject_file.content)
|
|
385
393
|
end
|
|
386
394
|
|
|
387
|
-
sig { returns(T.untyped) }
|
|
388
|
-
def parsed_lockfile
|
|
389
|
-
parsed_poetry_lock if poetry_lock
|
|
390
|
-
end
|
|
391
|
-
|
|
392
395
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
393
396
|
def poetry_lock
|
|
394
397
|
@poetry_lock ||= T.let(
|
|
@@ -397,6 +400,36 @@ module Dependabot
|
|
|
397
400
|
)
|
|
398
401
|
end
|
|
399
402
|
|
|
403
|
+
sig do
|
|
404
|
+
params(
|
|
405
|
+
requirement: T::Hash[String, Object],
|
|
406
|
+
key: String
|
|
407
|
+
).returns(T.nilable(String))
|
|
408
|
+
end
|
|
409
|
+
def requirement_string(requirement, key)
|
|
410
|
+
value = requirement[key]
|
|
411
|
+
return if value.nil?
|
|
412
|
+
return value if value.is_a?(String)
|
|
413
|
+
|
|
414
|
+
raise TypeError, "Poetry dependency #{key} must be a string or nil"
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
sig { params(value: T.nilable(Object)).returns(T.nilable(Dependabot::DependencyRequirement::Source)) }
|
|
418
|
+
def poetry_source(value)
|
|
419
|
+
return if value.nil?
|
|
420
|
+
return value if value.is_a?(String)
|
|
421
|
+
raise TypeError, "Poetry dependency source must be a string, object, or nil" unless value.is_a?(Hash)
|
|
422
|
+
|
|
423
|
+
source = T.let({}, Dependabot::DependencyRequirement::ObjectHash)
|
|
424
|
+
value.each do |raw_key, raw_value|
|
|
425
|
+
key = T.cast(raw_key, Object)
|
|
426
|
+
raise TypeError, "Poetry dependency source keys must be strings" unless key.is_a?(String)
|
|
427
|
+
|
|
428
|
+
source[key] = T.cast(raw_value, Object)
|
|
429
|
+
end
|
|
430
|
+
source
|
|
431
|
+
end
|
|
432
|
+
|
|
400
433
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
401
434
|
def pdm_lock
|
|
402
435
|
@pdm_lock ||= T.let(
|
|
@@ -44,21 +44,33 @@ module Dependabot
|
|
|
44
44
|
sig { returns(T.nilable(String)) }
|
|
45
45
|
attr_reader :repo_contents_path
|
|
46
46
|
|
|
47
|
+
sig { returns(T.nilable(String)) }
|
|
48
|
+
attr_reader :target_requirement
|
|
49
|
+
|
|
47
50
|
sig do
|
|
48
51
|
params(
|
|
49
52
|
dependencies: T::Array[Dependency],
|
|
50
53
|
dependency_files: T::Array[DependencyFile],
|
|
51
54
|
credentials: T::Array[Dependabot::Credential],
|
|
52
55
|
index_urls: T.nilable(T::Array[T.nilable(String)]),
|
|
53
|
-
repo_contents_path: T.nilable(String)
|
|
56
|
+
repo_contents_path: T.nilable(String),
|
|
57
|
+
target_requirement: T.nilable(String)
|
|
54
58
|
).void
|
|
55
59
|
end
|
|
56
|
-
def initialize(
|
|
60
|
+
def initialize(
|
|
61
|
+
dependencies:,
|
|
62
|
+
dependency_files:,
|
|
63
|
+
credentials:,
|
|
64
|
+
index_urls: nil,
|
|
65
|
+
repo_contents_path: nil,
|
|
66
|
+
target_requirement: nil
|
|
67
|
+
)
|
|
57
68
|
@dependencies = dependencies
|
|
58
69
|
@dependency_files = dependency_files
|
|
59
70
|
@credentials = credentials
|
|
60
71
|
@index_urls = index_urls
|
|
61
72
|
@repo_contents_path = repo_contents_path
|
|
73
|
+
@target_requirement = target_requirement
|
|
62
74
|
@prepared_pyproject = T.let(nil, T.nilable(String))
|
|
63
75
|
@updated_lockfile_content = T.let(nil, T.nilable(String))
|
|
64
76
|
@pyproject = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
|
@@ -112,7 +124,9 @@ module Dependabot
|
|
|
112
124
|
# Use updated_lockfile_content which might raise if the lockfile doesn't change
|
|
113
125
|
new_content = updated_lockfile_content
|
|
114
126
|
|
|
115
|
-
|
|
127
|
+
if T.must(lockfile).content == new_content
|
|
128
|
+
raise DependencyFileContentNotChanged, "Expected lockfile to change!"
|
|
129
|
+
end
|
|
116
130
|
|
|
117
131
|
updated_files << updated_file(file: T.must(lockfile), content: new_content)
|
|
118
132
|
end
|
|
@@ -291,7 +305,14 @@ module Dependabot
|
|
|
291
305
|
# Strip extras from the package name for the uv lock command
|
|
292
306
|
# uv lock --upgrade-package expects the base package name without extras
|
|
293
307
|
base_dep_name = normalise(dep_name)
|
|
294
|
-
package_spec =
|
|
308
|
+
package_spec =
|
|
309
|
+
if target_requirement
|
|
310
|
+
"#{base_dep_name}#{target_requirement}"
|
|
311
|
+
elsif dep_version
|
|
312
|
+
"#{base_dep_name}==#{dep_version}"
|
|
313
|
+
else
|
|
314
|
+
base_dep_name
|
|
315
|
+
end
|
|
295
316
|
|
|
296
317
|
command = "pyenv exec uv lock --upgrade-package #{package_spec} #{options}"
|
|
297
318
|
fingerprint = "pyenv exec uv lock --upgrade-package <dependency_name> #{options_fingerprint}"
|
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
|
+
require "toml-rb"
|
|
5
6
|
|
|
7
|
+
require "dependabot/errors"
|
|
6
8
|
require "dependabot/package/release_cooldown_options"
|
|
9
|
+
require "dependabot/update_checkers/version_filters"
|
|
7
10
|
require "dependabot/uv/version"
|
|
11
|
+
require "dependabot/uv/file_updater/lock_file_error_handler"
|
|
12
|
+
require "dependabot/uv/file_updater/lock_file_updater"
|
|
13
|
+
require "dependabot/uv/file_parser/python_requirement_parser"
|
|
14
|
+
require "dependabot/uv/language_version_manager"
|
|
15
|
+
require "dependabot/uv/name_normaliser"
|
|
8
16
|
require "dependabot/uv/requirement"
|
|
9
17
|
require "dependabot/uv/update_checker"
|
|
10
18
|
require "dependabot/uv/update_checker/latest_version_finder"
|
|
@@ -42,33 +50,42 @@ module Dependabot
|
|
|
42
50
|
@security_advisories = security_advisories
|
|
43
51
|
@ignored_versions = ignored_versions
|
|
44
52
|
@update_cooldown = update_cooldown
|
|
53
|
+
@latest_resolvable_versions = T.let(
|
|
54
|
+
{},
|
|
55
|
+
T::Hash[String, T.nilable(Dependabot::Uv::Version)]
|
|
56
|
+
)
|
|
57
|
+
@resolved_versions = T.let(
|
|
58
|
+
{},
|
|
59
|
+
T::Hash[String, T.nilable(Dependabot::Uv::Version)]
|
|
60
|
+
)
|
|
61
|
+
@resolvable_versions = T.let({}, T::Hash[String, T::Boolean])
|
|
45
62
|
end
|
|
46
63
|
|
|
47
64
|
sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Uv::Version)) }
|
|
48
65
|
def latest_resolvable_version(requirement:)
|
|
49
66
|
return nil unless requirement
|
|
67
|
+
return @latest_resolvable_versions[requirement] if @latest_resolvable_versions.key?(requirement)
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# Get the version from the dependency if available
|
|
54
|
-
version_from_dependency = dependency.version && Uv::Version.new(dependency.version)
|
|
55
|
-
return version_from_dependency if version_from_dependency && req.satisfied_by?(version_from_dependency)
|
|
56
|
-
|
|
57
|
-
nil
|
|
69
|
+
@latest_resolvable_versions[requirement] = fetch_latest_resolvable_version(requirement)
|
|
58
70
|
end
|
|
59
71
|
|
|
60
|
-
sig { params(
|
|
61
|
-
def resolvable?(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
sig { params(version: Gem::Version).returns(T::Boolean) }
|
|
73
|
+
def resolvable?(version:)
|
|
74
|
+
target_version = Uv::Version.new(version.to_s)
|
|
75
|
+
key = target_version.to_s
|
|
76
|
+
return T.must(@resolvable_versions[key]) if @resolvable_versions.key?(key)
|
|
77
|
+
|
|
78
|
+
@resolvable_versions[key] = resolved_version("==#{key}", target_version)&.to_s == key
|
|
65
79
|
end
|
|
66
80
|
|
|
67
81
|
sig { returns(T.nilable(Dependabot::Uv::Version)) }
|
|
68
82
|
def lowest_resolvable_security_fix_version
|
|
69
83
|
# Delegate to LatestVersionFinder which handles security advisory filtering
|
|
70
|
-
fix_version = latest_version_finder.lowest_security_fix_version
|
|
71
|
-
|
|
84
|
+
fix_version = latest_version_finder.lowest_security_fix_version(
|
|
85
|
+
language_version: python_version
|
|
86
|
+
)
|
|
87
|
+
return nil unless fix_version
|
|
88
|
+
return nil unless resolvable?(version: fix_version)
|
|
72
89
|
|
|
73
90
|
# Return the fix version cast to Uv::Version
|
|
74
91
|
Uv::Version.new(fix_version.to_s)
|
|
@@ -97,6 +114,191 @@ module Dependabot
|
|
|
97
114
|
sig { returns(T.nilable(Dependabot::Package::ReleaseCooldownOptions)) }
|
|
98
115
|
attr_reader :update_cooldown
|
|
99
116
|
|
|
117
|
+
sig { params(requirement_string: String).returns(T.nilable(Dependabot::Uv::Version)) }
|
|
118
|
+
def fetch_latest_resolvable_version(requirement_string)
|
|
119
|
+
requirement = Uv::Requirement.new(requirement_string)
|
|
120
|
+
current_version = dependency.version && Uv::Version.new(dependency.version)
|
|
121
|
+
eligible_versions = eligible_versions(requirement, current_version)
|
|
122
|
+
return current_version if no_newer_eligible_version?(requirement, current_version, eligible_versions)
|
|
123
|
+
|
|
124
|
+
native_version = resolved_version(
|
|
125
|
+
policy_requirement(requirement_string, requirement, current_version, eligible_versions),
|
|
126
|
+
nil
|
|
127
|
+
)
|
|
128
|
+
if eligible_native_version?(requirement, native_version, eligible_versions) &&
|
|
129
|
+
resolvable?(version: T.must(native_version))
|
|
130
|
+
return native_version
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
current_version if current_version && requirement.satisfied_by?(current_version)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
sig do
|
|
137
|
+
params(
|
|
138
|
+
requirement: Dependabot::Uv::Requirement,
|
|
139
|
+
current_version: T.nilable(Dependabot::Uv::Version),
|
|
140
|
+
eligible_versions: T.nilable(T::Array[Dependabot::Uv::Version])
|
|
141
|
+
).returns(T::Boolean)
|
|
142
|
+
end
|
|
143
|
+
def no_newer_eligible_version?(requirement, current_version, eligible_versions)
|
|
144
|
+
return false unless current_version && eligible_versions
|
|
145
|
+
|
|
146
|
+
requirement.satisfied_by?(current_version) && eligible_versions.empty?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
sig do
|
|
150
|
+
params(
|
|
151
|
+
requirement: Dependabot::Uv::Requirement,
|
|
152
|
+
native_version: T.nilable(Dependabot::Uv::Version),
|
|
153
|
+
eligible_versions: T.nilable(T::Array[Dependabot::Uv::Version])
|
|
154
|
+
).returns(T::Boolean)
|
|
155
|
+
end
|
|
156
|
+
def eligible_native_version?(requirement, native_version, eligible_versions)
|
|
157
|
+
return false unless native_version && requirement.satisfied_by?(native_version)
|
|
158
|
+
|
|
159
|
+
!eligible_versions || eligible_versions.include?(native_version)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
sig do
|
|
163
|
+
params(
|
|
164
|
+
requirement: Dependabot::Uv::Requirement,
|
|
165
|
+
current_version: T.nilable(Dependabot::Uv::Version)
|
|
166
|
+
).returns(T.nilable(T::Array[Dependabot::Uv::Version]))
|
|
167
|
+
end
|
|
168
|
+
def eligible_versions(requirement, current_version)
|
|
169
|
+
releases = latest_version_finder.eligible_releases(
|
|
170
|
+
language_version: python_version
|
|
171
|
+
)
|
|
172
|
+
return unless releases
|
|
173
|
+
|
|
174
|
+
releases = Dependabot::UpdateCheckers::VersionFilters
|
|
175
|
+
.filter_vulnerable_versions(releases, security_advisories)
|
|
176
|
+
releases
|
|
177
|
+
.map { |release| Uv::Version.new(release.version.to_s) }
|
|
178
|
+
.select { |version| requirement.satisfied_by?(version) && (!current_version || version > current_version) }
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
sig do
|
|
182
|
+
params(
|
|
183
|
+
requirement_string: String,
|
|
184
|
+
requirement: Dependabot::Uv::Requirement,
|
|
185
|
+
current_version: T.nilable(Dependabot::Uv::Version),
|
|
186
|
+
eligible_versions: T.nilable(T::Array[Dependabot::Uv::Version])
|
|
187
|
+
).returns(String)
|
|
188
|
+
end
|
|
189
|
+
def policy_requirement(requirement_string, requirement, current_version, eligible_versions)
|
|
190
|
+
releases = latest_version_finder.available_versions
|
|
191
|
+
return requirement_string unless releases && eligible_versions
|
|
192
|
+
|
|
193
|
+
excluded_versions = releases
|
|
194
|
+
.map { |release| Uv::Version.new(release.version.to_s) }
|
|
195
|
+
.select do |version|
|
|
196
|
+
requirement.satisfied_by?(version) &&
|
|
197
|
+
(!current_version || version > current_version) &&
|
|
198
|
+
!eligible_versions.include?(version)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
([requirement_string] + excluded_versions.map { |version| "!=#{version}" }).join(",")
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
sig do
|
|
205
|
+
params(
|
|
206
|
+
requirement_string: String,
|
|
207
|
+
target_version: T.nilable(Dependabot::Uv::Version)
|
|
208
|
+
)
|
|
209
|
+
.returns(T.nilable(Dependabot::Uv::Version))
|
|
210
|
+
end
|
|
211
|
+
def resolved_version(requirement_string, target_version)
|
|
212
|
+
return @resolved_versions[requirement_string] if @resolved_versions.key?(requirement_string)
|
|
213
|
+
|
|
214
|
+
@resolved_versions[requirement_string] = run_resolution(requirement_string, target_version)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
sig do
|
|
218
|
+
params(
|
|
219
|
+
requirement_string: String,
|
|
220
|
+
target_version: T.nilable(Dependabot::Uv::Version)
|
|
221
|
+
)
|
|
222
|
+
.returns(T.nilable(Dependabot::Uv::Version))
|
|
223
|
+
end
|
|
224
|
+
def run_resolution(requirement_string, target_version)
|
|
225
|
+
updated_dependency = Dependabot::Dependency.new(
|
|
226
|
+
name: dependency.name,
|
|
227
|
+
version: nil,
|
|
228
|
+
previous_version: dependency.version,
|
|
229
|
+
requirements: [],
|
|
230
|
+
previous_requirements: [],
|
|
231
|
+
package_manager: dependency.package_manager
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
updated_files = FileUpdater::LockFileUpdater.new(
|
|
235
|
+
dependencies: [updated_dependency],
|
|
236
|
+
dependency_files: dependency_files,
|
|
237
|
+
credentials: credentials,
|
|
238
|
+
repo_contents_path: repo_contents_path,
|
|
239
|
+
target_requirement: requirement_string
|
|
240
|
+
).updated_dependency_files
|
|
241
|
+
|
|
242
|
+
lockfile = updated_files.find { |file| file.name == "uv.lock" } || original_lockfile
|
|
243
|
+
resolved_locked_version(T.must(lockfile), target_version)
|
|
244
|
+
rescue Dependabot::DependencyFileContentNotChanged
|
|
245
|
+
resolved_locked_version(T.must(original_lockfile), target_version)
|
|
246
|
+
rescue Dependabot::UpdateNotPossible
|
|
247
|
+
nil
|
|
248
|
+
rescue Dependabot::DependencyFileNotResolvable => e
|
|
249
|
+
conflict = e.message.match?(FileUpdater::LockFileErrorHandler::UV_UNRESOLVABLE_REGEX) ||
|
|
250
|
+
e.message.include?(FileUpdater::LockFileErrorHandler::RESOLUTION_IMPOSSIBLE_ERROR)
|
|
251
|
+
raise unless conflict
|
|
252
|
+
|
|
253
|
+
nil
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
sig do
|
|
257
|
+
params(
|
|
258
|
+
lockfile: Dependabot::DependencyFile,
|
|
259
|
+
target_version: T.nilable(Dependabot::Uv::Version)
|
|
260
|
+
).returns(T.nilable(Dependabot::Uv::Version))
|
|
261
|
+
end
|
|
262
|
+
def resolved_locked_version(lockfile, target_version)
|
|
263
|
+
original_versions = locked_versions(T.must(original_lockfile))
|
|
264
|
+
updated_versions = locked_versions(lockfile)
|
|
265
|
+
current_version = dependency.version && Uv::Version.new(dependency.version)
|
|
266
|
+
return nil unless current_version && original_versions.min == current_version
|
|
267
|
+
return current_version if updated_versions.include?(current_version)
|
|
268
|
+
|
|
269
|
+
updated_version = updated_versions.min
|
|
270
|
+
return target_version if target_version && updated_version == target_version
|
|
271
|
+
return nil if target_version
|
|
272
|
+
|
|
273
|
+
updated_version
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
sig do
|
|
277
|
+
params(lockfile: Dependabot::DependencyFile)
|
|
278
|
+
.returns(T::Array[Dependabot::Uv::Version])
|
|
279
|
+
end
|
|
280
|
+
def locked_versions(lockfile)
|
|
281
|
+
parsed = T.cast(TomlRB.parse(T.must(lockfile.content)), T::Hash[String, Object])
|
|
282
|
+
packages = T.cast(parsed["package"], T.nilable(T::Array[T::Hash[String, Object]])) || []
|
|
283
|
+
dependency_name = NameNormaliser.normalise(dependency.name)
|
|
284
|
+
versions = T.let([], T::Array[Dependabot::Uv::Version])
|
|
285
|
+
packages.each do |package|
|
|
286
|
+
package_name = T.cast(package["name"], T.nilable(String))
|
|
287
|
+
package_version = T.cast(package["version"], T.nilable(String))
|
|
288
|
+
next unless package_name && NameNormaliser.normalise(package_name) == dependency_name
|
|
289
|
+
next unless Uv::Version.correct?(package_version)
|
|
290
|
+
|
|
291
|
+
versions << Uv::Version.new(package_version)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
versions
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
298
|
+
def original_lockfile
|
|
299
|
+
dependency_files.find { |file| file.name == "uv.lock" }
|
|
300
|
+
end
|
|
301
|
+
|
|
100
302
|
sig { returns(LatestVersionFinder) }
|
|
101
303
|
def latest_version_finder
|
|
102
304
|
@latest_version_finder ||= T.let(
|
|
@@ -112,6 +314,18 @@ module Dependabot
|
|
|
112
314
|
T.nilable(LatestVersionFinder)
|
|
113
315
|
)
|
|
114
316
|
end
|
|
317
|
+
|
|
318
|
+
sig { returns(String) }
|
|
319
|
+
def python_version
|
|
320
|
+
@python_version ||= T.let(
|
|
321
|
+
LanguageVersionManager.new(
|
|
322
|
+
python_requirement_parser: FileParser::PythonRequirementParser.new(
|
|
323
|
+
dependency_files: dependency_files
|
|
324
|
+
)
|
|
325
|
+
).python_version,
|
|
326
|
+
T.nilable(String)
|
|
327
|
+
)
|
|
328
|
+
end
|
|
115
329
|
end
|
|
116
330
|
end
|
|
117
331
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-uv
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.394.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.394.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.394.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: dependabot-python
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.394.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.
|
|
39
|
+
version: 0.394.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: debug
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -183,14 +183,14 @@ dependencies:
|
|
|
183
183
|
requirements:
|
|
184
184
|
- - "~>"
|
|
185
185
|
- !ruby/object:Gem::Version
|
|
186
|
-
version: '
|
|
186
|
+
version: '1.1'
|
|
187
187
|
type: :development
|
|
188
188
|
prerelease: false
|
|
189
189
|
version_requirements: !ruby/object:Gem::Requirement
|
|
190
190
|
requirements:
|
|
191
191
|
- - "~>"
|
|
192
192
|
- !ruby/object:Gem::Version
|
|
193
|
-
version: '
|
|
193
|
+
version: '1.1'
|
|
194
194
|
- !ruby/object:Gem::Dependency
|
|
195
195
|
name: turbo_tests
|
|
196
196
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -301,7 +301,7 @@ licenses:
|
|
|
301
301
|
- MIT
|
|
302
302
|
metadata:
|
|
303
303
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
304
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
304
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.394.0
|
|
305
305
|
rdoc_options: []
|
|
306
306
|
require_paths:
|
|
307
307
|
- lib
|