dependabot-python 0.234.0 → 0.236.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/lib/parser.py +5 -0
- data/lib/dependabot/python/file_fetcher.rb +68 -52
- data/lib/dependabot/python/file_parser/pyproject_files_parser.rb +23 -6
- data/lib/dependabot/python/file_parser/python_requirement_parser.rb +1 -15
- data/lib/dependabot/python/file_parser.rb +25 -20
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +53 -42
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +7 -19
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +38 -33
- data/lib/dependabot/python/file_updater/pyproject_preparer.rb +2 -0
- data/lib/dependabot/python/file_updater/requirement_file_updater.rb +3 -2
- data/lib/dependabot/python/pip_compile_file_matcher.rb +32 -0
- data/lib/dependabot/python/requirement_parser.rb +2 -2
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +72 -81
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +7 -17
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +1 -2
- data/lib/dependabot/python.rb +1 -1
- metadata +6 -6
- data/lib/dependabot/python/helpers.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9f6ef3950d4558611300b72d3839af555ea94e389aeb114a1257290901419e8
|
4
|
+
data.tar.gz: c896fc1ec74464344a70a3bf00add5181424b599c4553bf1d8dcb1e9708d8fcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c25f7375610f6be67b98731d756af8c16b551971dfef4661f59e48fd3b8cfb09ac962b60458bcb99ddcc40c58a6246c5e95062eb9d3724d0d7d93be5860e71a
|
7
|
+
data.tar.gz: 54538b41299885f7187c6c3fe8d2967eb2bf6001fdc18dfb897cc1b70841060de2b48f834e480406d4e8c595c5897d734856e549e9cfe04d820a66dee8e39c09
|
data/helpers/lib/parser.py
CHANGED
@@ -105,6 +105,11 @@ def parse_requirements(directory):
|
|
105
105
|
|
106
106
|
pattern = r"-[cr] (.*) \(line \d+\)"
|
107
107
|
abs_path = re.search(pattern, install_req.comes_from).group(1)
|
108
|
+
|
109
|
+
# Ignore dependencies from remote constraint files
|
110
|
+
if not os.path.isfile(abs_path):
|
111
|
+
continue
|
112
|
+
|
108
113
|
rel_path = os.path.relpath(abs_path, directory)
|
109
114
|
|
110
115
|
requirement_packages.append({
|
@@ -6,6 +6,7 @@ require "toml-rb"
|
|
6
6
|
require "dependabot/file_fetchers"
|
7
7
|
require "dependabot/file_fetchers/base"
|
8
8
|
require "dependabot/python/language_version_manager"
|
9
|
+
require "dependabot/python/pip_compile_file_matcher"
|
9
10
|
require "dependabot/python/requirement_parser"
|
10
11
|
require "dependabot/python/file_parser/pyproject_files_parser"
|
11
12
|
require "dependabot/python/file_parser/python_requirement_parser"
|
@@ -75,7 +76,7 @@ module Dependabot
|
|
75
76
|
|
76
77
|
fetched_files << setup_file if setup_file
|
77
78
|
fetched_files << setup_cfg_file if setup_cfg_file
|
78
|
-
fetched_files +=
|
79
|
+
fetched_files += project_files
|
79
80
|
fetched_files << pip_conf if pip_conf
|
80
81
|
fetched_files << python_version_file if python_version_file
|
81
82
|
|
@@ -113,8 +114,7 @@ module Dependabot
|
|
113
114
|
pipfile ||
|
114
115
|
pyproject
|
115
116
|
|
116
|
-
path =
|
117
|
-
.cleanpath.to_path
|
117
|
+
path = cleanpath(File.join(directory, "requirements.txt"))
|
118
118
|
raise Dependabot::DependencyFileNotFound, path
|
119
119
|
end
|
120
120
|
|
@@ -269,7 +269,7 @@ module Dependabot
|
|
269
269
|
|
270
270
|
paths.flat_map do |path|
|
271
271
|
path = File.join(current_dir, path) unless current_dir == "."
|
272
|
-
path =
|
272
|
+
path = cleanpath(path)
|
273
273
|
|
274
274
|
next if previously_fetched_files.map(&:name).include?(path)
|
275
275
|
next if file.name == path
|
@@ -293,43 +293,47 @@ module Dependabot
|
|
293
293
|
|
294
294
|
paths.map do |path|
|
295
295
|
path = File.join(current_dir, path) unless current_dir == "."
|
296
|
-
|
296
|
+
cleanpath(path)
|
297
297
|
end
|
298
298
|
end.flatten.uniq
|
299
299
|
|
300
300
|
constraints_paths.map { |path| fetch_file_from_host(path) }
|
301
301
|
end
|
302
302
|
|
303
|
-
def
|
304
|
-
|
305
|
-
|
303
|
+
def project_files
|
304
|
+
project_files = []
|
305
|
+
unfetchable_deps = []
|
306
306
|
|
307
|
-
|
308
|
-
|
307
|
+
path_dependencies.each do |dep|
|
308
|
+
path = dep[:path]
|
309
|
+
project_files += fetch_project_file(path)
|
309
310
|
rescue Dependabot::DependencyFileNotFound => e
|
310
|
-
|
311
|
+
unfetchable_deps << if sdist_or_wheel?(path)
|
312
|
+
e.file_path.gsub(%r{^/}, "")
|
313
|
+
else
|
314
|
+
"\"#{dep[:name]}\" at #{cleanpath(File.join(directory, dep[:file]))}"
|
315
|
+
end
|
311
316
|
end
|
312
317
|
|
313
|
-
|
314
|
-
|
318
|
+
poetry_path_dependencies.each do |path|
|
319
|
+
project_files += fetch_project_file(path)
|
315
320
|
rescue Dependabot::DependencyFileNotFound => e
|
316
|
-
|
321
|
+
unfetchable_deps << e.file_path.gsub(%r{^/}, "")
|
317
322
|
end
|
318
323
|
|
319
|
-
raise Dependabot::PathDependenciesNotReachable,
|
324
|
+
raise Dependabot::PathDependenciesNotReachable, unfetchable_deps if unfetchable_deps.any?
|
320
325
|
|
321
|
-
|
326
|
+
project_files
|
322
327
|
end
|
323
328
|
|
324
|
-
def
|
325
|
-
|
329
|
+
def fetch_project_file(path)
|
330
|
+
project_files = []
|
331
|
+
|
332
|
+
path = cleanpath(File.join(path, "setup.py")) unless sdist_or_wheel?(path)
|
326
333
|
|
327
|
-
unless path.end_with?(".tar.gz", ".whl", ".zip")
|
328
|
-
path = Pathname.new(File.join(path, "setup.py")).cleanpath.to_path
|
329
|
-
end
|
330
334
|
return [] if path == "setup.py" && setup_file
|
331
335
|
|
332
|
-
|
336
|
+
project_files <<
|
333
337
|
begin
|
334
338
|
fetch_file_from_host(
|
335
339
|
path,
|
@@ -337,19 +341,20 @@ module Dependabot
|
|
337
341
|
).tap { |f| f.support_file = true }
|
338
342
|
rescue Dependabot::DependencyFileNotFound
|
339
343
|
# For projects with pyproject.toml attempt to fetch a pyproject.toml
|
340
|
-
# at the given path instead of a setup.py.
|
341
|
-
# setup.py to be present, so if none can be found, simply return
|
342
|
-
return [] unless allow_pyproject
|
343
|
-
|
344
|
+
# at the given path instead of a setup.py.
|
344
345
|
fetch_file_from_host(
|
345
346
|
path.gsub("setup.py", "pyproject.toml"),
|
346
347
|
fetch_submodules: true
|
347
348
|
).tap { |f| f.support_file = true }
|
348
349
|
end
|
349
350
|
|
350
|
-
return
|
351
|
+
return project_files unless path.end_with?(".py")
|
351
352
|
|
352
|
-
|
353
|
+
project_files + cfg_files_for_setup_py(path)
|
354
|
+
end
|
355
|
+
|
356
|
+
def sdist_or_wheel?(path)
|
357
|
+
path.end_with?(".tar.gz", ".whl", ".zip")
|
353
358
|
end
|
354
359
|
|
355
360
|
def cfg_files_for_setup_py(path)
|
@@ -378,60 +383,63 @@ module Dependabot
|
|
378
383
|
end
|
379
384
|
end
|
380
385
|
|
381
|
-
def
|
382
|
-
|
383
|
-
|
384
|
-
|
386
|
+
def path_dependencies
|
387
|
+
requirement_txt_path_dependencies +
|
388
|
+
requirement_in_path_dependencies +
|
389
|
+
pipfile_path_dependencies
|
385
390
|
end
|
386
391
|
|
387
|
-
def
|
392
|
+
def requirement_txt_path_dependencies
|
388
393
|
(requirements_txt_files + child_requirement_txt_files)
|
389
|
-
.map { |req_file|
|
390
|
-
.flatten.uniq
|
394
|
+
.map { |req_file| parse_requirement_path_dependencies(req_file) }
|
395
|
+
.flatten.uniq { |dep| dep[:path] }
|
391
396
|
end
|
392
397
|
|
393
|
-
def
|
398
|
+
def requirement_in_path_dependencies
|
394
399
|
requirements_in_files
|
395
|
-
.map { |req_file|
|
396
|
-
.flatten.uniq
|
400
|
+
.map { |req_file| parse_requirement_path_dependencies(req_file) }
|
401
|
+
.flatten.uniq { |dep| dep[:path] }
|
397
402
|
end
|
398
403
|
|
399
|
-
def
|
404
|
+
def parse_requirement_path_dependencies(req_file)
|
405
|
+
# If this is a pip-compile lockfile, rely on whatever path dependencies we found in the main manifest
|
406
|
+
return [] if pip_compile_file_matcher.lockfile_for_pip_compile_file?(req_file)
|
407
|
+
|
400
408
|
uneditable_reqs =
|
401
409
|
req_file.content
|
402
|
-
.scan(
|
403
|
-
.
|
404
|
-
|
405
|
-
|
410
|
+
.scan(/(?<name>^['"]?(?:file:)?(?<path>\..*?)(?=\[|#|'|"|$))/)
|
411
|
+
.filter_map do |n, p|
|
412
|
+
{ name: n.strip, path: p.strip, file: req_file.name } unless p.include?("://")
|
413
|
+
end
|
406
414
|
|
407
415
|
editable_reqs =
|
408
416
|
req_file.content
|
409
|
-
.scan(
|
410
|
-
.
|
411
|
-
|
412
|
-
|
417
|
+
.scan(/(?<name>^(?:-e)\s+['"]?(?:file:)?(?<path>.*?)(?=\[|#|'|"|$))/)
|
418
|
+
.filter_map do |n, p|
|
419
|
+
{ name: n.strip, path: p.strip, file: req_file.name } unless p.include?("://") || p.include?("git@")
|
420
|
+
end
|
413
421
|
|
414
422
|
uneditable_reqs + editable_reqs
|
415
423
|
end
|
416
424
|
|
417
|
-
def
|
425
|
+
def pipfile_path_dependencies
|
418
426
|
return [] unless pipfile
|
419
427
|
|
420
|
-
|
428
|
+
deps = []
|
421
429
|
DEPENDENCY_TYPES.each do |dep_type|
|
422
430
|
next unless parsed_pipfile[dep_type]
|
423
431
|
|
424
432
|
parsed_pipfile[dep_type].each do |_, req|
|
425
433
|
next unless req.is_a?(Hash) && req["path"]
|
426
434
|
|
427
|
-
|
435
|
+
deps << { name: req["path"], path: req["path"], file: pipfile.name }
|
428
436
|
end
|
429
437
|
end
|
430
438
|
|
431
|
-
|
439
|
+
deps
|
432
440
|
end
|
433
441
|
|
434
|
-
def
|
442
|
+
def poetry_path_dependencies
|
435
443
|
return [] unless pyproject
|
436
444
|
|
437
445
|
paths = []
|
@@ -447,6 +455,14 @@ module Dependabot
|
|
447
455
|
|
448
456
|
paths
|
449
457
|
end
|
458
|
+
|
459
|
+
def cleanpath(path)
|
460
|
+
Pathname.new(path).cleanpath.to_path
|
461
|
+
end
|
462
|
+
|
463
|
+
def pip_compile_file_matcher
|
464
|
+
@pip_compile_file_matcher ||= PipCompileFileMatcher.new(requirements_in_files)
|
465
|
+
end
|
450
466
|
end
|
451
467
|
end
|
452
468
|
end
|
@@ -8,7 +8,6 @@ require "dependabot/file_parsers/base/dependency_set"
|
|
8
8
|
require "dependabot/python/file_parser"
|
9
9
|
require "dependabot/python/requirement"
|
10
10
|
require "dependabot/errors"
|
11
|
-
require "dependabot/python/helpers"
|
12
11
|
require "dependabot/python/name_normaliser"
|
13
12
|
|
14
13
|
module Dependabot
|
@@ -28,7 +27,7 @@ module Dependabot
|
|
28
27
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
29
28
|
|
30
29
|
dependency_set += pyproject_dependencies if using_poetry? || using_pep621?
|
31
|
-
dependency_set += lockfile_dependencies if lockfile
|
30
|
+
dependency_set += lockfile_dependencies if using_poetry? && lockfile
|
32
31
|
|
33
32
|
dependency_set
|
34
33
|
end
|
@@ -39,6 +38,16 @@ module Dependabot
|
|
39
38
|
|
40
39
|
def pyproject_dependencies
|
41
40
|
if using_poetry?
|
41
|
+
missing_keys = missing_poetry_keys
|
42
|
+
|
43
|
+
if missing_keys.any?
|
44
|
+
raise DependencyFileNotParseable.new(
|
45
|
+
pyproject.path,
|
46
|
+
"#{pyproject.path} is missing the following sections:\n" \
|
47
|
+
" * #{missing_keys.map { |key| "tool.poetry.#{key}" }.join("\n * ")}\n"
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
42
51
|
poetry_dependencies
|
43
52
|
else
|
44
53
|
pep621_dependencies
|
@@ -53,11 +62,11 @@ module Dependabot
|
|
53
62
|
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
54
63
|
|
55
64
|
POETRY_DEPENDENCY_TYPES.each do |type|
|
56
|
-
deps_hash =
|
65
|
+
deps_hash = poetry_root[type] || {}
|
57
66
|
dependencies += parse_poetry_dependency_group(type, deps_hash)
|
58
67
|
end
|
59
68
|
|
60
|
-
groups =
|
69
|
+
groups = poetry_root["group"] || {}
|
61
70
|
groups.each do |group, group_spec|
|
62
71
|
dependencies += parse_poetry_dependency_group(group, group_spec["dependencies"])
|
63
72
|
end
|
@@ -138,7 +147,11 @@ module Dependabot
|
|
138
147
|
end
|
139
148
|
|
140
149
|
def using_poetry?
|
141
|
-
!
|
150
|
+
!poetry_root.nil?
|
151
|
+
end
|
152
|
+
|
153
|
+
def missing_poetry_keys
|
154
|
+
%w(name version description authors).reject { |key| poetry_root.key?(key) }
|
142
155
|
end
|
143
156
|
|
144
157
|
def using_pep621?
|
@@ -146,6 +159,10 @@ module Dependabot
|
|
146
159
|
!parsed_pyproject.dig("project", "optional-dependencies").nil?
|
147
160
|
end
|
148
161
|
|
162
|
+
def poetry_root
|
163
|
+
parsed_pyproject.dig("tool", "poetry")
|
164
|
+
end
|
165
|
+
|
149
166
|
def using_pdm?
|
150
167
|
using_pep621? && pdm_lock
|
151
168
|
end
|
@@ -187,7 +204,7 @@ module Dependabot
|
|
187
204
|
File.write(lockfile.name, lockfile.content)
|
188
205
|
|
189
206
|
begin
|
190
|
-
output =
|
207
|
+
output = SharedHelpers.run_shell_command("pyenv exec poetry show --only main")
|
191
208
|
|
192
209
|
output.split("\n").map { |line| line.split.first }
|
193
210
|
rescue SharedHelpers::HelperSubprocessFailed
|
@@ -103,21 +103,7 @@ module Dependabot
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def run_command(command, env: {})
|
106
|
-
|
107
|
-
command = SharedHelpers.escape_command(command)
|
108
|
-
stdout, process = Open3.capture2e(env, command)
|
109
|
-
time_taken = Time.now - start
|
110
|
-
|
111
|
-
return stdout if process.success?
|
112
|
-
|
113
|
-
raise SharedHelpers::HelperSubprocessFailed.new(
|
114
|
-
message: stdout,
|
115
|
-
error_context: {
|
116
|
-
command: command,
|
117
|
-
time_taken: time_taken,
|
118
|
-
process_exit_value: process.to_s
|
119
|
-
}
|
120
|
-
)
|
106
|
+
SharedHelpers.run_shell_command(command, env: env, stderr_to_stdout: true)
|
121
107
|
end
|
122
108
|
|
123
109
|
def requirement_class
|
@@ -10,6 +10,7 @@ require "dependabot/python/requirement"
|
|
10
10
|
require "dependabot/errors"
|
11
11
|
require "dependabot/python/native_helpers"
|
12
12
|
require "dependabot/python/name_normaliser"
|
13
|
+
require "dependabot/python/pip_compile_file_matcher"
|
13
14
|
|
14
15
|
module Dependabot
|
15
16
|
module Python
|
@@ -74,21 +75,29 @@ module Dependabot
|
|
74
75
|
# probably blocked. Ignore it.
|
75
76
|
next if blocking_marker?(dep)
|
76
77
|
|
78
|
+
name = dep["name"]
|
79
|
+
file = dep["file"]
|
80
|
+
version = dep["version"]
|
81
|
+
original_file = get_original_file(file)
|
82
|
+
|
77
83
|
requirements =
|
78
|
-
if lockfile_for_pip_compile_file?(
|
84
|
+
if original_file && pip_compile_file_matcher.lockfile_for_pip_compile_file?(original_file) then []
|
79
85
|
else
|
80
86
|
[{
|
81
87
|
requirement: dep["requirement"],
|
82
|
-
file: Pathname.new(
|
88
|
+
file: Pathname.new(file).cleanpath.to_path,
|
83
89
|
source: nil,
|
84
|
-
groups: group_from_filename(
|
90
|
+
groups: group_from_filename(file)
|
85
91
|
}]
|
86
92
|
end
|
87
93
|
|
94
|
+
# PyYAML < 6.0 will cause `pip-compile` to fail due to incompatiblity with Cython 3. Workaround it.
|
95
|
+
SharedHelpers.run_shell_command("pyenv exec pip install cython<3.0") if old_pyyaml?(name, version)
|
96
|
+
|
88
97
|
dependencies <<
|
89
98
|
Dependency.new(
|
90
|
-
name: normalised_name(
|
91
|
-
version:
|
99
|
+
name: normalised_name(name, dep["extras"]),
|
100
|
+
version: version&.include?("*") ? nil : version,
|
92
101
|
requirements: requirements,
|
93
102
|
package_manager: "pip"
|
94
103
|
)
|
@@ -96,6 +105,13 @@ module Dependabot
|
|
96
105
|
dependencies
|
97
106
|
end
|
98
107
|
|
108
|
+
def old_pyyaml?(name, version)
|
109
|
+
major_version = version&.split(".")&.first
|
110
|
+
return false unless major_version
|
111
|
+
|
112
|
+
name == "pyyaml" && major_version < "6"
|
113
|
+
end
|
114
|
+
|
99
115
|
def group_from_filename(filename)
|
100
116
|
if filename.include?("dev") then ["dev-dependencies"]
|
101
117
|
else
|
@@ -118,17 +134,6 @@ module Dependabot
|
|
118
134
|
.dependency_set
|
119
135
|
end
|
120
136
|
|
121
|
-
def lockfile_for_pip_compile_file?(filename)
|
122
|
-
return false unless pip_compile_files.any?
|
123
|
-
return false unless filename.end_with?(".txt")
|
124
|
-
|
125
|
-
file = dependency_files.find { |f| f.name == filename }
|
126
|
-
return true if file&.content&.match?(output_file_regex(filename))
|
127
|
-
|
128
|
-
basename = filename.gsub(/\.txt$/, "")
|
129
|
-
pip_compile_files.any? { |f| f.name == basename + ".in" }
|
130
|
-
end
|
131
|
-
|
132
137
|
def parsed_requirement_files
|
133
138
|
SharedHelpers.in_a_temporary_directory do
|
134
139
|
write_temporary_dependency_files
|
@@ -201,10 +206,6 @@ module Dependabot
|
|
201
206
|
@pipfile_lock ||= get_original_file("Pipfile.lock")
|
202
207
|
end
|
203
208
|
|
204
|
-
def output_file_regex(filename)
|
205
|
-
"--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
|
206
|
-
end
|
207
|
-
|
208
209
|
def pyproject
|
209
210
|
@pyproject ||= get_original_file("pyproject.toml")
|
210
211
|
end
|
@@ -225,6 +226,10 @@ module Dependabot
|
|
225
226
|
@pip_compile_files ||=
|
226
227
|
dependency_files.select { |f| f.name.end_with?(".in") }
|
227
228
|
end
|
229
|
+
|
230
|
+
def pip_compile_file_matcher
|
231
|
+
@pip_compile_file_matcher ||= PipCompileFileMatcher.new(pip_compile_files)
|
232
|
+
end
|
228
233
|
end
|
229
234
|
end
|
230
235
|
end
|
@@ -27,6 +27,8 @@ module Dependabot
|
|
27
27
|
WARNINGS = /\s*# WARNING:.*\Z/m
|
28
28
|
UNSAFE_NOTE = /\s*# The following packages are considered to be unsafe.*\Z/m
|
29
29
|
RESOLVER_REGEX = /(?<=--resolver=)(\w+)/
|
30
|
+
NATIVE_COMPILATION_ERROR =
|
31
|
+
"pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1"
|
30
32
|
|
31
33
|
attr_reader :dependencies, :dependency_files, :credentials
|
32
34
|
|
@@ -34,6 +36,7 @@ module Dependabot
|
|
34
36
|
@dependencies = dependencies
|
35
37
|
@dependency_files = dependency_files
|
36
38
|
@credentials = credentials
|
39
|
+
@build_isolation = true
|
37
40
|
end
|
38
41
|
|
39
42
|
def updated_dependency_files
|
@@ -67,28 +70,7 @@ module Dependabot
|
|
67
70
|
language_version_manager.install_required_python
|
68
71
|
|
69
72
|
filenames_to_compile.each do |filename|
|
70
|
-
|
71
|
-
# This is slow, as pip-compile needs to do installs.
|
72
|
-
options = pip_compile_options(filename)
|
73
|
-
options_fingerprint = pip_compile_options_fingerprint(options)
|
74
|
-
|
75
|
-
name_part = "pyenv exec pip-compile " \
|
76
|
-
"#{options} -P " \
|
77
|
-
"#{dependency.name}"
|
78
|
-
fingerprint_name_part = "pyenv exec pip-compile " \
|
79
|
-
"#{options_fingerprint} -P " \
|
80
|
-
"<dependency_name>"
|
81
|
-
|
82
|
-
version_part = "#{dependency.version} #{filename}"
|
83
|
-
fingerprint_version_part = "<dependency_version> <filename>"
|
84
|
-
|
85
|
-
# Don't escape pyenv `dep-name==version` syntax
|
86
|
-
run_pip_compile_command(
|
87
|
-
"#{SharedHelpers.escape_command(name_part)}==" \
|
88
|
-
"#{SharedHelpers.escape_command(version_part)}",
|
89
|
-
allow_unsafe_shell_command: true,
|
90
|
-
fingerprint: "#{fingerprint_name_part}==#{fingerprint_version_part}"
|
91
|
-
)
|
73
|
+
compile_file(filename)
|
92
74
|
end
|
93
75
|
|
94
76
|
# Remove any .python-version file before parsing the reqs
|
@@ -108,6 +90,44 @@ module Dependabot
|
|
108
90
|
end
|
109
91
|
end
|
110
92
|
|
93
|
+
def compile_file(filename)
|
94
|
+
# Shell out to pip-compile, generate a new set of requirements.
|
95
|
+
# This is slow, as pip-compile needs to do installs.
|
96
|
+
options = pip_compile_options(filename)
|
97
|
+
options_fingerprint = pip_compile_options_fingerprint(options)
|
98
|
+
|
99
|
+
name_part = "pyenv exec pip-compile " \
|
100
|
+
"#{options} -P " \
|
101
|
+
"#{dependency.name}"
|
102
|
+
fingerprint_name_part = "pyenv exec pip-compile " \
|
103
|
+
"#{options_fingerprint} -P " \
|
104
|
+
"<dependency_name>"
|
105
|
+
|
106
|
+
version_part = "#{dependency.version} #{filename}"
|
107
|
+
fingerprint_version_part = "<dependency_version> <filename>"
|
108
|
+
|
109
|
+
# Don't escape pyenv `dep-name==version` syntax
|
110
|
+
run_pip_compile_command(
|
111
|
+
"#{SharedHelpers.escape_command(name_part)}==" \
|
112
|
+
"#{SharedHelpers.escape_command(version_part)}",
|
113
|
+
allow_unsafe_shell_command: true,
|
114
|
+
fingerprint: "#{fingerprint_name_part}==#{fingerprint_version_part}"
|
115
|
+
)
|
116
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
117
|
+
retry_count ||= 0
|
118
|
+
retry_count += 1
|
119
|
+
if compilation_error?(e) && retry_count <= 1
|
120
|
+
@build_isolation = false
|
121
|
+
retry
|
122
|
+
end
|
123
|
+
|
124
|
+
raise
|
125
|
+
end
|
126
|
+
|
127
|
+
def compilation_error?(error)
|
128
|
+
error.message.include?(NATIVE_COMPILATION_ERROR)
|
129
|
+
end
|
130
|
+
|
111
131
|
def update_manifest_files
|
112
132
|
dependency_files.filter_map do |file|
|
113
133
|
next unless file.name.end_with?(".in")
|
@@ -146,30 +166,21 @@ module Dependabot
|
|
146
166
|
end
|
147
167
|
|
148
168
|
def run_command(cmd, env: python_env, allow_unsafe_shell_command: false, fingerprint:)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
return stdout if process.success?
|
169
|
+
SharedHelpers.run_shell_command(
|
170
|
+
cmd,
|
171
|
+
env: env,
|
172
|
+
allow_unsafe_shell_command: allow_unsafe_shell_command,
|
173
|
+
fingerprint: fingerprint,
|
174
|
+
stderr_to_stdout: true
|
175
|
+
)
|
176
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
177
|
+
stdout = e.message
|
159
178
|
|
160
179
|
if stdout.match?(INCOMPATIBLE_VERSIONS_REGEX)
|
161
180
|
raise DependencyFileNotResolvable, stdout.match(INCOMPATIBLE_VERSIONS_REGEX)
|
162
181
|
end
|
163
182
|
|
164
|
-
raise
|
165
|
-
message: stdout,
|
166
|
-
error_context: {
|
167
|
-
command: command,
|
168
|
-
fingerprint: fingerprint,
|
169
|
-
time_taken: time_taken,
|
170
|
-
process_exit_value: process.to_s
|
171
|
-
}
|
172
|
-
)
|
183
|
+
raise
|
173
184
|
end
|
174
185
|
|
175
186
|
def run_pip_compile_command(command, allow_unsafe_shell_command: false, fingerprint:)
|
@@ -412,7 +423,7 @@ module Dependabot
|
|
412
423
|
end
|
413
424
|
|
414
425
|
def pip_compile_options(filename)
|
415
|
-
options = ["--build-isolation"]
|
426
|
+
options = @build_isolation ? ["--build-isolation"] : ["--no-build-isolation"]
|
416
427
|
options += pip_compile_index_options
|
417
428
|
|
418
429
|
if (requirements_file = compiled_file_for_filename(filename))
|
@@ -245,28 +245,16 @@ module Dependabot
|
|
245
245
|
File.write("dev-req.txt", dev_req_content)
|
246
246
|
end
|
247
247
|
|
248
|
-
def run_command(command, env: {})
|
249
|
-
|
250
|
-
command = SharedHelpers.escape_command(command)
|
251
|
-
stdout, _, process = Open3.capture3(env, command)
|
252
|
-
time_taken = Time.now - start
|
253
|
-
|
254
|
-
# Raise an error with the output from the shell session if Pipenv
|
255
|
-
# returns a non-zero status
|
256
|
-
return stdout if process.success?
|
257
|
-
|
258
|
-
raise SharedHelpers::HelperSubprocessFailed.new(
|
259
|
-
message: stdout,
|
260
|
-
error_context: {
|
261
|
-
command: command,
|
262
|
-
time_taken: time_taken,
|
263
|
-
process_exit_value: process.to_s
|
264
|
-
}
|
265
|
-
)
|
248
|
+
def run_command(command, env: {}, fingerprint: nil)
|
249
|
+
SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint)
|
266
250
|
end
|
267
251
|
|
268
252
|
def run_pipenv_command(command, env: pipenv_env_variables)
|
269
|
-
run_command(
|
253
|
+
run_command(
|
254
|
+
"pyenv local #{language_version_manager.python_major_minor}",
|
255
|
+
fingerprint: "pyenv local <python_major_minor>"
|
256
|
+
)
|
257
|
+
|
270
258
|
run_command(command, env: env)
|
271
259
|
end
|
272
260
|
|
@@ -10,7 +10,6 @@ require "dependabot/python/version"
|
|
10
10
|
require "dependabot/python/requirement"
|
11
11
|
require "dependabot/python/file_parser/python_requirement_parser"
|
12
12
|
require "dependabot/python/file_updater"
|
13
|
-
require "dependabot/python/helpers"
|
14
13
|
require "dependabot/python/native_helpers"
|
15
14
|
require "dependabot/python/name_normaliser"
|
16
15
|
|
@@ -61,34 +60,38 @@ module Dependabot
|
|
61
60
|
end
|
62
61
|
|
63
62
|
def updated_pyproject_content
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
63
|
+
content = pyproject.content
|
64
|
+
return content unless requirement_changed?(pyproject, dependency)
|
65
|
+
|
66
|
+
updated_content = content.dup
|
67
|
+
|
68
|
+
dependency.requirements.zip(dependency.previous_requirements).each do |new_r, old_r|
|
69
|
+
next unless new_r[:file] == pyproject.name && old_r[:file] == pyproject.name
|
70
|
+
|
71
|
+
updated_content = replace_dep(dependency, updated_content, new_r, old_r)
|
72
|
+
end
|
73
|
+
|
74
|
+
raise "Content did not change!" if content == updated_content
|
75
|
+
|
76
|
+
updated_content
|
77
|
+
end
|
78
|
+
|
79
|
+
def replace_dep(dep, content, new_r, old_r)
|
80
|
+
new_req = new_r[:requirement]
|
81
|
+
old_req = old_r[:requirement]
|
82
|
+
|
83
|
+
declaration_regex = declaration_regex(dep, old_r)
|
84
|
+
declaration_match = content.match(declaration_regex)
|
85
|
+
if declaration_match
|
86
|
+
declaration = declaration_match[:declaration]
|
87
|
+
new_declaration = declaration.sub(old_req, new_req)
|
88
|
+
content.sub(declaration, new_declaration)
|
89
|
+
else
|
90
|
+
content.gsub(table_declaration_regex(dep, new_r)) do |match|
|
91
|
+
match.gsub(/(\s*version\s*=\s*["'])#{Regexp.escape(old_req)}/,
|
92
|
+
'\1' + new_req)
|
91
93
|
end
|
94
|
+
end
|
92
95
|
end
|
93
96
|
|
94
97
|
def updated_lockfile_content
|
@@ -204,7 +207,7 @@ module Dependabot
|
|
204
207
|
end
|
205
208
|
|
206
209
|
def run_poetry_command(command, fingerprint: nil)
|
207
|
-
|
210
|
+
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
208
211
|
end
|
209
212
|
|
210
213
|
def write_temporary_dependency_files(pyproject_content)
|
@@ -241,12 +244,14 @@ module Dependabot
|
|
241
244
|
end
|
242
245
|
end
|
243
246
|
|
244
|
-
def declaration_regex(dep)
|
245
|
-
|
247
|
+
def declaration_regex(dep, old_req)
|
248
|
+
group = old_req[:groups].first
|
249
|
+
|
250
|
+
/#{group}(?:\.dependencies)?\]\s*\n.*?(?<declaration>(?:^\s*|["'])#{escape(dep)}["']?\s*=[^\n]*)$/mi
|
246
251
|
end
|
247
252
|
|
248
|
-
def table_declaration_regex(dep)
|
249
|
-
/tool\.poetry
|
253
|
+
def table_declaration_regex(dep, old_req)
|
254
|
+
/tool\.poetry\.#{old_req[:groups].first}\.#{escape(dep)}\]\n.*?\s*version\s* =.*?\n/m
|
250
255
|
end
|
251
256
|
|
252
257
|
def escape(dep)
|
@@ -82,6 +82,8 @@ module Dependabot
|
|
82
82
|
"git" => locked_details&.dig("source", "url"),
|
83
83
|
"rev" => locked_details&.dig("source", "reference")
|
84
84
|
}
|
85
|
+
subdirectory = locked_details&.dig("source", "subdirectory")
|
86
|
+
poetry_object[key][dep_name]["subdirectory"] = subdirectory if subdirectory
|
85
87
|
elsif poetry_object[key][dep_name].is_a?(Hash)
|
86
88
|
poetry_object[key][dep_name]["version"] = locked_version
|
87
89
|
elsif poetry_object[key][dep_name].is_a?(Array)
|
@@ -48,10 +48,11 @@ module Dependabot
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def updated_requirement_or_setup_file_content(new_req, old_req)
|
51
|
-
|
51
|
+
original_file = get_original_file(new_req.fetch(:file))
|
52
|
+
raise "Could not find a dependency file for #{new_req}" unless original_file
|
52
53
|
|
53
54
|
RequirementReplacer.new(
|
54
|
-
content: content,
|
55
|
+
content: original_file.content,
|
55
56
|
dependency_name: dependency.name,
|
56
57
|
old_requirement: old_req.fetch(:requirement),
|
57
58
|
new_requirement: new_req.fetch(:requirement),
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Dependabot
|
5
|
+
module Python
|
6
|
+
class PipCompileFileMatcher
|
7
|
+
def initialize(requirements_in_files)
|
8
|
+
@requirements_in_files = requirements_in_files
|
9
|
+
end
|
10
|
+
|
11
|
+
def lockfile_for_pip_compile_file?(file)
|
12
|
+
return false unless requirements_in_files.any?
|
13
|
+
|
14
|
+
name = file.name
|
15
|
+
return false unless name.end_with?(".txt")
|
16
|
+
|
17
|
+
return true if file.content.match?(output_file_regex(name))
|
18
|
+
|
19
|
+
basename = name.gsub(/\.txt$/, "")
|
20
|
+
requirements_in_files.any? { |f| f.name == basename + ".in" }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :requirements_in_files
|
26
|
+
|
27
|
+
def output_file_regex(filename)
|
28
|
+
"--output-file[=\s]+#{Regexp.escape(filename)}(?:\s|$)"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Dependabot
|
@@ -9,7 +9,7 @@ module Dependabot
|
|
9
9
|
COMPARISON = /===|==|>=|<=|<|>|~=|!=/
|
10
10
|
VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?/
|
11
11
|
|
12
|
-
REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*(?<version>#{VERSION})/
|
12
|
+
REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*v?(?<version>#{VERSION})/
|
13
13
|
HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|\\|$)/
|
14
14
|
REQUIREMENTS = /#{REQUIREMENT}(\s*,\s*\\?\s*#{REQUIREMENT})*/
|
15
15
|
HASHES = /#{HASH}(\s*\\?\s*#{HASH})*/
|
@@ -27,7 +27,7 @@ module Dependabot
|
|
27
27
|
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).* /
|
28
28
|
GIT_REFERENCE_NOT_FOUND_REGEX = /Did not find branch or tag '(?<tag>[^\n"]+)'/m
|
29
29
|
NATIVE_COMPILATION_ERROR =
|
30
|
-
"pip._internal.exceptions.InstallationSubprocessError:
|
30
|
+
"pip._internal.exceptions.InstallationSubprocessError: Getting requirements to build wheel exited with 1"
|
31
31
|
# See https://packaging.python.org/en/latest/tutorials/packaging-projects/#configuring-metadata
|
32
32
|
PYTHON_PACKAGE_NAME_REGEX = /[A-Za-z0-9_\-]+/
|
33
33
|
RESOLUTION_IMPOSSIBLE_ERROR = "ResolutionImpossible"
|
@@ -43,17 +43,21 @@ module Dependabot
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def latest_resolvable_version(requirement: nil)
|
46
|
+
@latest_resolvable_version_string ||= {}
|
47
|
+
return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
|
48
|
+
|
46
49
|
version_string =
|
47
50
|
fetch_latest_resolvable_version_string(requirement: requirement)
|
48
51
|
|
49
|
-
|
52
|
+
@latest_resolvable_version_string[requirement] ||=
|
53
|
+
version_string.nil? ? nil : Python::Version.new(version_string)
|
50
54
|
end
|
51
55
|
|
52
56
|
def resolvable?(version:)
|
53
57
|
@resolvable ||= {}
|
54
58
|
return @resolvable[version] if @resolvable.key?(version)
|
55
59
|
|
56
|
-
@resolvable[version] = if
|
60
|
+
@resolvable[version] = if latest_resolvable_version(requirement: "==#{version}")
|
57
61
|
true
|
58
62
|
else
|
59
63
|
false
|
@@ -63,57 +67,59 @@ module Dependabot
|
|
63
67
|
private
|
64
68
|
|
65
69
|
def fetch_latest_resolvable_version_string(requirement:)
|
66
|
-
|
67
|
-
|
70
|
+
SharedHelpers.in_a_temporary_directory do
|
71
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
72
|
+
write_temporary_dependency_files(updated_req: requirement)
|
73
|
+
language_version_manager.install_required_python
|
68
74
|
|
69
|
-
|
70
|
-
|
71
|
-
SharedHelpers.with_git_configured(credentials: credentials) do
|
72
|
-
write_temporary_dependency_files(updated_req: requirement)
|
73
|
-
language_version_manager.install_required_python
|
74
|
-
|
75
|
-
filenames_to_compile.each do |filename|
|
76
|
-
# Shell out to pip-compile.
|
77
|
-
# This is slow, as pip-compile needs to do installs.
|
78
|
-
options = pip_compile_options(filename)
|
79
|
-
options_fingerprint = pip_compile_options_fingerprint(options)
|
80
|
-
|
81
|
-
run_pip_compile_command(
|
82
|
-
"pyenv exec pip-compile -v #{options} -P #{dependency.name} #{filename}",
|
83
|
-
fingerprint: "pyenv exec pip-compile -v #{options_fingerprint} -P <dependency_name> <filename>"
|
84
|
-
)
|
85
|
-
|
86
|
-
next if dependency.top_level?
|
87
|
-
|
88
|
-
# Run pip-compile a second time for transient dependencies
|
89
|
-
# to make sure we do not update dependencies that are
|
90
|
-
# superfluous. pip-compile does not detect these when
|
91
|
-
# updating a specific dependency with the -P option.
|
92
|
-
# Running pip-compile a second time will automatically remove
|
93
|
-
# superfluous dependencies. Dependabot then marks those with
|
94
|
-
# update_not_possible.
|
95
|
-
write_original_manifest_files
|
96
|
-
run_pip_compile_command(
|
97
|
-
"pyenv exec pip-compile #{options} #{filename}",
|
98
|
-
fingerprint: "pyenv exec pip-compile #{options_fingerprint} <filename>"
|
99
|
-
)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Remove any .python-version file before parsing the reqs
|
103
|
-
FileUtils.remove_entry(".python-version", true)
|
104
|
-
|
105
|
-
parse_updated_files
|
106
|
-
end
|
107
|
-
rescue SharedHelpers::HelperSubprocessFailed => e
|
108
|
-
retry_count ||= 0
|
109
|
-
retry_count += 1
|
110
|
-
if compilation_error?(e) && retry_count <= 1
|
111
|
-
@build_isolation = false
|
112
|
-
retry
|
75
|
+
filenames_to_compile.each do |filename|
|
76
|
+
return nil unless compile_file(filename)
|
113
77
|
end
|
114
78
|
|
115
|
-
|
79
|
+
# Remove any .python-version file before parsing the reqs
|
80
|
+
FileUtils.remove_entry(".python-version", true)
|
81
|
+
|
82
|
+
parse_updated_files
|
116
83
|
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def compile_file(filename)
|
88
|
+
# Shell out to pip-compile.
|
89
|
+
# This is slow, as pip-compile needs to do installs.
|
90
|
+
options = pip_compile_options(filename)
|
91
|
+
options_fingerprint = pip_compile_options_fingerprint(options)
|
92
|
+
|
93
|
+
run_pip_compile_command(
|
94
|
+
"pyenv exec pip-compile -v #{options} -P #{dependency.name} #{filename}",
|
95
|
+
fingerprint: "pyenv exec pip-compile -v #{options_fingerprint} -P <dependency_name> <filename>"
|
96
|
+
)
|
97
|
+
|
98
|
+
return true if dependency.top_level?
|
99
|
+
|
100
|
+
# Run pip-compile a second time for transient dependencies
|
101
|
+
# to make sure we do not update dependencies that are
|
102
|
+
# superfluous. pip-compile does not detect these when
|
103
|
+
# updating a specific dependency with the -P option.
|
104
|
+
# Running pip-compile a second time will automatically remove
|
105
|
+
# superfluous dependencies. Dependabot then marks those with
|
106
|
+
# update_not_possible.
|
107
|
+
write_original_manifest_files
|
108
|
+
run_pip_compile_command(
|
109
|
+
"pyenv exec pip-compile #{options} #{filename}",
|
110
|
+
fingerprint: "pyenv exec pip-compile #{options_fingerprint} <filename>"
|
111
|
+
)
|
112
|
+
|
113
|
+
true
|
114
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
115
|
+
retry_count ||= 0
|
116
|
+
retry_count += 1
|
117
|
+
if compilation_error?(e) && retry_count <= 1
|
118
|
+
@build_isolation = false
|
119
|
+
retry
|
120
|
+
end
|
121
|
+
|
122
|
+
handle_pip_compile_errors(e.message)
|
117
123
|
end
|
118
124
|
|
119
125
|
def compilation_error?(error)
|
@@ -122,8 +128,8 @@ module Dependabot
|
|
122
128
|
|
123
129
|
# rubocop:disable Metrics/AbcSize
|
124
130
|
# rubocop:disable Metrics/PerceivedComplexity
|
125
|
-
def handle_pip_compile_errors(
|
126
|
-
if
|
131
|
+
def handle_pip_compile_errors(message)
|
132
|
+
if message.include?(RESOLUTION_IMPOSSIBLE_ERROR)
|
127
133
|
check_original_requirements_resolvable
|
128
134
|
# If the original requirements are resolvable but we get an
|
129
135
|
# incompatibility error after unlocking then it's likely to be
|
@@ -131,14 +137,14 @@ module Dependabot
|
|
131
137
|
return nil
|
132
138
|
end
|
133
139
|
|
134
|
-
if
|
140
|
+
if message.include?("UnsupportedConstraint")
|
135
141
|
# If there's an unsupported constraint, check if it existed
|
136
142
|
# previously (and raise if it did)
|
137
143
|
check_original_requirements_resolvable
|
138
144
|
end
|
139
145
|
|
140
|
-
if (
|
141
|
-
|
146
|
+
if (message.include?('Command "python setup.py egg_info') ||
|
147
|
+
message.include?(
|
142
148
|
"exit status 1: python setup.py egg_info"
|
143
149
|
)) &&
|
144
150
|
check_original_requirements_resolvable
|
@@ -147,16 +153,16 @@ module Dependabot
|
|
147
153
|
return
|
148
154
|
end
|
149
155
|
|
150
|
-
if
|
151
|
-
!
|
156
|
+
if message.include?(RESOLUTION_IMPOSSIBLE_ERROR) &&
|
157
|
+
!message.match?(/#{Regexp.quote(dependency.name)}/i)
|
152
158
|
# Sometimes pip-tools gets confused and can't work around
|
153
159
|
# sub-dependency incompatibilities. Ignore those cases.
|
154
160
|
return nil
|
155
161
|
end
|
156
162
|
|
157
|
-
if
|
158
|
-
tag =
|
159
|
-
constraints_section =
|
163
|
+
if message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
|
164
|
+
tag = message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
|
165
|
+
constraints_section = message.split("Finding the best candidates:").first
|
160
166
|
egg_regex = /#{Regexp.escape(tag)}#egg=(#{PYTHON_PACKAGE_NAME_REGEX})/
|
161
167
|
name_match = constraints_section.scan(egg_regex)
|
162
168
|
|
@@ -166,15 +172,15 @@ module Dependabot
|
|
166
172
|
raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
|
167
173
|
end
|
168
174
|
|
169
|
-
if
|
170
|
-
url =
|
171
|
-
|
175
|
+
if message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
176
|
+
url = message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
177
|
+
.named_captures.fetch("url")
|
172
178
|
raise GitDependenciesNotReachable, url
|
173
179
|
end
|
174
180
|
|
175
|
-
raise Dependabot::OutOfDisk if
|
181
|
+
raise Dependabot::OutOfDisk if message.end_with?("[Errno 28] No space left on device")
|
176
182
|
|
177
|
-
raise Dependabot::OutOfMemory if
|
183
|
+
raise Dependabot::OutOfMemory if message.end_with?("MemoryError")
|
178
184
|
|
179
185
|
raise
|
180
186
|
end
|
@@ -217,22 +223,7 @@ module Dependabot
|
|
217
223
|
end
|
218
224
|
|
219
225
|
def run_command(command, env: python_env, fingerprint:)
|
220
|
-
|
221
|
-
command = SharedHelpers.escape_command(command)
|
222
|
-
stdout, process = Open3.capture2e(env, command)
|
223
|
-
time_taken = Time.now - start
|
224
|
-
|
225
|
-
return stdout if process.success?
|
226
|
-
|
227
|
-
raise SharedHelpers::HelperSubprocessFailed.new(
|
228
|
-
message: stdout,
|
229
|
-
error_context: {
|
230
|
-
command: command,
|
231
|
-
fingerprint: fingerprint,
|
232
|
-
time_taken: time_taken,
|
233
|
-
process_exit_value: process.to_s
|
234
|
-
}
|
235
|
-
)
|
226
|
+
SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint, stderr_to_stdout: true)
|
236
227
|
end
|
237
228
|
|
238
229
|
def pip_compile_options_fingerprint(options)
|
@@ -381,26 +381,16 @@ module Dependabot
|
|
381
381
|
.replace_sources(credentials)
|
382
382
|
end
|
383
383
|
|
384
|
-
def run_command(command, env: {})
|
385
|
-
|
386
|
-
command = SharedHelpers.escape_command(command)
|
387
|
-
stdout, process = Open3.capture2e(env, command)
|
388
|
-
time_taken = Time.now - start
|
389
|
-
|
390
|
-
return stdout if process.success?
|
391
|
-
|
392
|
-
raise SharedHelpers::HelperSubprocessFailed.new(
|
393
|
-
message: stdout,
|
394
|
-
error_context: {
|
395
|
-
command: command,
|
396
|
-
time_taken: time_taken,
|
397
|
-
process_exit_value: process.to_s
|
398
|
-
}
|
399
|
-
)
|
384
|
+
def run_command(command, env: {}, fingerprint: nil)
|
385
|
+
SharedHelpers.run_shell_command(command, env: env, fingerprint: fingerprint, stderr_to_stdout: true)
|
400
386
|
end
|
401
387
|
|
402
388
|
def run_pipenv_command(command, env: pipenv_env_variables)
|
403
|
-
run_command(
|
389
|
+
run_command(
|
390
|
+
"pyenv local #{language_version_manager.python_major_minor}",
|
391
|
+
fingerprint: "pyenv local <python_major_minor>"
|
392
|
+
)
|
393
|
+
|
404
394
|
run_command(command, env: env)
|
405
395
|
end
|
406
396
|
|
@@ -14,7 +14,6 @@ require "dependabot/python/file_updater/pyproject_preparer"
|
|
14
14
|
require "dependabot/python/update_checker"
|
15
15
|
require "dependabot/python/version"
|
16
16
|
require "dependabot/python/requirement"
|
17
|
-
require "dependabot/python/helpers"
|
18
17
|
require "dependabot/python/native_helpers"
|
19
18
|
require "dependabot/python/authed_url_builder"
|
20
19
|
require "dependabot/python/name_normaliser"
|
@@ -309,7 +308,7 @@ module Dependabot
|
|
309
308
|
end
|
310
309
|
|
311
310
|
def run_poetry_command(command, fingerprint: nil)
|
312
|
-
|
311
|
+
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
313
312
|
end
|
314
313
|
|
315
314
|
def normalise(name)
|
data/lib/dependabot/python.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-python
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.236.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-26 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.236.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.236.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,11 +224,11 @@ files:
|
|
224
224
|
- lib/dependabot/python/file_updater/requirement_file_updater.rb
|
225
225
|
- lib/dependabot/python/file_updater/requirement_replacer.rb
|
226
226
|
- lib/dependabot/python/file_updater/setup_file_sanitizer.rb
|
227
|
-
- lib/dependabot/python/helpers.rb
|
228
227
|
- lib/dependabot/python/language_version_manager.rb
|
229
228
|
- lib/dependabot/python/metadata_finder.rb
|
230
229
|
- lib/dependabot/python/name_normaliser.rb
|
231
230
|
- lib/dependabot/python/native_helpers.rb
|
231
|
+
- lib/dependabot/python/pip_compile_file_matcher.rb
|
232
232
|
- lib/dependabot/python/requirement.rb
|
233
233
|
- lib/dependabot/python/requirement_parser.rb
|
234
234
|
- lib/dependabot/python/update_checker.rb
|
@@ -245,7 +245,7 @@ licenses:
|
|
245
245
|
- Nonstandard
|
246
246
|
metadata:
|
247
247
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
248
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
248
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.236.0
|
249
249
|
post_install_message:
|
250
250
|
rdoc_options: []
|
251
251
|
require_paths:
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "time"
|
5
|
-
require "open3"
|
6
|
-
|
7
|
-
require "dependabot/errors"
|
8
|
-
require "dependabot/shared_helpers"
|
9
|
-
|
10
|
-
module Dependabot
|
11
|
-
module Python
|
12
|
-
module Helpers
|
13
|
-
def self.run_poetry_command(command, fingerprint: nil)
|
14
|
-
start = Time.now
|
15
|
-
command = SharedHelpers.escape_command(command)
|
16
|
-
stdout, stderr, process = Open3.capture3(command)
|
17
|
-
time_taken = Time.now - start
|
18
|
-
|
19
|
-
# Raise an error with the output from the shell session if Poetry
|
20
|
-
# returns a non-zero status
|
21
|
-
return stdout if process.success?
|
22
|
-
|
23
|
-
raise SharedHelpers::HelperSubprocessFailed.new(
|
24
|
-
message: stderr,
|
25
|
-
error_context: {
|
26
|
-
command: command,
|
27
|
-
fingerprint: fingerprint,
|
28
|
-
time_taken: time_taken,
|
29
|
-
process_exit_value: process.to_s
|
30
|
-
}
|
31
|
-
)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|