dependabot-python 0.104.5 → 0.104.6
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/python/update_checker.rb +72 -29
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +24 -65
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +23 -65
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +29 -73
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86a22a74e2cfeb8d600f160d2078900565de559537249b34d6e8d31d3475f01f
|
4
|
+
data.tar.gz: 7a0b0874123f7decd66511a39b1ebe207c98fd8968e6194b0906ca5ef68e6d8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30592c176e2ce1dadc2b1f4d689861fd57388377827af9d1a189cb9ae37aa790eba4ef9a727e325c6ef696b8364c93703a9116657c26840091e9de95bbfecc9e
|
7
|
+
data.tar.gz: cbc364db1c7a3de0b4c7643af272c424946e32b16e90742cc911d9e728eb42f97e850bd5d4c304e66fe27fa3b7fa322c4b2b25724a72134a2bb3377dfb068cb2
|
@@ -22,6 +22,7 @@ module Dependabot
|
|
22
22
|
https://pypi.python.org/simple/
|
23
23
|
https://pypi.org/simple/
|
24
24
|
).freeze
|
25
|
+
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
|
25
26
|
|
26
27
|
def latest_version
|
27
28
|
@latest_version ||= fetch_latest_version
|
@@ -31,11 +32,17 @@ module Dependabot
|
|
31
32
|
@latest_resolvable_version ||=
|
32
33
|
case resolver_type
|
33
34
|
when :pipenv
|
34
|
-
pipenv_version_resolver.latest_resolvable_version
|
35
|
+
pipenv_version_resolver.latest_resolvable_version(
|
36
|
+
requirement: unlocked_requirement_string
|
37
|
+
)
|
35
38
|
when :poetry
|
36
|
-
poetry_version_resolver.latest_resolvable_version
|
39
|
+
poetry_version_resolver.latest_resolvable_version(
|
40
|
+
requirement: unlocked_requirement_string
|
41
|
+
)
|
37
42
|
when :pip_compile
|
38
|
-
pip_compile_version_resolver.latest_resolvable_version
|
43
|
+
pip_compile_version_resolver.latest_resolvable_version(
|
44
|
+
requirement: unlocked_requirement_string
|
45
|
+
)
|
39
46
|
when :requirements
|
40
47
|
# pip doesn't (yet) do any dependency resolution, so if we don't
|
41
48
|
# have a Pipfile or a pip-compile file, we just return the latest
|
@@ -49,17 +56,17 @@ module Dependabot
|
|
49
56
|
@latest_resolvable_version_with_no_unlock ||=
|
50
57
|
case resolver_type
|
51
58
|
when :pipenv
|
52
|
-
pipenv_version_resolver(
|
53
|
-
|
54
|
-
)
|
59
|
+
pipenv_version_resolver.latest_resolvable_version(
|
60
|
+
requirement: current_requirement_string
|
61
|
+
)
|
55
62
|
when :poetry
|
56
|
-
poetry_version_resolver(
|
57
|
-
|
58
|
-
)
|
63
|
+
poetry_version_resolver.latest_resolvable_version(
|
64
|
+
requirement: current_requirement_string
|
65
|
+
)
|
59
66
|
when :pip_compile
|
60
|
-
pip_compile_version_resolver(
|
61
|
-
|
62
|
-
)
|
67
|
+
pip_compile_version_resolver.latest_resolvable_version(
|
68
|
+
requirement: current_requirement_string
|
69
|
+
)
|
63
70
|
when :requirements
|
64
71
|
latest_pip_version_with_no_unlock
|
65
72
|
else raise "Unexpected resolver type #{resolver_type}"
|
@@ -154,36 +161,72 @@ module Dependabot
|
|
154
161
|
reqs.any? { |r| Python::Requirement.new(r).exact? }
|
155
162
|
end
|
156
163
|
|
157
|
-
def pipenv_version_resolver
|
158
|
-
@pipenv_version_resolver ||=
|
159
|
-
@pipenv_version_resolver[unlock_requirement] ||=
|
160
|
-
PipenvVersionResolver.
|
161
|
-
new(resolver_args.merge(unlock_requirement: unlock_requirement))
|
164
|
+
def pipenv_version_resolver
|
165
|
+
@pipenv_version_resolver ||= PipenvVersionResolver.new(resolver_args)
|
162
166
|
end
|
163
167
|
|
164
|
-
def pip_compile_version_resolver
|
165
|
-
@pip_compile_version_resolver ||=
|
166
|
-
|
167
|
-
PipCompileVersionResolver.
|
168
|
-
new(resolver_args.merge(unlock_requirement: unlock_requirement))
|
168
|
+
def pip_compile_version_resolver
|
169
|
+
@pip_compile_version_resolver ||=
|
170
|
+
PipCompileVersionResolver.new(resolver_args)
|
169
171
|
end
|
170
172
|
|
171
|
-
def poetry_version_resolver
|
172
|
-
@poetry_version_resolver ||=
|
173
|
-
@poetry_version_resolver[unlock_requirement] ||=
|
174
|
-
PoetryVersionResolver.
|
175
|
-
new(resolver_args.merge(unlock_requirement: unlock_requirement))
|
173
|
+
def poetry_version_resolver
|
174
|
+
@poetry_version_resolver ||= PoetryVersionResolver.new(resolver_args)
|
176
175
|
end
|
177
176
|
|
178
177
|
def resolver_args
|
179
178
|
{
|
180
179
|
dependency: dependency,
|
181
180
|
dependency_files: dependency_files,
|
182
|
-
credentials: credentials
|
183
|
-
latest_allowable_version: latest_version
|
181
|
+
credentials: credentials
|
184
182
|
}
|
185
183
|
end
|
186
184
|
|
185
|
+
def current_requirement_string
|
186
|
+
reqs = dependency.requirements
|
187
|
+
return if reqs.none?
|
188
|
+
|
189
|
+
requirement =
|
190
|
+
case resolver_type
|
191
|
+
when :pipenv then reqs.find { |r| r[:file] == "Pipfile" }
|
192
|
+
when :poetry then reqs.find { |r| r[:file] == "pyproject.toml" }
|
193
|
+
when :pip_compile then reqs.find { |r| r[:file].end_with?(".in") }
|
194
|
+
when :requirements then reqs.find { |r| r[:file].end_with?(".txt") }
|
195
|
+
end
|
196
|
+
|
197
|
+
requirement.fetch(:requirement)
|
198
|
+
end
|
199
|
+
|
200
|
+
def unlocked_requirement_string
|
201
|
+
lower_bound_req = updated_version_req_lower_bound
|
202
|
+
|
203
|
+
# Add the latest_version as an upper bound. This means
|
204
|
+
# ignore conditions are considered when checking for the latest
|
205
|
+
# resolvable version.
|
206
|
+
#
|
207
|
+
# NOTE: This isn't perfect. If v2.x is ignored and v3 is out but
|
208
|
+
# unresolvable then the `latest_version` will be v3, and
|
209
|
+
# we won't be ignoring v2.x releases like we should be.
|
210
|
+
return lower_bound_req if latest_version.nil?
|
211
|
+
return lower_bound_req unless Python::Version.correct?(latest_version)
|
212
|
+
|
213
|
+
lower_bound_req + ", <= #{latest_version}"
|
214
|
+
end
|
215
|
+
|
216
|
+
def updated_version_req_lower_bound
|
217
|
+
return ">= #{dependency.version}" if dependency.version
|
218
|
+
|
219
|
+
version_for_requirement =
|
220
|
+
dependency.requirements.map { |r| r[:requirement] }.compact.
|
221
|
+
reject { |req_string| req_string.start_with?("<") }.
|
222
|
+
select { |req_string| req_string.match?(VERSION_REGEX) }.
|
223
|
+
map { |req_string| req_string.match(VERSION_REGEX) }.
|
224
|
+
select { |version| Gem::Version.correct?(version) }.
|
225
|
+
max_by { |version| Gem::Version.new(version) }
|
226
|
+
|
227
|
+
">= #{version_for_requirement || 0}"
|
228
|
+
end
|
229
|
+
|
187
230
|
def fetch_latest_version
|
188
231
|
latest_version_finder.latest_version
|
189
232
|
end
|
@@ -20,39 +20,34 @@ module Dependabot
|
|
20
20
|
# - Run `pip-compile` and see what the result is
|
21
21
|
# rubocop:disable Metrics/ClassLength
|
22
22
|
class PipCompileVersionResolver
|
23
|
-
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
|
24
|
-
|
25
23
|
attr_reader :dependency, :dependency_files, :credentials
|
26
24
|
|
27
|
-
def initialize(dependency:, dependency_files:, credentials
|
28
|
-
unlock_requirement:, latest_allowable_version:)
|
25
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
29
26
|
@dependency = dependency
|
30
27
|
@dependency_files = dependency_files
|
31
28
|
@credentials = credentials
|
32
|
-
@latest_allowable_version = latest_allowable_version
|
33
|
-
@unlock_requirement = unlock_requirement
|
34
29
|
end
|
35
30
|
|
36
|
-
def latest_resolvable_version
|
37
|
-
|
31
|
+
def latest_resolvable_version(requirement: nil)
|
32
|
+
version_string =
|
33
|
+
fetch_latest_resolvable_version_string(requirement: requirement)
|
38
34
|
|
39
|
-
|
40
|
-
@latest_resolvable_version ||= fetch_latest_resolvable_version
|
35
|
+
version_string.nil? ? nil : Python::Version.new(version_string)
|
41
36
|
end
|
42
37
|
|
43
38
|
private
|
44
39
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
@
|
49
|
-
|
40
|
+
# rubocop:disable Metrics/MethodLength
|
41
|
+
def fetch_latest_resolvable_version_string(requirement:)
|
42
|
+
@latest_resolvable_version_string ||= {}
|
43
|
+
if @latest_resolvable_version_string.key?(requirement)
|
44
|
+
return @latest_resolvable_version_string[requirement]
|
45
|
+
end
|
50
46
|
|
51
|
-
|
52
|
-
@latest_resolvable_version_string ||=
|
47
|
+
@latest_resolvable_version_string[requirement] ||=
|
53
48
|
SharedHelpers.in_a_temporary_directory do
|
54
49
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
55
|
-
write_temporary_dependency_files
|
50
|
+
write_temporary_dependency_files(updated_req: requirement)
|
56
51
|
install_required_python
|
57
52
|
|
58
53
|
filenames_to_compile.each do |filename|
|
@@ -80,10 +75,8 @@ module Dependabot
|
|
80
75
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
81
76
|
handle_pip_compile_errors(e)
|
82
77
|
end
|
83
|
-
return unless @latest_resolvable_version_string
|
84
|
-
|
85
|
-
Python::Version.new(@latest_resolvable_version_string)
|
86
78
|
end
|
79
|
+
# rubocop:enable Metrics/MethodLength
|
87
80
|
|
88
81
|
def handle_pip_compile_errors(error)
|
89
82
|
if error.message.include?("Could not find a version")
|
@@ -159,7 +152,7 @@ module Dependabot
|
|
159
152
|
def check_original_requirements_resolvable
|
160
153
|
SharedHelpers.in_a_temporary_directory do
|
161
154
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
162
|
-
write_temporary_dependency_files(
|
155
|
+
write_temporary_dependency_files(update_requirement: false)
|
163
156
|
|
164
157
|
filenames_to_compile.each do |filename|
|
165
158
|
run_command("pyenv exec pip-compile --allow-unsafe #{filename}")
|
@@ -243,14 +236,16 @@ module Dependabot
|
|
243
236
|
message.include?('Command "python setup.py egg_info" failed')
|
244
237
|
end
|
245
238
|
|
246
|
-
def write_temporary_dependency_files(
|
239
|
+
def write_temporary_dependency_files(updated_req: nil,
|
240
|
+
update_requirement: true)
|
247
241
|
dependency_files.each do |file|
|
248
242
|
path = file.name
|
249
243
|
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
244
|
+
updated_content =
|
245
|
+
if update_requirement then update_req_file(file, updated_req)
|
246
|
+
else file.content
|
247
|
+
end
|
248
|
+
File.write(path, updated_content)
|
254
249
|
end
|
255
250
|
|
256
251
|
# Overwrite the .python-version with updated content
|
@@ -297,10 +292,8 @@ module Dependabot
|
|
297
292
|
end
|
298
293
|
end
|
299
294
|
|
300
|
-
def
|
295
|
+
def update_req_file(file, updated_req)
|
301
296
|
return file.content unless file.name.end_with?(".in")
|
302
|
-
return file.content unless dependency.version
|
303
|
-
return file.content unless unlock_requirement?
|
304
297
|
|
305
298
|
req = dependency.requirements.find { |r| r[:file] == file.name }
|
306
299
|
return file.content unless req&.fetch(:requirement)
|
@@ -309,44 +302,10 @@ module Dependabot
|
|
309
302
|
content: file.content,
|
310
303
|
dependency_name: dependency.name,
|
311
304
|
old_requirement: req[:requirement],
|
312
|
-
new_requirement:
|
305
|
+
new_requirement: updated_req
|
313
306
|
).updated_content
|
314
307
|
end
|
315
308
|
|
316
|
-
def updated_version_requirement_string
|
317
|
-
lower_bound_req = updated_version_req_lower_bound
|
318
|
-
|
319
|
-
# Add the latest_allowable_version as an upper bound. This means
|
320
|
-
# ignore conditions are considered when checking for the latest
|
321
|
-
# resolvable version.
|
322
|
-
#
|
323
|
-
# NOTE: This isn't perfect. If v2.x is ignored and v3 is out but
|
324
|
-
# unresolvable then the `latest_allowable_version` will be v3, and
|
325
|
-
# we won't be ignoring v2.x releases like we should be.
|
326
|
-
return lower_bound_req if latest_allowable_version.nil?
|
327
|
-
unless Python::Version.correct?(latest_allowable_version)
|
328
|
-
return lower_bound_req
|
329
|
-
end
|
330
|
-
|
331
|
-
lower_bound_req + ", <= #{latest_allowable_version}"
|
332
|
-
end
|
333
|
-
|
334
|
-
def updated_version_req_lower_bound
|
335
|
-
if dependency.version
|
336
|
-
">= #{dependency.version}"
|
337
|
-
else
|
338
|
-
version_for_requirement =
|
339
|
-
dependency.requirements.map { |r| r[:requirement] }.compact.
|
340
|
-
reject { |req_string| req_string.start_with?("<") }.
|
341
|
-
select { |req_string| req_string.match?(VERSION_REGEX) }.
|
342
|
-
map { |req_string| req_string.match(VERSION_REGEX) }.
|
343
|
-
select { |version| Gem::Version.correct?(version) }.
|
344
|
-
max_by { |version| Gem::Version.new(version) }
|
345
|
-
|
346
|
-
">= #{version_for_requirement || 0}"
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
309
|
# See https://www.python.org/dev/peps/pep-0503/#normalized-names
|
351
310
|
def normalise(name)
|
352
311
|
name.downcase.gsub(/[-_.]+/, "-")
|
@@ -28,7 +28,6 @@ module Dependabot
|
|
28
28
|
# just raise if the latest version can't be resolved. Knowing that is
|
29
29
|
# still better than nothing, though.
|
30
30
|
class PipenvVersionResolver
|
31
|
-
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
|
32
31
|
GIT_DEPENDENCY_UNREACHABLE_REGEX =
|
33
32
|
/Command "git clone -q (?<url>[^\s]+).*" failed/.freeze
|
34
33
|
GIT_REFERENCE_NOT_FOUND_REGEX =
|
@@ -41,37 +40,33 @@ module Dependabot
|
|
41
40
|
|
42
41
|
attr_reader :dependency, :dependency_files, :credentials
|
43
42
|
|
44
|
-
def initialize(dependency:, dependency_files:, credentials
|
45
|
-
unlock_requirement:, latest_allowable_version:)
|
43
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
46
44
|
@dependency = dependency
|
47
45
|
@dependency_files = dependency_files
|
48
46
|
@credentials = credentials
|
49
|
-
@latest_allowable_version = latest_allowable_version
|
50
|
-
@unlock_requirement = unlock_requirement
|
51
47
|
|
52
48
|
check_private_sources_are_reachable
|
53
49
|
end
|
54
50
|
|
55
|
-
def latest_resolvable_version
|
56
|
-
|
51
|
+
def latest_resolvable_version(requirement: nil)
|
52
|
+
version_string =
|
53
|
+
fetch_latest_resolvable_version_string(requirement: requirement)
|
57
54
|
|
58
|
-
|
59
|
-
@latest_resolvable_version ||= fetch_latest_resolvable_version
|
55
|
+
version_string.nil? ? nil : Python::Version.new(version_string)
|
60
56
|
end
|
61
57
|
|
62
58
|
private
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
def fetch_latest_resolvable_version_string(requirement:)
|
61
|
+
@latest_resolvable_version_string ||= {}
|
62
|
+
if @latest_resolvable_version_string.key?(requirement)
|
63
|
+
return @latest_resolvable_version_string[requirement]
|
64
|
+
end
|
69
65
|
|
70
|
-
|
71
|
-
@latest_resolvable_version_string ||=
|
66
|
+
@latest_resolvable_version_string[requirement] ||=
|
72
67
|
SharedHelpers.in_a_temporary_directory do
|
73
68
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
74
|
-
write_temporary_dependency_files
|
69
|
+
write_temporary_dependency_files(updated_req: requirement)
|
75
70
|
install_required_python
|
76
71
|
|
77
72
|
# Shell out to Pipenv, which handles everything for us.
|
@@ -87,9 +82,6 @@ module Dependabot
|
|
87
82
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
88
83
|
handle_pipenv_errors(e)
|
89
84
|
end
|
90
|
-
return unless @latest_resolvable_version_string
|
91
|
-
|
92
|
-
Python::Version.new(@latest_resolvable_version_string)
|
93
85
|
end
|
94
86
|
|
95
87
|
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
@@ -252,7 +244,8 @@ module Dependabot
|
|
252
244
|
msg.gsub(/http.*?(?=\s)/, "<redacted>")
|
253
245
|
end
|
254
246
|
|
255
|
-
def write_temporary_dependency_files(
|
247
|
+
def write_temporary_dependency_files(updated_req: nil,
|
248
|
+
update_pipfile: true)
|
256
249
|
dependency_files.each do |file|
|
257
250
|
path = file.name
|
258
251
|
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
@@ -273,11 +266,12 @@ module Dependabot
|
|
273
266
|
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
274
267
|
File.write(path, "[metadata]\nname = sanitized-package\n")
|
275
268
|
end
|
269
|
+
return unless update_pipfile
|
276
270
|
|
277
271
|
# Overwrite the pipfile with updated content
|
278
272
|
File.write(
|
279
273
|
"Pipfile",
|
280
|
-
pipfile_content(
|
274
|
+
pipfile_content(updated_requirement: updated_req)
|
281
275
|
)
|
282
276
|
end
|
283
277
|
|
@@ -312,12 +306,10 @@ module Dependabot
|
|
312
306
|
dependency_files.find { |f| f.name == config_name }
|
313
307
|
end
|
314
308
|
|
315
|
-
def pipfile_content(
|
309
|
+
def pipfile_content(updated_requirement:)
|
316
310
|
content = pipfile.content
|
317
|
-
return content unless update_pipfile
|
318
|
-
|
319
311
|
content = freeze_other_dependencies(content)
|
320
|
-
content =
|
312
|
+
content = set_target_dependency_req(content, updated_requirement)
|
321
313
|
content = add_private_sources(content)
|
322
314
|
content
|
323
315
|
end
|
@@ -328,7 +320,9 @@ module Dependabot
|
|
328
320
|
freeze_top_level_dependencies_except([dependency])
|
329
321
|
end
|
330
322
|
|
331
|
-
def
|
323
|
+
def set_target_dependency_req(pipfile_content, updated_requirement)
|
324
|
+
return pipfile_content unless updated_requirement
|
325
|
+
|
332
326
|
pipfile_object = TomlRB.parse(pipfile_content)
|
333
327
|
|
334
328
|
%w(packages dev-packages).each do |type|
|
@@ -337,11 +331,9 @@ module Dependabot
|
|
337
331
|
next unless pkg_name
|
338
332
|
|
339
333
|
if pipfile_object.dig(type, pkg_name).is_a?(Hash)
|
340
|
-
pipfile_object[type][pkg_name]["version"] =
|
341
|
-
updated_version_requirement_string
|
334
|
+
pipfile_object[type][pkg_name]["version"] = updated_requirement
|
342
335
|
else
|
343
|
-
pipfile_object[type][pkg_name] =
|
344
|
-
updated_version_requirement_string
|
336
|
+
pipfile_object[type][pkg_name] = updated_requirement
|
345
337
|
end
|
346
338
|
end
|
347
339
|
|
@@ -445,40 +437,6 @@ module Dependabot
|
|
445
437
|
end
|
446
438
|
end
|
447
439
|
|
448
|
-
def updated_version_requirement_string
|
449
|
-
lower_bound_req = updated_version_req_lower_bound
|
450
|
-
|
451
|
-
# Add the latest_allowable_version as an upper bound. This means
|
452
|
-
# ignore conditions are considered when checking for the latest
|
453
|
-
# resolvable version.
|
454
|
-
#
|
455
|
-
# NOTE: This isn't perfect. If v2.x is ignored and v3 is out but
|
456
|
-
# unresolvable then the `latest_allowable_version` will be v3, and
|
457
|
-
# we won't be ignoring v2.x releases like we should be.
|
458
|
-
return lower_bound_req if latest_allowable_version.nil?
|
459
|
-
unless Python::Version.correct?(latest_allowable_version)
|
460
|
-
return lower_bound_req
|
461
|
-
end
|
462
|
-
|
463
|
-
lower_bound_req + ", <= #{latest_allowable_version}"
|
464
|
-
end
|
465
|
-
|
466
|
-
def updated_version_req_lower_bound
|
467
|
-
if dependency.version
|
468
|
-
">= #{dependency.version}"
|
469
|
-
else
|
470
|
-
version_for_requirement =
|
471
|
-
dependency.requirements.map { |r| r[:requirement] }.compact.
|
472
|
-
reject { |req_string| req_string.start_with?("<") }.
|
473
|
-
select { |req_string| req_string.match?(VERSION_REGEX) }.
|
474
|
-
map { |req_string| req_string.match(VERSION_REGEX) }.
|
475
|
-
select { |version| Gem::Version.correct?(version) }.
|
476
|
-
max_by { |version| Gem::Version.new(version) }
|
477
|
-
|
478
|
-
">= #{version_for_requirement || 0}"
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
440
|
def run_command(command, env: {})
|
483
441
|
start = Time.now
|
484
442
|
command = SharedHelpers.escape_command(command)
|
@@ -14,46 +14,39 @@ require "dependabot/python/native_helpers"
|
|
14
14
|
require "dependabot/python/python_versions"
|
15
15
|
require "dependabot/python/authed_url_builder"
|
16
16
|
|
17
|
-
# rubocop:disable Metrics/ClassLength
|
18
17
|
module Dependabot
|
19
18
|
module Python
|
20
19
|
class UpdateChecker
|
21
20
|
# This class does version resolution for pyproject.toml files.
|
22
21
|
class PoetryVersionResolver
|
23
|
-
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
|
24
|
-
|
25
22
|
attr_reader :dependency, :dependency_files, :credentials
|
26
23
|
|
27
|
-
def initialize(dependency:, dependency_files:, credentials
|
28
|
-
unlock_requirement:, latest_allowable_version:)
|
24
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
29
25
|
@dependency = dependency
|
30
26
|
@dependency_files = dependency_files
|
31
27
|
@credentials = credentials
|
32
|
-
@latest_allowable_version = latest_allowable_version
|
33
|
-
@unlock_requirement = unlock_requirement
|
34
28
|
|
35
29
|
check_private_sources_are_reachable
|
36
30
|
end
|
37
31
|
|
38
|
-
def latest_resolvable_version
|
39
|
-
|
32
|
+
def latest_resolvable_version(requirement: nil)
|
33
|
+
version_string =
|
34
|
+
fetch_latest_resolvable_version_string(requirement: requirement)
|
40
35
|
|
41
|
-
|
42
|
-
@latest_resolvable_version ||= fetch_latest_resolvable_version
|
36
|
+
version_string.nil? ? nil : Python::Version.new(version_string)
|
43
37
|
end
|
44
38
|
|
45
39
|
private
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
def fetch_latest_resolvable_version_string(requirement:)
|
42
|
+
@latest_resolvable_version_string ||= {}
|
43
|
+
if @latest_resolvable_version_string.key?(requirement)
|
44
|
+
return @latest_resolvable_version_string[requirement]
|
45
|
+
end
|
52
46
|
|
53
|
-
|
54
|
-
latest_resolvable_version_string =
|
47
|
+
@latest_resolvable_version_string[requirement] ||=
|
55
48
|
SharedHelpers.in_a_temporary_directory do
|
56
|
-
write_temporary_dependency_files
|
49
|
+
write_temporary_dependency_files(updated_req: requirement)
|
57
50
|
|
58
51
|
if python_version && !pre_installed_python?(python_version)
|
59
52
|
run_poetry_command("pyenv install -s #{python_version}")
|
@@ -77,9 +70,6 @@ module Dependabot
|
|
77
70
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
78
71
|
handle_poetry_errors(e)
|
79
72
|
end
|
80
|
-
return unless latest_resolvable_version_string
|
81
|
-
|
82
|
-
Python::Version.new(latest_resolvable_version_string)
|
83
73
|
end
|
84
74
|
|
85
75
|
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
@@ -131,7 +121,8 @@ module Dependabot
|
|
131
121
|
message.gsub(/http.*?(?=\s)/, "<redacted>")
|
132
122
|
end
|
133
123
|
|
134
|
-
def write_temporary_dependency_files(
|
124
|
+
def write_temporary_dependency_files(updated_req: nil,
|
125
|
+
update_pyproject: true)
|
135
126
|
dependency_files.each do |file|
|
136
127
|
path = file.name
|
137
128
|
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
@@ -143,7 +134,10 @@ module Dependabot
|
|
143
134
|
|
144
135
|
# Overwrite the pyproject with updated content
|
145
136
|
if update_pyproject
|
146
|
-
File.write(
|
137
|
+
File.write(
|
138
|
+
"pyproject.toml",
|
139
|
+
updated_pyproject_content(updated_requirement: updated_req)
|
140
|
+
)
|
147
141
|
else
|
148
142
|
File.write("pyproject.toml", sanitized_pyproject_content)
|
149
143
|
end
|
@@ -191,15 +185,12 @@ module Dependabot
|
|
191
185
|
PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.include?(version)
|
192
186
|
end
|
193
187
|
|
194
|
-
def updated_pyproject_content
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
content = unlock_target_dependency(content) if unlock_requirement?
|
201
|
-
content
|
202
|
-
end
|
188
|
+
def updated_pyproject_content(updated_requirement:)
|
189
|
+
content = pyproject.content
|
190
|
+
content = sanitize_pyproject_content(content)
|
191
|
+
content = freeze_other_dependencies(content)
|
192
|
+
content = set_target_dependency_req(content, updated_requirement)
|
193
|
+
content
|
203
194
|
end
|
204
195
|
|
205
196
|
def sanitized_pyproject_content
|
@@ -220,7 +211,9 @@ module Dependabot
|
|
220
211
|
freeze_top_level_dependencies_except([dependency])
|
221
212
|
end
|
222
213
|
|
223
|
-
def
|
214
|
+
def set_target_dependency_req(pyproject_content, updated_requirement)
|
215
|
+
return pyproject_content unless updated_requirement
|
216
|
+
|
224
217
|
pyproject_object = TomlRB.parse(pyproject_content)
|
225
218
|
poetry_object = pyproject_object.dig("tool", "poetry")
|
226
219
|
|
@@ -230,11 +223,9 @@ module Dependabot
|
|
230
223
|
next unless pkg_name
|
231
224
|
|
232
225
|
if poetry_object.dig(type, pkg_name).is_a?(Hash)
|
233
|
-
poetry_object[type][pkg_name]["version"] =
|
234
|
-
updated_version_requirement_string
|
226
|
+
poetry_object[type][pkg_name]["version"] = updated_requirement
|
235
227
|
else
|
236
|
-
poetry_object[type][pkg_name] =
|
237
|
-
updated_version_requirement_string
|
228
|
+
poetry_object[type][pkg_name] = updated_requirement
|
238
229
|
end
|
239
230
|
end
|
240
231
|
|
@@ -266,40 +257,6 @@ module Dependabot
|
|
266
257
|
end
|
267
258
|
end
|
268
259
|
|
269
|
-
def updated_version_requirement_string
|
270
|
-
lower_bound_req = updated_version_req_lower_bound
|
271
|
-
|
272
|
-
# Add the latest_allowable_version as an upper bound. This means
|
273
|
-
# ignore conditions are considered when checking for the latest
|
274
|
-
# resolvable version.
|
275
|
-
#
|
276
|
-
# NOTE: This isn't perfect. If v2.x is ignored and v3 is out but
|
277
|
-
# unresolvable then the `latest_allowable_version` will be v3, and
|
278
|
-
# we won't be ignoring v2.x releases like we should be.
|
279
|
-
return lower_bound_req if latest_allowable_version.nil?
|
280
|
-
unless Python::Version.correct?(latest_allowable_version)
|
281
|
-
return lower_bound_req
|
282
|
-
end
|
283
|
-
|
284
|
-
lower_bound_req + ", <= #{latest_allowable_version}"
|
285
|
-
end
|
286
|
-
|
287
|
-
def updated_version_req_lower_bound
|
288
|
-
if dependency.version
|
289
|
-
">= #{dependency.version}"
|
290
|
-
else
|
291
|
-
version_for_requirement =
|
292
|
-
dependency.requirements.map { |r| r[:requirement] }.compact.
|
293
|
-
reject { |req_string| req_string.start_with?("<") }.
|
294
|
-
select { |req_string| req_string.match?(VERSION_REGEX) }.
|
295
|
-
map { |req_string| req_string.match(VERSION_REGEX) }.
|
296
|
-
select { |version| Gem::Version.correct?(version) }.
|
297
|
-
max_by { |version| Gem::Version.new(version) }
|
298
|
-
|
299
|
-
">= #{version_for_requirement || 0}"
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
260
|
def pyproject
|
304
261
|
dependency_files.find { |f| f.name == "pyproject.toml" }
|
305
262
|
end
|
@@ -372,4 +329,3 @@ module Dependabot
|
|
372
329
|
end
|
373
330
|
end
|
374
331
|
end
|
375
|
-
# rubocop:enable Metrics/ClassLength
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-python
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.104.
|
4
|
+
version: 0.104.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.104.
|
19
|
+
version: 0.104.6
|
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.104.
|
26
|
+
version: 0.104.6
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: byebug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|