dependabot-uv 0.332.0 → 0.334.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/lib/dependabot/uv/authed_url_builder.rb +8 -3
- data/lib/dependabot/uv/file_fetcher.rb +16 -2
- data/lib/dependabot/uv/file_parser/pyproject_files_parser.rb +1 -0
- data/lib/dependabot/uv/file_parser/python_requirement_parser.rb +39 -16
- data/lib/dependabot/uv/file_parser/setup_file_parser.rb +1 -0
- data/lib/dependabot/uv/file_updater/compile_file_updater.rb +149 -70
- data/lib/dependabot/uv/file_updater/lock_file_updater.rb +3 -2
- data/lib/dependabot/uv/file_updater/requirement_file_updater.rb +8 -8
- data/lib/dependabot/uv/file_updater/requirement_replacer.rb +61 -24
- data/lib/dependabot/uv/file_updater.rb +2 -2
- data/lib/dependabot/uv/language.rb +1 -0
- data/lib/dependabot/uv/metadata_finder.rb +41 -10
- data/lib/dependabot/uv/package/package_registry_finder.rb +116 -61
- data/lib/dependabot/uv/requirement.rb +28 -19
- data/lib/dependabot/uv/update_checker/lock_file_resolver.rb +26 -2
- data/lib/dependabot/uv/update_checker/pip_compile_version_resolver.rb +133 -54
- data/lib/dependabot/uv/update_checker/pip_version_resolver.rb +58 -22
- data/lib/dependabot/uv/update_checker/requirements_updater.rb +79 -31
- data/lib/dependabot/uv/update_checker.rb +120 -36
- data/lib/dependabot/uv/version.rb +22 -14
- metadata +6 -6
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "open3"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
|
|
5
7
|
require "dependabot/dependency"
|
|
6
8
|
require "dependabot/uv/requirement_parser"
|
|
7
9
|
require "dependabot/uv/file_fetcher"
|
|
@@ -18,40 +20,64 @@ module Dependabot
|
|
|
18
20
|
class FileUpdater
|
|
19
21
|
# rubocop:disable Metrics/ClassLength
|
|
20
22
|
class CompileFileUpdater
|
|
23
|
+
extend T::Sig
|
|
24
|
+
|
|
21
25
|
require_relative "requirement_replacer"
|
|
22
26
|
require_relative "requirement_file_updater"
|
|
23
27
|
|
|
24
|
-
UNSAFE_PACKAGES = %w(setuptools distribute pip).freeze
|
|
25
|
-
INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
UNSAFE_PACKAGES = T.let(%w(setuptools distribute pip).freeze, T::Array[String])
|
|
29
|
+
INCOMPATIBLE_VERSIONS_REGEX = T.let(/There are incompatible versions in the resolved dependencies:.*\z/m,
|
|
30
|
+
Regexp)
|
|
31
|
+
WARNINGS = T.let(/\s*# WARNING:.*\Z/m, Regexp)
|
|
32
|
+
UNSAFE_NOTE = T.let(/\s*# The following packages are considered to be unsafe.*\Z/m, Regexp)
|
|
33
|
+
RESOLVER_REGEX = T.let(/(?<=--resolver=)(\w+)/, Regexp)
|
|
34
|
+
NATIVE_COMPILATION_ERROR = T.let(
|
|
35
|
+
"pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1",
|
|
36
|
+
String
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
|
32
40
|
attr_reader :dependencies
|
|
41
|
+
|
|
42
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
33
43
|
attr_reader :dependency_files
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
|
34
46
|
attr_reader :credentials
|
|
35
47
|
|
|
48
|
+
sig do
|
|
49
|
+
params(
|
|
50
|
+
dependencies: T::Array[Dependabot::Dependency],
|
|
51
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
52
|
+
credentials: T::Array[Dependabot::Credential],
|
|
53
|
+
index_urls: T.nilable(T::Array[T.nilable(String)])
|
|
54
|
+
).void
|
|
55
|
+
end
|
|
36
56
|
def initialize(dependencies:, dependency_files:, credentials:, index_urls: nil)
|
|
37
|
-
@dependencies = dependencies
|
|
38
|
-
@dependency_files = dependency_files
|
|
39
|
-
@credentials = credentials
|
|
40
|
-
@index_urls = index_urls
|
|
41
|
-
@build_isolation = true
|
|
57
|
+
@dependencies = T.let(dependencies, T::Array[Dependabot::Dependency])
|
|
58
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
|
59
|
+
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
|
60
|
+
@index_urls = T.let(index_urls, T.nilable(T::Array[T.nilable(String)]))
|
|
61
|
+
@build_isolation = T.let(true, T::Boolean)
|
|
42
62
|
end
|
|
43
63
|
|
|
64
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
44
65
|
def updated_dependency_files
|
|
45
|
-
@updated_dependency_files ||=
|
|
66
|
+
@updated_dependency_files ||= T.let(
|
|
67
|
+
fetch_updated_dependency_files,
|
|
68
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
69
|
+
)
|
|
46
70
|
end
|
|
47
71
|
|
|
48
72
|
private
|
|
49
73
|
|
|
74
|
+
sig { returns(T.nilable(Dependabot::Dependency)) }
|
|
50
75
|
def dependency
|
|
51
76
|
# For now, we'll only ever be updating a single dependency
|
|
52
77
|
dependencies.first
|
|
53
78
|
end
|
|
54
79
|
|
|
80
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
55
81
|
def fetch_updated_dependency_files
|
|
56
82
|
updated_compiled_files = compile_new_requirement_files
|
|
57
83
|
updated_manifest_files = update_manifest_files
|
|
@@ -66,6 +92,7 @@ module Dependabot
|
|
|
66
92
|
]
|
|
67
93
|
end
|
|
68
94
|
|
|
95
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
69
96
|
def compile_new_requirement_files
|
|
70
97
|
SharedHelpers.in_a_temporary_directory do
|
|
71
98
|
write_updated_dependency_files
|
|
@@ -92,6 +119,7 @@ module Dependabot
|
|
|
92
119
|
end
|
|
93
120
|
end
|
|
94
121
|
|
|
122
|
+
sig { params(filename: String).void }
|
|
95
123
|
def compile_file(filename)
|
|
96
124
|
# Shell out to pip-compile, generate a new set of requirements.
|
|
97
125
|
# This is slow, as pip-compile needs to do installs.
|
|
@@ -100,12 +128,12 @@ module Dependabot
|
|
|
100
128
|
|
|
101
129
|
name_part = "pyenv exec uv pip compile " \
|
|
102
130
|
"#{options} -P " \
|
|
103
|
-
"#{dependency.name}"
|
|
131
|
+
"#{T.must(dependency).name}"
|
|
104
132
|
fingerprint_name_part = "pyenv exec uv pip compile " \
|
|
105
133
|
"#{options_fingerprint} -P " \
|
|
106
134
|
"<dependency_name>"
|
|
107
135
|
|
|
108
|
-
version_part = "#{dependency.version} #{filename}"
|
|
136
|
+
version_part = "#{T.must(dependency).version} #{filename}"
|
|
109
137
|
fingerprint_version_part = "<dependency_version> <filename>"
|
|
110
138
|
|
|
111
139
|
# Don't escape pyenv `dep-name==version` syntax
|
|
@@ -126,10 +154,12 @@ module Dependabot
|
|
|
126
154
|
raise
|
|
127
155
|
end
|
|
128
156
|
|
|
157
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
|
129
158
|
def compilation_error?(error)
|
|
130
159
|
error.message.include?(NATIVE_COMPILATION_ERROR)
|
|
131
160
|
end
|
|
132
161
|
|
|
162
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
133
163
|
def update_manifest_files
|
|
134
164
|
dependency_files.filter_map do |file|
|
|
135
165
|
next unless file.name.end_with?(".in")
|
|
@@ -143,31 +173,44 @@ module Dependabot
|
|
|
143
173
|
end
|
|
144
174
|
end
|
|
145
175
|
|
|
176
|
+
# rubocop:disable Metrics/AbcSize
|
|
177
|
+
sig do
|
|
178
|
+
params(updated_files: T::Array[Dependabot::DependencyFile]).returns(T::Array[Dependabot::DependencyFile])
|
|
179
|
+
end
|
|
146
180
|
def update_uncompiled_files(updated_files)
|
|
147
181
|
updated_filenames = updated_files.map(&:name)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
182
|
+
all_old_reqs = T.must(dependency).previous_requirements
|
|
183
|
+
old_reqs = T.must(all_old_reqs).reject { |r| updated_filenames.include?(r[:file]) }
|
|
184
|
+
all_new_reqs = T.must(dependency).requirements
|
|
185
|
+
new_reqs = all_new_reqs.reject { |r| updated_filenames.include?(r[:file]) }
|
|
152
186
|
|
|
153
187
|
return [] if new_reqs.none?
|
|
154
188
|
|
|
155
189
|
files = dependency_files
|
|
156
190
|
.reject { |file| updated_filenames.include?(file.name) }
|
|
157
191
|
|
|
158
|
-
args = dependency.to_h
|
|
192
|
+
args = T.must(dependency).to_h
|
|
159
193
|
args = args.keys.to_h { |k| [k.to_sym, args[k]] }
|
|
160
194
|
args[:requirements] = new_reqs
|
|
161
195
|
args[:previous_requirements] = old_reqs
|
|
162
196
|
|
|
163
197
|
RequirementFileUpdater.new(
|
|
164
|
-
dependencies: [Dependency.new(**args)],
|
|
198
|
+
dependencies: [Dependency.new(**T.unsafe(args))],
|
|
165
199
|
dependency_files: files,
|
|
166
200
|
credentials: credentials
|
|
167
201
|
).updated_dependency_files
|
|
168
202
|
end
|
|
203
|
+
# rubocop:enable Metrics/AbcSize
|
|
169
204
|
|
|
170
|
-
|
|
205
|
+
sig do
|
|
206
|
+
params(
|
|
207
|
+
cmd: String,
|
|
208
|
+
fingerprint: String,
|
|
209
|
+
env: T.nilable(T::Hash[String, String]),
|
|
210
|
+
allow_unsafe_shell_command: T::Boolean
|
|
211
|
+
).returns(String)
|
|
212
|
+
end
|
|
213
|
+
def run_command(cmd, fingerprint:, env: python_env, allow_unsafe_shell_command: false)
|
|
171
214
|
SharedHelpers.run_shell_command(
|
|
172
215
|
cmd,
|
|
173
216
|
env: env,
|
|
@@ -185,7 +228,8 @@ module Dependabot
|
|
|
185
228
|
raise
|
|
186
229
|
end
|
|
187
230
|
|
|
188
|
-
|
|
231
|
+
sig { params(command: String, fingerprint: String, allow_unsafe_shell_command: T::Boolean).returns(String) }
|
|
232
|
+
def run_uv_compile_command(command, fingerprint:, allow_unsafe_shell_command: false)
|
|
189
233
|
run_command(
|
|
190
234
|
"pyenv local #{language_version_manager.python_major_minor}",
|
|
191
235
|
fingerprint: "pyenv local <python_major_minor>"
|
|
@@ -198,12 +242,13 @@ module Dependabot
|
|
|
198
242
|
)
|
|
199
243
|
end
|
|
200
244
|
|
|
245
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
|
201
246
|
def python_env
|
|
202
247
|
env = {}
|
|
203
248
|
|
|
204
249
|
# Handle Apache Airflow 1.10.x installs
|
|
205
|
-
if dependency_files.any? { |f| f.content.include?("apache-airflow") }
|
|
206
|
-
if dependency_files.any? { |f| f.content.include?("unidecode") }
|
|
250
|
+
if dependency_files.any? { |f| T.must(f.content).include?("apache-airflow") }
|
|
251
|
+
if dependency_files.any? { |f| T.must(f.content).include?("unidecode") }
|
|
207
252
|
env["AIRFLOW_GPL_UNIDECODE"] = "yes"
|
|
208
253
|
else
|
|
209
254
|
env["SLUGIFY_USES_TEXT_UNIDECODE"] = "yes"
|
|
@@ -213,6 +258,7 @@ module Dependabot
|
|
|
213
258
|
env
|
|
214
259
|
end
|
|
215
260
|
|
|
261
|
+
sig { void }
|
|
216
262
|
def write_updated_dependency_files
|
|
217
263
|
dependency_files.each do |file|
|
|
218
264
|
path = file.name
|
|
@@ -224,50 +270,54 @@ module Dependabot
|
|
|
224
270
|
File.write(".python-version", language_version_manager.python_major_minor)
|
|
225
271
|
end
|
|
226
272
|
|
|
273
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
|
227
274
|
def freeze_dependency_requirement(file)
|
|
228
275
|
return file.content unless file.name.end_with?(".in")
|
|
229
276
|
|
|
230
|
-
old_req = dependency.previous_requirements
|
|
231
|
-
|
|
277
|
+
old_req = T.must(dependency).previous_requirements
|
|
278
|
+
old_req = old_req&.find { |r| r[:file] == file.name }
|
|
232
279
|
|
|
233
280
|
return file.content unless old_req
|
|
234
|
-
return file.content if old_req == "==#{dependency.version}"
|
|
281
|
+
return file.content if old_req == "==#{T.must(dependency).version}"
|
|
235
282
|
|
|
236
283
|
RequirementReplacer.new(
|
|
237
|
-
content: file.content,
|
|
238
|
-
dependency_name: dependency.name,
|
|
284
|
+
content: T.must(file.content),
|
|
285
|
+
dependency_name: T.must(dependency).name,
|
|
239
286
|
old_requirement: old_req[:requirement],
|
|
240
|
-
new_requirement: "==#{dependency.version}",
|
|
287
|
+
new_requirement: "==#{T.must(dependency).version}",
|
|
241
288
|
index_urls: @index_urls
|
|
242
289
|
).updated_content
|
|
243
290
|
end
|
|
244
291
|
|
|
292
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
|
245
293
|
def update_dependency_requirement(file)
|
|
246
294
|
return file.content unless file.name.end_with?(".in")
|
|
247
295
|
|
|
248
|
-
old_req = dependency.previous_requirements
|
|
249
|
-
|
|
250
|
-
new_req = dependency.requirements
|
|
251
|
-
|
|
296
|
+
old_req = T.must(dependency).previous_requirements
|
|
297
|
+
old_req = old_req&.find { |r| r[:file] == file.name }
|
|
298
|
+
new_req = T.must(dependency).requirements
|
|
299
|
+
new_req = new_req.find { |r| r[:file] == file.name }
|
|
252
300
|
return file.content unless old_req&.fetch(:requirement)
|
|
253
301
|
return file.content if old_req == new_req
|
|
254
302
|
|
|
255
303
|
RequirementReplacer.new(
|
|
256
|
-
content: file.content,
|
|
257
|
-
dependency_name: dependency.name,
|
|
304
|
+
content: T.must(file.content),
|
|
305
|
+
dependency_name: T.must(dependency).name,
|
|
258
306
|
old_requirement: old_req[:requirement],
|
|
259
|
-
new_requirement: new_req[:requirement],
|
|
307
|
+
new_requirement: T.must(new_req)[:requirement],
|
|
260
308
|
index_urls: @index_urls
|
|
261
309
|
).updated_content
|
|
262
310
|
end
|
|
263
311
|
|
|
312
|
+
sig { params(updated_content: String, file: Dependabot::DependencyFile).returns(String) }
|
|
264
313
|
def post_process_compiled_file(updated_content, file)
|
|
265
|
-
content = replace_header_with_original(updated_content, file.content)
|
|
266
|
-
content = remove_new_warnings(content, file.content)
|
|
267
|
-
content = update_hashes_if_required(content, file.content)
|
|
268
|
-
replace_absolute_file_paths(content, file.content)
|
|
314
|
+
content = replace_header_with_original(updated_content, T.must(file.content))
|
|
315
|
+
content = remove_new_warnings(content, T.must(file.content))
|
|
316
|
+
content = update_hashes_if_required(content, T.must(file.content))
|
|
317
|
+
replace_absolute_file_paths(content, T.must(file.content))
|
|
269
318
|
end
|
|
270
319
|
|
|
320
|
+
sig { params(updated_content: String, original_content: String).returns(String) }
|
|
271
321
|
def replace_header_with_original(updated_content, original_content)
|
|
272
322
|
original_header_lines =
|
|
273
323
|
original_content.lines.take_while { |l| l.start_with?("#") }
|
|
@@ -278,6 +328,7 @@ module Dependabot
|
|
|
278
328
|
[*original_header_lines, *updated_content_lines].join
|
|
279
329
|
end
|
|
280
330
|
|
|
331
|
+
sig { params(updated_content: String, original_content: String).returns(String) }
|
|
281
332
|
def replace_absolute_file_paths(updated_content, original_content)
|
|
282
333
|
content = updated_content
|
|
283
334
|
|
|
@@ -299,6 +350,7 @@ module Dependabot
|
|
|
299
350
|
content
|
|
300
351
|
end
|
|
301
352
|
|
|
353
|
+
sig { params(updated_content: String, original_content: String).returns(String) }
|
|
302
354
|
def remove_new_warnings(updated_content, original_content)
|
|
303
355
|
content = updated_content
|
|
304
356
|
|
|
@@ -312,6 +364,7 @@ module Dependabot
|
|
|
312
364
|
content
|
|
313
365
|
end
|
|
314
366
|
|
|
367
|
+
sig { params(updated_content: String, original_content: String).returns(String) }
|
|
315
368
|
def update_hashes_if_required(updated_content, original_content)
|
|
316
369
|
deps_to_update =
|
|
317
370
|
deps_to_augment_hashes_for(updated_content, original_content)
|
|
@@ -324,7 +377,7 @@ module Dependabot
|
|
|
324
377
|
name: mtch.named_captures.fetch("name"),
|
|
325
378
|
version: mtch.named_captures.fetch("version"),
|
|
326
379
|
algorithm: mtch.named_captures.fetch("algorithm")
|
|
327
|
-
).sort.join(hash_separator(mtch.to_s))
|
|
380
|
+
).sort.join(T.must(hash_separator(mtch.to_s)))
|
|
328
381
|
)
|
|
329
382
|
|
|
330
383
|
updated_content_with_hashes = updated_content_with_hashes
|
|
@@ -333,6 +386,7 @@ module Dependabot
|
|
|
333
386
|
updated_content_with_hashes
|
|
334
387
|
end
|
|
335
388
|
|
|
389
|
+
sig { params(updated_content: String, original_content: String).returns(T::Array[T.untyped]) }
|
|
336
390
|
def deps_to_augment_hashes_for(updated_content, original_content)
|
|
337
391
|
regex = /^#{RequirementParser::INSTALL_REQ_WITH_REQUIREMENT}/o
|
|
338
392
|
|
|
@@ -362,6 +416,7 @@ module Dependabot
|
|
|
362
416
|
[*new_deps, *changed_hashes_deps]
|
|
363
417
|
end
|
|
364
418
|
|
|
419
|
+
sig { params(name: String, version: String, algorithm: String).returns(T::Array[String]) }
|
|
365
420
|
def package_hashes_for(name:, version:, algorithm:)
|
|
366
421
|
index_urls = @index_urls || [nil]
|
|
367
422
|
hashes = []
|
|
@@ -391,6 +446,7 @@ module Dependabot
|
|
|
391
446
|
hashes
|
|
392
447
|
end
|
|
393
448
|
|
|
449
|
+
sig { params(requirement_string: String).returns(T.nilable(String)) }
|
|
394
450
|
def hash_separator(requirement_string)
|
|
395
451
|
hash_regex = RequirementParser::HASH
|
|
396
452
|
return unless requirement_string.match?(hash_regex)
|
|
@@ -398,17 +454,18 @@ module Dependabot
|
|
|
398
454
|
current_separator =
|
|
399
455
|
requirement_string
|
|
400
456
|
.match(/#{hash_regex}((?<separator>\s*\\?\s*?)#{hash_regex})*/)
|
|
401
|
-
|
|
457
|
+
&.named_captures&.fetch("separator")
|
|
402
458
|
|
|
403
459
|
default_separator =
|
|
404
460
|
requirement_string
|
|
405
461
|
.match(RequirementParser::HASH)
|
|
406
|
-
|
|
407
|
-
|
|
462
|
+
&.pre_match&.match(/(?<separator>\s*\\?\s*?)\z/)
|
|
463
|
+
&.named_captures&.fetch("separator")
|
|
408
464
|
|
|
409
465
|
current_separator || default_separator
|
|
410
466
|
end
|
|
411
467
|
|
|
468
|
+
sig { params(options: String).returns(String) }
|
|
412
469
|
def compile_options_fingerprint(options)
|
|
413
470
|
options.sub(
|
|
414
471
|
/--output-file=\S+/, "--output-file=<output_file>"
|
|
@@ -419,6 +476,7 @@ module Dependabot
|
|
|
419
476
|
)
|
|
420
477
|
end
|
|
421
478
|
|
|
479
|
+
sig { params(filename: String).returns(String) }
|
|
422
480
|
def compile_options(filename)
|
|
423
481
|
options = @build_isolation ? ["--build-isolation"] : ["--no-build-isolation"]
|
|
424
482
|
options += compile_index_options
|
|
@@ -430,23 +488,26 @@ module Dependabot
|
|
|
430
488
|
options.join(" ")
|
|
431
489
|
end
|
|
432
490
|
|
|
491
|
+
sig { params(requirements_file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
|
433
492
|
def uv_compile_options_from_compiled_file(requirements_file)
|
|
434
493
|
options = ["--output-file=#{requirements_file.name}"]
|
|
435
|
-
options << "--emit-index-url" if requirements_file.content.include?("index-url http")
|
|
436
|
-
options << "--generate-hashes" if requirements_file.content.include?("--hash=sha")
|
|
437
|
-
options << "--no-annotate" unless requirements_file.content.include?("# via ")
|
|
438
|
-
options << "--pre" if requirements_file.content.include?("--pre")
|
|
439
|
-
options << "--no-strip-extras" if requirements_file.content.include?("--no-strip-extras")
|
|
440
|
-
|
|
441
|
-
if requirements_file.content.include?("--no-binary") ||
|
|
494
|
+
options << "--emit-index-url" if T.must(requirements_file.content).include?("index-url http")
|
|
495
|
+
options << "--generate-hashes" if T.must(requirements_file.content).include?("--hash=sha")
|
|
496
|
+
options << "--no-annotate" unless T.must(requirements_file.content).include?("# via ")
|
|
497
|
+
options << "--pre" if T.must(requirements_file.content).include?("--pre")
|
|
498
|
+
options << "--no-strip-extras" if T.must(requirements_file.content).include?("--no-strip-extras")
|
|
499
|
+
|
|
500
|
+
if T.must(requirements_file.content).include?("--no-binary") ||
|
|
501
|
+
T.must(requirements_file.content).include?("--only-binary")
|
|
442
502
|
options << "--emit-build-options"
|
|
443
503
|
end
|
|
444
504
|
|
|
445
|
-
options << "--universal" if requirements_file.content.include?("--universal")
|
|
505
|
+
options << "--universal" if T.must(requirements_file.content).include?("--universal")
|
|
446
506
|
|
|
447
507
|
options
|
|
448
508
|
end
|
|
449
509
|
|
|
510
|
+
sig { returns(T::Array[String]) }
|
|
450
511
|
def compile_index_options
|
|
451
512
|
credentials
|
|
452
513
|
.select { |cred| cred["type"] == "python_index" }
|
|
@@ -461,15 +522,17 @@ module Dependabot
|
|
|
461
522
|
end
|
|
462
523
|
end
|
|
463
524
|
|
|
525
|
+
sig { params(content: String).returns(T::Boolean) }
|
|
464
526
|
def includes_unsafe_packages?(content)
|
|
465
527
|
UNSAFE_PACKAGES.any? { |n| content.match?(/^#{Regexp.quote(n)}==/) }
|
|
466
528
|
end
|
|
467
529
|
|
|
530
|
+
sig { returns(T::Array[String]) }
|
|
468
531
|
def filenames_to_compile
|
|
469
532
|
files_from_reqs =
|
|
470
|
-
dependency.requirements
|
|
471
|
-
|
|
472
|
-
|
|
533
|
+
T.must(dependency).requirements
|
|
534
|
+
.map { |r| r[:file] }
|
|
535
|
+
.select { |fn| fn.end_with?(".in") }
|
|
473
536
|
|
|
474
537
|
files_from_compiled_files =
|
|
475
538
|
compile_files.map(&:name).select do |fn|
|
|
@@ -482,10 +545,11 @@ module Dependabot
|
|
|
482
545
|
order_filenames_for_compilation(filenames)
|
|
483
546
|
end
|
|
484
547
|
|
|
548
|
+
sig { params(filename: String).returns(T.nilable(Dependabot::DependencyFile)) }
|
|
485
549
|
def compiled_file_for_filename(filename)
|
|
486
550
|
compiled_file =
|
|
487
551
|
compiled_files
|
|
488
|
-
.find { |f| f.content.match?(output_file_regex(filename)) }
|
|
552
|
+
.find { |f| T.must(f.content).match?(output_file_regex(filename)) }
|
|
489
553
|
|
|
490
554
|
compiled_file ||=
|
|
491
555
|
compiled_files
|
|
@@ -494,26 +558,30 @@ module Dependabot
|
|
|
494
558
|
compiled_file
|
|
495
559
|
end
|
|
496
560
|
|
|
561
|
+
sig { params(filename: T.any(String, Symbol)).returns(String) }
|
|
497
562
|
def output_file_regex(filename)
|
|
498
563
|
"--output-file[=\s]+.*\s#{Regexp.escape(filename)}\s*$"
|
|
499
564
|
end
|
|
500
565
|
|
|
566
|
+
sig { params(compiled_file: T.nilable(Dependabot::DependencyFile)).returns(T::Boolean) }
|
|
501
567
|
def compiled_file_includes_dependency?(compiled_file)
|
|
502
568
|
return false unless compiled_file
|
|
503
569
|
|
|
504
570
|
regex = RequirementParser::INSTALL_REQ_WITH_REQUIREMENT
|
|
505
571
|
|
|
506
572
|
matches = []
|
|
507
|
-
compiled_file.content.scan(regex) { matches << Regexp.last_match }
|
|
508
|
-
matches.any? { |m| normalise(m[:name]) == dependency.name }
|
|
573
|
+
T.must(compiled_file.content).scan(regex) { matches << Regexp.last_match }
|
|
574
|
+
matches.any? { |m| normalise(m[:name]) == T.must(dependency).name }
|
|
509
575
|
end
|
|
510
576
|
|
|
577
|
+
sig { params(name: String).returns(String) }
|
|
511
578
|
def normalise(name)
|
|
512
579
|
NameNormaliser.normalise(name)
|
|
513
580
|
end
|
|
514
581
|
|
|
515
582
|
# If the files we need to update require one another then we need to
|
|
516
583
|
# update them in the right order
|
|
584
|
+
sig { params(filenames: T::Array[String]).returns(T::Array[String]) }
|
|
517
585
|
def order_filenames_for_compilation(filenames)
|
|
518
586
|
ordered_filenames = T.let([], T::Array[String])
|
|
519
587
|
|
|
@@ -521,7 +589,7 @@ module Dependabot
|
|
|
521
589
|
ordered_filenames +=
|
|
522
590
|
remaining_filenames
|
|
523
591
|
.reject do |fn|
|
|
524
|
-
unupdated_reqs = requirement_map[fn] - ordered_filenames
|
|
592
|
+
unupdated_reqs = (requirement_map[fn] || []) - ordered_filenames
|
|
525
593
|
unupdated_reqs.intersect?(filenames)
|
|
526
594
|
end
|
|
527
595
|
end
|
|
@@ -529,11 +597,12 @@ module Dependabot
|
|
|
529
597
|
ordered_filenames
|
|
530
598
|
end
|
|
531
599
|
|
|
600
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
532
601
|
def requirement_map
|
|
533
602
|
child_req_regex = Uv::FileFetcher::CHILD_REQUIREMENT_REGEX
|
|
534
|
-
@requirement_map ||=
|
|
603
|
+
@requirement_map ||= T.let(
|
|
535
604
|
compile_files.each_with_object({}) do |file, req_map|
|
|
536
|
-
paths = file.content.scan(child_req_regex).flatten
|
|
605
|
+
paths = T.must(file.content).scan(child_req_regex).flatten
|
|
537
606
|
current_dir = File.dirname(file.name)
|
|
538
607
|
|
|
539
608
|
req_map[file.name] =
|
|
@@ -545,27 +614,37 @@ module Dependabot
|
|
|
545
614
|
|
|
546
615
|
path
|
|
547
616
|
end.uniq.compact
|
|
548
|
-
end
|
|
617
|
+
end,
|
|
618
|
+
T.nilable(T::Hash[String, T::Array[String]])
|
|
619
|
+
)
|
|
549
620
|
end
|
|
550
621
|
|
|
622
|
+
sig { returns(Dependabot::Uv::FileParser::PythonRequirementParser) }
|
|
551
623
|
def python_requirement_parser
|
|
552
|
-
@python_requirement_parser ||=
|
|
624
|
+
@python_requirement_parser ||= T.let(
|
|
553
625
|
FileParser::PythonRequirementParser.new(
|
|
554
626
|
dependency_files: dependency_files
|
|
555
|
-
)
|
|
627
|
+
),
|
|
628
|
+
T.nilable(FileParser::PythonRequirementParser)
|
|
629
|
+
)
|
|
556
630
|
end
|
|
557
631
|
|
|
632
|
+
sig { returns(Dependabot::Uv::LanguageVersionManager) }
|
|
558
633
|
def language_version_manager
|
|
559
|
-
@language_version_manager ||=
|
|
634
|
+
@language_version_manager ||= T.let(
|
|
560
635
|
LanguageVersionManager.new(
|
|
561
636
|
python_requirement_parser: python_requirement_parser
|
|
562
|
-
)
|
|
637
|
+
),
|
|
638
|
+
T.nilable(LanguageVersionManager)
|
|
639
|
+
)
|
|
563
640
|
end
|
|
564
641
|
|
|
642
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
565
643
|
def compile_files
|
|
566
644
|
dependency_files.select { |f| f.name.end_with?(".in") }
|
|
567
645
|
end
|
|
568
646
|
|
|
647
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
569
648
|
def compiled_files
|
|
570
649
|
dependency_files.select { |f| f.name.end_with?(".txt") }
|
|
571
650
|
end
|
|
@@ -18,6 +18,7 @@ module Dependabot
|
|
|
18
18
|
class FileUpdater
|
|
19
19
|
class LockFileUpdater
|
|
20
20
|
extend T::Sig
|
|
21
|
+
|
|
21
22
|
require_relative "pyproject_preparer"
|
|
22
23
|
|
|
23
24
|
REQUIRED_FILES = %w(pyproject.toml uv.lock).freeze # At least one of these files should be present
|
|
@@ -31,7 +32,7 @@ module Dependabot
|
|
|
31
32
|
sig { returns(T::Array[Dependabot::Credential]) }
|
|
32
33
|
attr_reader :credentials
|
|
33
34
|
|
|
34
|
-
sig { returns(T.nilable(T::Array[String])) }
|
|
35
|
+
sig { returns(T.nilable(T::Array[T.nilable(String)])) }
|
|
35
36
|
attr_reader :index_urls
|
|
36
37
|
|
|
37
38
|
sig do
|
|
@@ -39,7 +40,7 @@ module Dependabot
|
|
|
39
40
|
dependencies: T::Array[Dependency],
|
|
40
41
|
dependency_files: T::Array[DependencyFile],
|
|
41
42
|
credentials: T::Array[Dependabot::Credential],
|
|
42
|
-
index_urls: T.nilable(T::Array[String])
|
|
43
|
+
index_urls: T.nilable(T::Array[T.nilable(String)])
|
|
43
44
|
).void
|
|
44
45
|
end
|
|
45
46
|
def initialize(dependencies:, dependency_files:, credentials:, index_urls: nil)
|
|
@@ -29,7 +29,7 @@ module Dependabot
|
|
|
29
29
|
dependencies: T::Array[Dependency],
|
|
30
30
|
dependency_files: T::Array[DependencyFile],
|
|
31
31
|
credentials: T::Array[Dependabot::Credential],
|
|
32
|
-
index_urls: T.nilable(T::Array[String])
|
|
32
|
+
index_urls: T.nilable(T::Array[T.nilable(String)])
|
|
33
33
|
).void
|
|
34
34
|
end
|
|
35
35
|
def initialize(dependencies:, dependency_files:, credentials:, index_urls: nil)
|
|
@@ -47,16 +47,16 @@ module Dependabot
|
|
|
47
47
|
|
|
48
48
|
private
|
|
49
49
|
|
|
50
|
-
sig { returns(
|
|
50
|
+
sig { returns(Dependency) }
|
|
51
51
|
def dependency
|
|
52
52
|
# For now, we'll only ever be updating a single dependency
|
|
53
|
-
dependencies.first
|
|
53
|
+
T.must(dependencies.first)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
sig { returns(T::Array[DependencyFile]) }
|
|
57
57
|
def fetch_updated_dependency_files
|
|
58
|
-
previous_requirements = dependency
|
|
59
|
-
reqs =
|
|
58
|
+
previous_requirements = dependency.previous_requirements || []
|
|
59
|
+
reqs = dependency.requirements.zip(previous_requirements)
|
|
60
60
|
|
|
61
61
|
reqs.filter_map do |(new_req, old_req)|
|
|
62
62
|
next if new_req == old_req
|
|
@@ -77,11 +77,11 @@ module Dependabot
|
|
|
77
77
|
raise "Could not find a dependency file for #{new_req}" unless original_file
|
|
78
78
|
|
|
79
79
|
RequirementReplacer.new(
|
|
80
|
-
content: original_file.content,
|
|
81
|
-
dependency_name: dependency
|
|
80
|
+
content: T.must(original_file.content),
|
|
81
|
+
dependency_name: dependency.name,
|
|
82
82
|
old_requirement: old_req.fetch(:requirement),
|
|
83
83
|
new_requirement: new_req.fetch(:requirement),
|
|
84
|
-
new_hash_version: dependency
|
|
84
|
+
new_hash_version: dependency.version,
|
|
85
85
|
index_urls: @index_urls
|
|
86
86
|
).updated_content
|
|
87
87
|
end
|