dependabot-uv 0.301.1 → 0.303.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.rb +1 -1
- data/lib/dependabot/uv/file_updater/lock_file_updater.rb +53 -122
- data/lib/dependabot/uv/file_updater/requirement_file_updater.rb +30 -7
- data/lib/dependabot/uv/language.rb +36 -34
- data/lib/dependabot/uv/language_version_manager.rb +44 -20
- data/lib/dependabot/uv/requirement.rb +34 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42bfabcf245ebc41ef81303c9de0c8271a0a0073af315d23b182b64901646b9d
|
4
|
+
data.tar.gz: 7e6a04dabc0d50c374a0e118d08b705aadca94e5952eba891b14623c99a76776
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 246aebc2e0a8d0402e8edb1754df23a4f5428156b8ebb0c0748ba55f45ae30f1056aa1ff89d17af9b6d7b16d158c97aa1c39c86d0f49e598683baaffa53f3513
|
7
|
+
data.tar.gz: e5eaa3c52329795b579f7041d555ebcbec0377b37e6e898e122bacffad4c3432c5bc6319acc0305dfd1dcc6a900b7d1baa576bf25e690c2178e044b342232d98
|
data/helpers/requirements.txt
CHANGED
@@ -50,7 +50,7 @@ module Dependabot
|
|
50
50
|
# the user-specified range of versions, not the version Dependabot chose to run.
|
51
51
|
python_requirement_parser = FileParser::PythonRequirementParser.new(dependency_files: files)
|
52
52
|
language_version_manager = LanguageVersionManager.new(python_requirement_parser: python_requirement_parser)
|
53
|
-
Dependabot.logger.info("Dependabot is using Python version '#{language_version_manager.
|
53
|
+
Dependabot.logger.info("Dependabot is using Python version '#{language_version_manager.python_version}'.")
|
54
54
|
{
|
55
55
|
languages: {
|
56
56
|
python: {
|
@@ -19,6 +19,8 @@ module Dependabot
|
|
19
19
|
class LockFileUpdater
|
20
20
|
require_relative "pyproject_preparer"
|
21
21
|
|
22
|
+
REQUIRED_FILES = %w(pyproject.toml uv.lock).freeze # At least one of these files should be present
|
23
|
+
|
22
24
|
attr_reader :dependencies
|
23
25
|
attr_reader :dependency_files
|
24
26
|
attr_reader :credentials
|
@@ -43,6 +45,8 @@ module Dependabot
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def fetch_updated_dependency_files
|
48
|
+
return [] unless create_or_update_lock_file?
|
49
|
+
|
46
50
|
updated_files = []
|
47
51
|
|
48
52
|
if file_changed?(pyproject)
|
@@ -104,48 +108,36 @@ module Dependabot
|
|
104
108
|
original_requires_python = original_content
|
105
109
|
.match(/requires-python\s*=\s*["']([^"']+)["']/)&.captures&.first
|
106
110
|
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
result = original_content.sub(dependency_section_pattern) do
|
123
|
-
section_start = Regexp.last_match(1)
|
124
|
-
version_line = "version = \"#{dependency.version}\""
|
125
|
-
section_end = Regexp.last_match(3)
|
126
|
-
next_section_or_end = Regexp.last_match(4)
|
127
|
-
|
128
|
-
"#{section_start}#{version_line}#{section_end}#{next_section_or_end}"
|
129
|
-
end
|
130
|
-
|
131
|
-
# If the content didn't change and we expect it to, something went wrong
|
132
|
-
if result == original_content
|
133
|
-
Dependabot.logger.warn("Package section not found for #{dependency.name}, falling back to raw update")
|
134
|
-
result = new_lockfile
|
135
|
-
end
|
136
|
-
|
137
|
-
# Restore the original requires-python if it exists
|
138
|
-
if original_requires_python
|
139
|
-
result = result.gsub(/requires-python\s*=\s*["'][^"']+["']/,
|
140
|
-
"requires-python = \"#{original_requires_python}\"")
|
141
|
-
end
|
142
|
-
|
143
|
-
result
|
111
|
+
# Store the original Python version requirement for later use
|
112
|
+
@original_python_version = original_requires_python
|
113
|
+
|
114
|
+
new_lockfile = updated_lockfile_content_for(prepared_pyproject)
|
115
|
+
|
116
|
+
# Normalize line endings to ensure proper comparison
|
117
|
+
new_lockfile = normalize_line_endings(new_lockfile, original_content)
|
118
|
+
|
119
|
+
result = new_lockfile
|
120
|
+
|
121
|
+
# Restore the original requires-python if it exists
|
122
|
+
if original_requires_python
|
123
|
+
result = result.gsub(/requires-python\s*=\s*["'][^"']+["']/,
|
124
|
+
"requires-python = \"#{original_requires_python}\"")
|
144
125
|
end
|
126
|
+
|
127
|
+
result
|
145
128
|
end
|
146
129
|
end
|
147
130
|
|
148
|
-
# Helper method to
|
131
|
+
# Helper method to normalize line endings between two strings
|
132
|
+
def normalize_line_endings(content, reference)
|
133
|
+
# Check if reference has escaped newlines like "\n" +
|
134
|
+
if reference.include?("\\n")
|
135
|
+
content.gsub("\n", "\\n")
|
136
|
+
else
|
137
|
+
content
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
149
141
|
def with_original_python_version(original_requires_python)
|
150
142
|
if original_requires_python
|
151
143
|
original_python_version = @original_python_version
|
@@ -164,7 +156,6 @@ module Dependabot
|
|
164
156
|
content = updated_pyproject_content
|
165
157
|
content = sanitize(content)
|
166
158
|
content = freeze_other_dependencies(content)
|
167
|
-
content = update_python_requirement(content)
|
168
159
|
content
|
169
160
|
end
|
170
161
|
end
|
@@ -175,12 +166,6 @@ module Dependabot
|
|
175
166
|
.freeze_top_level_dependencies_except(dependencies)
|
176
167
|
end
|
177
168
|
|
178
|
-
def update_python_requirement(pyproject_content)
|
179
|
-
PyprojectPreparer
|
180
|
-
.new(pyproject_content: pyproject_content)
|
181
|
-
.update_python_requirement(language_version_manager.python_version)
|
182
|
-
end
|
183
|
-
|
184
169
|
def sanitize(pyproject_content)
|
185
170
|
PyprojectPreparer
|
186
171
|
.new(pyproject_content: pyproject_content)
|
@@ -192,14 +177,8 @@ module Dependabot
|
|
192
177
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
193
178
|
write_temporary_dependency_files(pyproject_content)
|
194
179
|
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
# Determine the Python version to use after installation
|
199
|
-
python_version = determine_python_version
|
200
|
-
|
201
|
-
# Now write the .python-version file with a version we know is installed
|
202
|
-
File.write(".python-version", python_version)
|
180
|
+
# Set up Python environment using LanguageVersionManager
|
181
|
+
setup_python_environment
|
203
182
|
|
204
183
|
run_update_command
|
205
184
|
|
@@ -209,6 +188,7 @@ module Dependabot
|
|
209
188
|
end
|
210
189
|
|
211
190
|
def run_update_command
|
191
|
+
# Use pyenv exec to ensure we're using the correct Python environment
|
212
192
|
command = "pyenv exec uv lock --upgrade-package #{dependency.name}"
|
213
193
|
fingerprint = "pyenv exec uv lock --upgrade-package <dependency_name>"
|
214
194
|
|
@@ -216,6 +196,7 @@ module Dependabot
|
|
216
196
|
end
|
217
197
|
|
218
198
|
def run_command(command, fingerprint: nil)
|
199
|
+
Dependabot.logger.info("Running command: #{command}")
|
219
200
|
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
220
201
|
end
|
221
202
|
|
@@ -226,82 +207,28 @@ module Dependabot
|
|
226
207
|
File.write(path, file.content)
|
227
208
|
end
|
228
209
|
|
229
|
-
# Only write the .python-version file after the language version manager has
|
230
|
-
# installed the required Python version to ensure it's available
|
231
210
|
# Overwrite the pyproject with updated content
|
232
211
|
File.write("pyproject.toml", pyproject_content)
|
233
212
|
end
|
234
213
|
|
235
|
-
def
|
236
|
-
#
|
237
|
-
|
238
|
-
begin
|
239
|
-
available_versions = SharedHelpers.run_shell_command("pyenv versions --bare")
|
240
|
-
.split("\n")
|
241
|
-
.map(&:strip)
|
242
|
-
.reject(&:empty?)
|
243
|
-
rescue StandardError => e
|
244
|
-
Dependabot.logger.warn("Error checking available Python versions: #{e}")
|
245
|
-
end
|
246
|
-
|
247
|
-
# Try to find the closest match for our priority order
|
248
|
-
preferred_version = find_preferred_version(available_versions)
|
214
|
+
def setup_python_environment
|
215
|
+
# Use LanguageVersionManager to determine and install the appropriate Python version
|
216
|
+
Dependabot.logger.info("Setting up Python environment using LanguageVersionManager")
|
249
217
|
|
250
|
-
|
251
|
-
#
|
252
|
-
|
253
|
-
else
|
254
|
-
# If all else fails, use "system" which should work with whatever Python is available
|
255
|
-
"system"
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
def find_preferred_version(available_versions)
|
260
|
-
return nil unless available_versions&.any?
|
261
|
-
|
262
|
-
# Try each strategy in order of preference
|
263
|
-
try_version_from_file(available_versions) ||
|
264
|
-
try_version_from_requires_python(available_versions) ||
|
265
|
-
try_highest_python3_version(available_versions)
|
266
|
-
end
|
267
|
-
|
268
|
-
def try_version_from_file(available_versions)
|
269
|
-
python_version_file = dependency_files.find { |f| f.name == ".python-version" }
|
270
|
-
return nil unless python_version_file && !python_version_file.content.strip.empty?
|
271
|
-
|
272
|
-
requested_version = python_version_file.content.strip
|
273
|
-
return requested_version if version_available?(available_versions, requested_version)
|
274
|
-
|
275
|
-
Dependabot.logger.info("Python version #{requested_version} from .python-version not available")
|
276
|
-
nil
|
277
|
-
end
|
278
|
-
|
279
|
-
def try_version_from_requires_python(available_versions)
|
280
|
-
return nil unless @original_python_version
|
281
|
-
|
282
|
-
version_match = @original_python_version.match(/(\d+\.\d+)/)
|
283
|
-
return nil unless version_match
|
284
|
-
|
285
|
-
requested_version = version_match[1]
|
286
|
-
return requested_version if version_available?(available_versions, requested_version)
|
287
|
-
|
288
|
-
Dependabot.logger.info("Python version #{requested_version} from requires-python not available")
|
289
|
-
nil
|
290
|
-
end
|
291
|
-
|
292
|
-
def try_highest_python3_version(available_versions)
|
293
|
-
python3_versions = available_versions
|
294
|
-
.select { |v| v.match(/^3\.\d+/) }
|
295
|
-
.sort_by { |v| Gem::Version.new(v.match(/^(\d+\.\d+)/)[1]) }
|
296
|
-
.reverse
|
218
|
+
begin
|
219
|
+
# Install the required Python version
|
220
|
+
language_version_manager.install_required_python
|
297
221
|
|
298
|
-
|
299
|
-
|
222
|
+
# Set the local Python version
|
223
|
+
python_version = language_version_manager.python_version
|
224
|
+
Dependabot.logger.info("Setting Python version to #{python_version}")
|
225
|
+
SharedHelpers.run_shell_command("pyenv local #{python_version}")
|
300
226
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
227
|
+
# We don't need to install uv as it should be available in the Docker environment
|
228
|
+
Dependabot.logger.info("Using pre-installed uv package")
|
229
|
+
rescue StandardError => e
|
230
|
+
Dependabot.logger.warn("Error setting up Python environment: #{e.message}")
|
231
|
+
Dependabot.logger.info("Falling back to system Python")
|
305
232
|
end
|
306
233
|
end
|
307
234
|
|
@@ -386,6 +313,10 @@ module Dependabot
|
|
386
313
|
def uv_lock
|
387
314
|
dependency_files.find { |f| f.name == "uv.lock" }
|
388
315
|
end
|
316
|
+
|
317
|
+
def create_or_update_lock_file?
|
318
|
+
dependency.requirements.select { _1[:file].end_with?(*REQUIRED_FILES) }.any?
|
319
|
+
end
|
389
320
|
end
|
390
321
|
end
|
391
322
|
end
|
@@ -1,69 +1,92 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/uv/requirement_parser"
|
5
5
|
require "dependabot/uv/file_updater"
|
6
6
|
require "dependabot/shared_helpers"
|
7
7
|
require "dependabot/uv/native_helpers"
|
8
|
+
require "sorbet-runtime"
|
8
9
|
|
9
10
|
module Dependabot
|
10
11
|
module Uv
|
11
12
|
class FileUpdater
|
12
13
|
class RequirementFileUpdater
|
14
|
+
extend T::Sig
|
15
|
+
|
13
16
|
require_relative "requirement_replacer"
|
14
17
|
|
18
|
+
sig { returns(T::Array[Dependency]) }
|
15
19
|
attr_reader :dependencies
|
20
|
+
|
21
|
+
sig { returns(T::Array[DependencyFile]) }
|
16
22
|
attr_reader :dependency_files
|
23
|
+
|
24
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
17
25
|
attr_reader :credentials
|
18
26
|
|
27
|
+
sig do
|
28
|
+
params(
|
29
|
+
dependencies: T::Array[Dependency],
|
30
|
+
dependency_files: T::Array[DependencyFile],
|
31
|
+
credentials: T::Array[Dependabot::Credential],
|
32
|
+
index_urls: T.nilable(T::Array[String])
|
33
|
+
).void
|
34
|
+
end
|
19
35
|
def initialize(dependencies:, dependency_files:, credentials:, index_urls: nil)
|
20
36
|
@dependencies = dependencies
|
21
37
|
@dependency_files = dependency_files
|
22
38
|
@credentials = credentials
|
23
39
|
@index_urls = index_urls
|
40
|
+
@updated_dependency_files = T.let(nil, T.nilable(T::Array[DependencyFile]))
|
24
41
|
end
|
25
42
|
|
43
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
26
44
|
def updated_dependency_files
|
27
45
|
@updated_dependency_files ||= fetch_updated_dependency_files
|
28
46
|
end
|
29
47
|
|
30
48
|
private
|
31
49
|
|
50
|
+
sig { returns(T.nilable(Dependency)) }
|
32
51
|
def dependency
|
33
52
|
# For now, we'll only ever be updating a single dependency
|
34
53
|
dependencies.first
|
35
54
|
end
|
36
55
|
|
56
|
+
sig { returns(T::Array[DependencyFile]) }
|
37
57
|
def fetch_updated_dependency_files
|
38
|
-
|
58
|
+
previous_requirements = dependency&.previous_requirements || []
|
59
|
+
reqs = T.must(dependency).requirements.zip(previous_requirements)
|
39
60
|
|
40
61
|
reqs.filter_map do |(new_req, old_req)|
|
41
62
|
next if new_req == old_req
|
42
63
|
|
43
64
|
file = get_original_file(new_req.fetch(:file)).dup
|
44
65
|
updated_content =
|
45
|
-
updated_requirement_or_setup_file_content(new_req, old_req)
|
46
|
-
next if updated_content == file
|
66
|
+
updated_requirement_or_setup_file_content(new_req, T.must(old_req))
|
67
|
+
next if updated_content == file&.content
|
47
68
|
|
48
|
-
file
|
69
|
+
file&.content = updated_content
|
49
70
|
file
|
50
71
|
end
|
51
72
|
end
|
52
73
|
|
74
|
+
sig { params(new_req: T::Hash[Symbol, T.untyped], old_req: T::Hash[Symbol, T.untyped]).returns(String) }
|
53
75
|
def updated_requirement_or_setup_file_content(new_req, old_req)
|
54
76
|
original_file = get_original_file(new_req.fetch(:file))
|
55
77
|
raise "Could not find a dependency file for #{new_req}" unless original_file
|
56
78
|
|
57
79
|
RequirementReplacer.new(
|
58
80
|
content: original_file.content,
|
59
|
-
dependency_name: dependency
|
81
|
+
dependency_name: dependency&.name,
|
60
82
|
old_requirement: old_req.fetch(:requirement),
|
61
83
|
new_requirement: new_req.fetch(:requirement),
|
62
|
-
new_hash_version: dependency
|
84
|
+
new_hash_version: dependency&.version,
|
63
85
|
index_urls: @index_urls
|
64
86
|
).updated_content
|
65
87
|
end
|
66
88
|
|
89
|
+
sig { params(filename: String).returns(T.nilable(DependencyFile)) }
|
67
90
|
def get_original_file(filename)
|
68
91
|
dependency_files.find { |f| f.name == filename }
|
69
92
|
end
|
@@ -11,28 +11,43 @@ module Dependabot
|
|
11
11
|
|
12
12
|
class Language < Dependabot::Ecosystem::VersionManager
|
13
13
|
extend T::Sig
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# This list must match the versions specified at the top of `python/Dockerfile`
|
15
|
+
# ARG PY_3_13=3.13.2
|
16
|
+
PRE_INSTALLED_PYTHON_VERSIONS_RAW = %w(
|
17
|
+
3.13.2
|
18
|
+
3.12.9
|
19
|
+
3.11.11
|
20
|
+
3.10.16
|
21
|
+
3.9.21
|
22
|
+
).freeze
|
21
23
|
|
22
|
-
|
24
|
+
PRE_INSTALLED_PYTHON_VERSIONS = T.let(PRE_INSTALLED_PYTHON_VERSIONS_RAW.map do |v|
|
25
|
+
Version.new(v)
|
26
|
+
end.sort, T::Array[Version])
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Version
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
PRE_INSTALLED_VERSIONS_MAP = T.let(
|
29
|
+
PRE_INSTALLED_PYTHON_VERSIONS.to_h do |v|
|
30
|
+
[Version.new(T.must(v.segments[0..1]).join(".")), v]
|
31
|
+
end,
|
32
|
+
T::Hash[Version, Version]
|
33
|
+
)
|
34
|
+
|
35
|
+
PRE_INSTALLED_HIGHEST_VERSION = T.let(T.must(PRE_INSTALLED_PYTHON_VERSIONS.max), Version)
|
36
|
+
|
37
|
+
SUPPORTED_VERSIONS = T.let(
|
38
|
+
PRE_INSTALLED_PYTHON_VERSIONS.map do |v|
|
39
|
+
Version.new(T.must(v.segments[0..1]&.join(".")))
|
40
|
+
end,
|
41
|
+
T::Array[Version]
|
42
|
+
)
|
43
|
+
|
44
|
+
NON_SUPPORTED_HIGHEST_VERSION = "3.8"
|
45
|
+
|
46
|
+
DEPRECATED_VERSIONS = T.let([Version.new(NON_SUPPORTED_HIGHEST_VERSION)].freeze, T::Array[Dependabot::Version])
|
32
47
|
|
33
48
|
sig do
|
34
49
|
params(
|
35
|
-
detected_version: String,
|
50
|
+
detected_version: T.nilable(String),
|
36
51
|
raw_version: T.nilable(String),
|
37
52
|
requirement: T.nilable(Requirement)
|
38
53
|
).void
|
@@ -40,7 +55,7 @@ module Dependabot
|
|
40
55
|
def initialize(detected_version:, raw_version: nil, requirement: nil)
|
41
56
|
super(
|
42
57
|
name: LANGUAGE,
|
43
|
-
detected_version: major_minor_version(detected_version),
|
58
|
+
detected_version: detected_version ? major_minor_version(detected_version) : nil,
|
44
59
|
version: raw_version ? Version.new(raw_version) : nil,
|
45
60
|
deprecated_versions: DEPRECATED_VERSIONS,
|
46
61
|
supported_versions: SUPPORTED_VERSIONS,
|
@@ -48,25 +63,12 @@ module Dependabot
|
|
48
63
|
)
|
49
64
|
end
|
50
65
|
|
51
|
-
sig { override.returns(T::Boolean) }
|
52
|
-
def deprecated?
|
53
|
-
return false unless detected_version
|
54
|
-
return false if unsupported?
|
55
|
-
|
56
|
-
deprecated_versions.include?(detected_version)
|
57
|
-
end
|
58
|
-
|
59
|
-
sig { override.returns(T::Boolean) }
|
60
|
-
def unsupported?
|
61
|
-
return false unless detected_version
|
62
|
-
|
63
|
-
supported_versions.all? { |supported| supported > detected_version }
|
64
|
-
end
|
65
|
-
|
66
66
|
private
|
67
67
|
|
68
|
-
sig { params(version: String).returns(
|
68
|
+
sig { params(version: String).returns(T.nilable(Version)) }
|
69
69
|
def major_minor_version(version)
|
70
|
+
return nil if version.empty?
|
71
|
+
|
70
72
|
major_minor = T.let(T.must(Version.new(version).segments[0..1]&.join(".")), String)
|
71
73
|
|
72
74
|
Version.new(major_minor)
|
@@ -9,14 +9,6 @@ module Dependabot
|
|
9
9
|
module Uv
|
10
10
|
class LanguageVersionManager
|
11
11
|
extend T::Sig
|
12
|
-
# This list must match the versions specified at the top of `python/Dockerfile`
|
13
|
-
PRE_INSTALLED_PYTHON_VERSIONS = %w(
|
14
|
-
3.13.2
|
15
|
-
3.12.9
|
16
|
-
3.11.11
|
17
|
-
3.10.16
|
18
|
-
3.9.21
|
19
|
-
).freeze
|
20
12
|
|
21
13
|
sig { params(python_requirement_parser: T.untyped).void }
|
22
14
|
def initialize(python_requirement_parser:)
|
@@ -44,7 +36,7 @@ module Dependabot
|
|
44
36
|
|
45
37
|
sig { returns(T.untyped) }
|
46
38
|
def python_major_minor
|
47
|
-
@python_major_minor ||= T.let(T.must(
|
39
|
+
@python_major_minor ||= T.let(T.must(Version.new(python_version).segments[0..1]).join("."), T.untyped)
|
48
40
|
end
|
49
41
|
|
50
42
|
sig { returns(String) }
|
@@ -62,7 +54,34 @@ module Dependabot
|
|
62
54
|
user_specified_python_version
|
63
55
|
end
|
64
56
|
else
|
65
|
-
python_version_matching_imputed_requirements ||
|
57
|
+
python_version_matching_imputed_requirements || Language::PRE_INSTALLED_HIGHEST_VERSION.to_s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sig { params(requirement_string: T.nilable(String)).returns(T.nilable(String)) }
|
62
|
+
def normalize_python_exact_version(requirement_string)
|
63
|
+
return requirement_string if requirement_string.nil? || requirement_string.strip.empty?
|
64
|
+
|
65
|
+
requirement_string = requirement_string.strip
|
66
|
+
|
67
|
+
# If the requirement already has a wildcard, return nil
|
68
|
+
return nil if requirement_string == "*"
|
69
|
+
|
70
|
+
# If the requirement is not an exact version such as not X.Y.Z, =X.Y.Z, ==X.Y.Z, ===X.Y.Z
|
71
|
+
# then return the requirement as is
|
72
|
+
return requirement_string unless requirement_string.match?(/^=?={0,2}\s*\d+\.\d+(\.\d+)?(-[a-z0-9.-]+)?$/i)
|
73
|
+
|
74
|
+
parts = requirement_string.gsub(/^=+/, "").split(".")
|
75
|
+
|
76
|
+
case parts.length
|
77
|
+
when 1 # Only major version (X)
|
78
|
+
">= #{parts[0]}.0.0 < #{parts[0].to_i + 1}.0.0" # Ensure only major version range
|
79
|
+
when 2 # Major.Minor (X.Y)
|
80
|
+
">= #{parts[0]}.#{parts[1]}.0 < #{parts[0].to_i}.#{parts[1].to_i + 1}.0" # Ensure only minor version range
|
81
|
+
when 3 # Major.Minor.Patch (X.Y.Z)
|
82
|
+
">= #{parts[0]}.#{parts[1]}.0 < #{parts[0].to_i}.#{parts[1].to_i + 1}.0" # Convert to >= X.Y.0
|
83
|
+
else
|
84
|
+
requirement_string
|
66
85
|
end
|
67
86
|
end
|
68
87
|
|
@@ -72,15 +91,21 @@ module Dependabot
|
|
72
91
|
|
73
92
|
# If the requirement string isn't already a range (eg ">3.10"), coerce it to "major.minor.*".
|
74
93
|
# The patch version is ignored because a non-matching patch version is unlikely to affect resolution.
|
75
|
-
requirement_string = requirement_string.gsub(/\.\d+$/, ".*") if
|
94
|
+
requirement_string = requirement_string.gsub(/\.\d+$/, ".*") if /^\d/.match?(requirement_string)
|
95
|
+
|
96
|
+
requirement_string = normalize_python_exact_version(requirement_string)
|
97
|
+
|
98
|
+
if requirement_string.nil? || requirement_string.strip.empty?
|
99
|
+
return Language::PRE_INSTALLED_HIGHEST_VERSION.to_s
|
100
|
+
end
|
76
101
|
|
77
102
|
# Try to match one of our pre-installed Python versions
|
78
|
-
requirement = T.must(
|
79
|
-
version = PRE_INSTALLED_PYTHON_VERSIONS.find { |v| requirement.satisfied_by?(
|
80
|
-
return version if version
|
103
|
+
requirement = T.must(Requirement.requirements_array(requirement_string).first)
|
104
|
+
version = Language::PRE_INSTALLED_PYTHON_VERSIONS.find { |v| requirement.satisfied_by?(v) }
|
105
|
+
return version.to_s if version
|
81
106
|
|
82
|
-
# Otherwise we have to raise
|
83
|
-
supported_versions =
|
107
|
+
# Otherwise we have to raise an error
|
108
|
+
supported_versions = Language::SUPPORTED_VERSIONS.map { |v| "#{v}.*" }.join(", ")
|
84
109
|
raise ToolVersionNotSupported.new("Python", python_requirement_string, supported_versions)
|
85
110
|
end
|
86
111
|
|
@@ -93,21 +118,20 @@ module Dependabot
|
|
93
118
|
def python_version_matching_imputed_requirements
|
94
119
|
compiled_file_python_requirement_markers =
|
95
120
|
@python_requirement_parser.imputed_requirements.map do |r|
|
96
|
-
|
121
|
+
Requirement.new(r)
|
97
122
|
end
|
98
123
|
python_version_matching(compiled_file_python_requirement_markers)
|
99
124
|
end
|
100
125
|
|
101
126
|
sig { params(requirements: T.untyped).returns(T.nilable(String)) }
|
102
127
|
def python_version_matching(requirements)
|
103
|
-
PRE_INSTALLED_PYTHON_VERSIONS.find do |
|
104
|
-
version = Uv::Version.new(version_string)
|
128
|
+
Language::PRE_INSTALLED_PYTHON_VERSIONS.find do |version|
|
105
129
|
requirements.all? do |req|
|
106
130
|
next req.any? { |r| r.satisfied_by?(version) } if req.is_a?(Array)
|
107
131
|
|
108
132
|
req.satisfied_by?(version)
|
109
133
|
end
|
110
|
-
end
|
134
|
+
end.to_s
|
111
135
|
end
|
112
136
|
end
|
113
137
|
end
|
@@ -93,7 +93,7 @@ module Dependabot
|
|
93
93
|
private
|
94
94
|
|
95
95
|
def convert_python_constraint_to_ruby_constraint(req_string)
|
96
|
-
return nil if req_string.nil?
|
96
|
+
return nil if req_string.nil? || req_string.strip.empty?
|
97
97
|
return nil if req_string == "*"
|
98
98
|
|
99
99
|
req_string = req_string.gsub("~=", "~>")
|
@@ -101,6 +101,8 @@ module Dependabot
|
|
101
101
|
|
102
102
|
if req_string.match?(/~[^>]/) then convert_tilde_req(req_string)
|
103
103
|
elsif req_string.start_with?("^") then convert_caret_req(req_string)
|
104
|
+
elsif req_string.match?(/^=?={0,2}\s*\d+\.\d+(\.\d+)?(-[a-z0-9.-]+)?(\.\*)?$/i)
|
105
|
+
convert_exact(req_string)
|
104
106
|
elsif req_string.include?(".*") then convert_wildcard(req_string)
|
105
107
|
else
|
106
108
|
req_string
|
@@ -155,6 +157,37 @@ module Dependabot
|
|
155
157
|
.gsub(/\*$/, "0.dev")
|
156
158
|
.tap { |s| exact_op ? s.gsub!(/^(?<!!)=*/, "~>") : s }
|
157
159
|
end
|
160
|
+
|
161
|
+
def convert_exact(req_string)
|
162
|
+
arbitrary_equality = req_string.start_with?("===")
|
163
|
+
cleaned_version = req_string.gsub(/^=+/, "").strip
|
164
|
+
|
165
|
+
return ["=== #{cleaned_version}"] if arbitrary_equality
|
166
|
+
|
167
|
+
# Handle versions wildcarded with .*, e.g. 1.0.*
|
168
|
+
if cleaned_version.include?(".*")
|
169
|
+
# Remove all characters after the first .*, and the .*
|
170
|
+
cleaned_version = cleaned_version.split(".*").first
|
171
|
+
version = Version.new(cleaned_version)
|
172
|
+
# Get the release segment parts [major, minor, patch]
|
173
|
+
version_parts = version.release_segment
|
174
|
+
|
175
|
+
if version_parts.length == 1
|
176
|
+
major = T.must(version_parts[0])
|
177
|
+
[">= #{major}.0.0.dev", "< #{major + 1}.0.0"]
|
178
|
+
elsif version_parts.length == 2
|
179
|
+
major, minor = version_parts
|
180
|
+
"~> #{major}.#{minor}.0.dev"
|
181
|
+
elsif version_parts.length == 3
|
182
|
+
major, minor, patch = version_parts
|
183
|
+
"~> #{major}.#{minor}.#{patch}.dev"
|
184
|
+
else
|
185
|
+
"= #{cleaned_version}"
|
186
|
+
end
|
187
|
+
else
|
188
|
+
"= #{cleaned_version}"
|
189
|
+
end
|
190
|
+
end
|
158
191
|
end
|
159
192
|
end
|
160
193
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-uv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.303.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.303.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.303.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.8.
|
159
|
+
version: 0.8.7
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.8.
|
166
|
+
version: 0.8.7
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: simplecov
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -285,7 +285,7 @@ licenses:
|
|
285
285
|
- MIT
|
286
286
|
metadata:
|
287
287
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
288
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
288
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.303.0
|
289
289
|
post_install_message:
|
290
290
|
rdoc_options: []
|
291
291
|
require_paths:
|