dependabot-python 0.211.0 → 0.213.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/build +1 -6
- data/helpers/lib/parser.py +52 -0
- data/helpers/requirements.txt +3 -3
- data/helpers/run.py +2 -0
- data/lib/dependabot/python/file_fetcher.rb +24 -14
- data/lib/dependabot/python/file_parser/{poetry_files_parser.rb → pyproject_files_parser.rb} +87 -5
- data/lib/dependabot/python/file_parser/python_requirement_parser.rb +1 -2
- data/lib/dependabot/python/file_parser/setup_file_parser.rb +5 -5
- data/lib/dependabot/python/file_parser.rb +5 -29
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +14 -29
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +7 -9
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +7 -6
- data/lib/dependabot/python/file_updater/pyproject_preparer.rb +3 -2
- data/lib/dependabot/python/file_updater/requirement_file_updater.rb +2 -2
- data/lib/dependabot/python/file_updater/requirement_replacer.rb +2 -2
- data/lib/dependabot/python/file_updater/setup_file_sanitizer.rb +8 -8
- data/lib/dependabot/python/file_updater.rb +15 -2
- data/lib/dependabot/python/helpers.rb +20 -0
- data/lib/dependabot/python/metadata_finder.rb +2 -0
- data/lib/dependabot/python/native_helpers.rb +1 -1
- data/lib/dependabot/python/python_versions.rb +5 -5
- data/lib/dependabot/python/requirement.rb +7 -4
- data/lib/dependabot/python/requirement_parser.rb +20 -23
- data/lib/dependabot/python/update_checker/index_finder.rb +2 -2
- data/lib/dependabot/python/update_checker/latest_version_finder.rb +10 -7
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +17 -19
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +29 -34
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +45 -26
- data/lib/dependabot/python/update_checker/requirements_updater.rb +18 -5
- data/lib/dependabot/python/update_checker.rb +82 -27
- data/lib/dependabot/python/version.rb +2 -2
- metadata +16 -43
@@ -4,6 +4,7 @@ require "toml-rb"
|
|
4
4
|
require "open3"
|
5
5
|
require "dependabot/dependency"
|
6
6
|
require "dependabot/shared_helpers"
|
7
|
+
require "dependabot/python/helpers"
|
7
8
|
require "dependabot/python/version"
|
8
9
|
require "dependabot/python/requirement"
|
9
10
|
require "dependabot/python/python_versions"
|
@@ -131,7 +132,7 @@ module Dependabot
|
|
131
132
|
end
|
132
133
|
|
133
134
|
def lock_declaration_to_new_version!(poetry_object, dep)
|
134
|
-
Dependabot::Python::FileParser::
|
135
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
|
135
136
|
names = poetry_object[type]&.keys || []
|
136
137
|
pkg_name = names.find { |nm| normalise(nm) == dep.name }
|
137
138
|
next unless pkg_name
|
@@ -170,11 +171,11 @@ module Dependabot
|
|
170
171
|
write_temporary_dependency_files(pyproject_content)
|
171
172
|
add_auth_env_vars
|
172
173
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
174
|
+
Helpers.install_required_python(python_version)
|
175
|
+
|
176
|
+
# use system git instead of the pure Python dulwich
|
177
|
+
unless python_version&.start_with?("3.6")
|
178
|
+
run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
|
178
179
|
end
|
179
180
|
|
180
181
|
run_poetry_command(poetry_update_command)
|
@@ -52,9 +52,10 @@ module Dependabot
|
|
52
52
|
poetry_object = pyproject_object["tool"]["poetry"]
|
53
53
|
excluded_names = dependencies.map(&:name) + ["python"]
|
54
54
|
|
55
|
-
Dependabot::Python::FileParser::
|
55
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |key|
|
56
56
|
next unless poetry_object[key]
|
57
57
|
|
58
|
+
source_types = %w(directory file url)
|
58
59
|
poetry_object.fetch(key).each do |dep_name, _|
|
59
60
|
next if excluded_names.include?(normalise(dep_name))
|
60
61
|
|
@@ -62,7 +63,7 @@ module Dependabot
|
|
62
63
|
|
63
64
|
next unless (locked_version = locked_details&.fetch("version"))
|
64
65
|
|
65
|
-
next if
|
66
|
+
next if source_types.include?(locked_details&.dig("source", "type"))
|
66
67
|
|
67
68
|
if locked_details&.dig("source", "type") == "git"
|
68
69
|
poetry_object[key][dep_name] = {
|
@@ -36,7 +36,7 @@ module Dependabot
|
|
36
36
|
def fetch_updated_dependency_files
|
37
37
|
reqs = dependency.requirements.zip(dependency.previous_requirements)
|
38
38
|
|
39
|
-
reqs.
|
39
|
+
reqs.filter_map do |(new_req, old_req)|
|
40
40
|
next if new_req == old_req
|
41
41
|
|
42
42
|
file = get_original_file(new_req.fetch(:file)).dup
|
@@ -46,7 +46,7 @@ module Dependabot
|
|
46
46
|
|
47
47
|
file.content = updated_content
|
48
48
|
file
|
49
|
-
end
|
49
|
+
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def updated_requirement_or_setup_file_content(new_req, old_req)
|
@@ -52,7 +52,7 @@ module Dependabot
|
|
52
52
|
if add_space_after_operators?
|
53
53
|
new_req_string =
|
54
54
|
new_req_string.
|
55
|
-
gsub(/(#{RequirementParser::COMPARISON})\s*(?=\d)
|
55
|
+
gsub(/(#{RequirementParser::COMPARISON})\s*(?=\d)/o, '\1 ')
|
56
56
|
end
|
57
57
|
|
58
58
|
new_req_string
|
@@ -92,7 +92,7 @@ module Dependabot
|
|
92
92
|
def add_space_after_operators?
|
93
93
|
original_dependency_declaration_string(old_requirement).
|
94
94
|
match(RequirementParser::REQUIREMENTS).
|
95
|
-
to_s.match?(/#{RequirementParser::COMPARISON}\s+\d/)
|
95
|
+
to_s.match?(/#{RequirementParser::COMPARISON}\s+\d/o)
|
96
96
|
end
|
97
97
|
|
98
98
|
def original_declaration_replacement_regex
|
@@ -19,9 +19,9 @@ module Dependabot
|
|
19
19
|
# install_requires. A name and version are required by don't end up
|
20
20
|
# in the lockfile.
|
21
21
|
content =
|
22
|
-
"from setuptools import setup\n\n"\
|
23
|
-
"setup(name=\"sanitized-package\",version=\"0.0.1\","\
|
24
|
-
"install_requires=#{install_requires_array.to_json},"\
|
22
|
+
"from setuptools import setup\n\n" \
|
23
|
+
"setup(name=\"sanitized-package\",version=\"0.0.1\"," \
|
24
|
+
"install_requires=#{install_requires_array.to_json}," \
|
25
25
|
"extras_require=#{extras_require_hash.to_json}"
|
26
26
|
|
27
27
|
content += ',setup_requires=["pbr"],pbr=True' if include_pbr?
|
@@ -38,22 +38,22 @@ module Dependabot
|
|
38
38
|
|
39
39
|
def install_requires_array
|
40
40
|
@install_requires_array ||=
|
41
|
-
parsed_setup_file.dependencies.
|
41
|
+
parsed_setup_file.dependencies.filter_map do |dep|
|
42
42
|
next unless dep.requirements.first[:groups].
|
43
43
|
include?("install_requires")
|
44
44
|
|
45
45
|
dep.name + dep.requirements.first[:requirement].to_s
|
46
|
-
end
|
46
|
+
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def setup_requires_array
|
50
50
|
@setup_requires_array ||=
|
51
|
-
parsed_setup_file.dependencies.
|
51
|
+
parsed_setup_file.dependencies.filter_map do |dep|
|
52
52
|
next unless dep.requirements.first[:groups].
|
53
53
|
include?("setup_requires")
|
54
54
|
|
55
55
|
dep.name + dep.requirements.first[:requirement].to_s
|
56
|
-
end
|
56
|
+
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def extras_require_hash
|
@@ -66,7 +66,7 @@ module Dependabot
|
|
66
66
|
|
67
67
|
hash[group.split(":").last] ||= []
|
68
68
|
hash[group.split(":").last] <<
|
69
|
-
dep.name + dep.requirements.first[:requirement].to_s
|
69
|
+
(dep.name + dep.requirements.first[:requirement].to_s)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "toml-rb"
|
3
4
|
require "dependabot/file_updaters"
|
4
5
|
require "dependabot/file_updaters/base"
|
5
6
|
require "dependabot/shared_helpers"
|
@@ -60,8 +61,14 @@ module Dependabot
|
|
60
61
|
|
61
62
|
# Otherwise, this is a top-level dependency, and we can figure out
|
62
63
|
# which resolver to use based on the filename of its requirements
|
63
|
-
return :pipfile if changed_req_files.any?
|
64
|
-
|
64
|
+
return :pipfile if changed_req_files.any?("Pipfile")
|
65
|
+
|
66
|
+
if changed_req_files.any?("pyproject.toml")
|
67
|
+
return :poetry if poetry_based?
|
68
|
+
|
69
|
+
return :requirements
|
70
|
+
end
|
71
|
+
|
65
72
|
return :pip_compile if changed_req_files.any? { |f| f.end_with?(".in") }
|
66
73
|
|
67
74
|
:requirements
|
@@ -119,6 +126,12 @@ module Dependabot
|
|
119
126
|
raise "Missing required files!"
|
120
127
|
end
|
121
128
|
|
129
|
+
def poetry_based?
|
130
|
+
return false unless pyproject
|
131
|
+
|
132
|
+
!TomlRB.parse(pyproject.content).dig("tool", "poetry").nil?
|
133
|
+
end
|
134
|
+
|
122
135
|
def pipfile
|
123
136
|
@pipfile ||= get_original_file("Pipfile")
|
124
137
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/logger"
|
4
|
+
|
5
|
+
module Dependabot
|
6
|
+
module Python
|
7
|
+
module Helpers
|
8
|
+
def self.install_required_python(python_version)
|
9
|
+
# The leading space is important in the version check
|
10
|
+
return if SharedHelpers.run_shell_command("pyenv versions").include?(" #{python_version}")
|
11
|
+
|
12
|
+
Dependabot.logger.info("Installing required Python #{python_version}.")
|
13
|
+
SharedHelpers.run_shell_command("pyenv install -s #{python_version}")
|
14
|
+
SharedHelpers.run_shell_command("pyenv exec pip install --upgrade pip")
|
15
|
+
SharedHelpers.run_shell_command("pyenv exec pip install -r" \
|
16
|
+
"#{NativeHelpers.python_requirements_path}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -4,16 +4,16 @@ module Dependabot
|
|
4
4
|
module Python
|
5
5
|
module PythonVersions
|
6
6
|
PRE_INSTALLED_PYTHON_VERSIONS = %w(
|
7
|
-
3.10.
|
7
|
+
3.10.7
|
8
8
|
).freeze
|
9
9
|
|
10
10
|
# Due to an OpenSSL issue we can only install the following versions in
|
11
11
|
# the Dependabot container.
|
12
12
|
SUPPORTED_VERSIONS = %w(
|
13
|
-
3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
|
14
|
-
3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
|
15
|
-
3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
|
16
|
-
3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
|
13
|
+
3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
|
14
|
+
3.9.14 3.9.13 3.9.12 3.9.11 3.9.10 3.9.9 3.9.8 3.9.7 3.9.6 3.9.5 3.9.4 3.9.2 3.9.1 3.9.0
|
15
|
+
3.8.14 3.8.13 3.8.12 3.8.11 3.8.10 3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 3.8.2 3.8.1 3.8.0
|
16
|
+
3.7.14 3.7.13 3.7.12 3.7.11 3.7.10 3.7.9 3.7.8 3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0
|
17
17
|
3.6.15 3.6.14 3.6.13 3.6.12 3.6.11 3.6.10 3.6.9 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3
|
18
18
|
3.6.2 3.6.1 3.6.0 3.5.10 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3
|
19
19
|
).freeze
|
@@ -6,7 +6,7 @@ require "dependabot/python/version"
|
|
6
6
|
module Dependabot
|
7
7
|
module Python
|
8
8
|
class Requirement < Gem::Requirement
|
9
|
-
OR_SEPARATOR = /(?<=[a-zA-Z0-9)*])\s
|
9
|
+
OR_SEPARATOR = /(?<=[a-zA-Z0-9)*])\s*\|+/
|
10
10
|
|
11
11
|
# Add equality and arbitrary-equality matchers
|
12
12
|
OPS = OPS.merge(
|
@@ -19,8 +19,8 @@ module Dependabot
|
|
19
19
|
version_pattern = Python::Version::VERSION_PATTERN
|
20
20
|
|
21
21
|
PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{version_pattern})\\s*"
|
22
|
-
PATTERN = /\A#{PATTERN_RAW}\z
|
23
|
-
PARENS_PATTERN = /\A\(([^)]+)\)\z
|
22
|
+
PATTERN = /\A#{PATTERN_RAW}\z/
|
23
|
+
PARENS_PATTERN = /\A\(([^)]+)\)\z/
|
24
24
|
|
25
25
|
def self.parse(obj)
|
26
26
|
return ["=", Python::Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
|
@@ -60,6 +60,9 @@ module Dependabot
|
|
60
60
|
requirements = requirements.flatten.flat_map do |req_string|
|
61
61
|
next if req_string.nil?
|
62
62
|
|
63
|
+
# Standard python doesn't support whitespace in requirements, but Poetry does.
|
64
|
+
req_string = req_string.gsub(/(\d +)([<=>])/, '\1,\2')
|
65
|
+
|
63
66
|
req_string.split(",").map(&:strip).map do |r|
|
64
67
|
convert_python_constraint_to_ruby_constraint(r)
|
65
68
|
end
|
@@ -87,7 +90,7 @@ module Dependabot
|
|
87
90
|
return nil if req_string == "*"
|
88
91
|
|
89
92
|
req_string = req_string.gsub("~=", "~>")
|
90
|
-
req_string = req_string.gsub(/(?<=\d)[<=>]
|
93
|
+
req_string = req_string.gsub(/(?<=\d)[<=>].*\Z/, "")
|
91
94
|
|
92
95
|
if req_string.match?(/~[^>]/) then convert_tilde_req(req_string)
|
93
96
|
elsif req_string.start_with?("^") then convert_caret_req(req_string)
|
@@ -3,29 +3,26 @@
|
|
3
3
|
module Dependabot
|
4
4
|
module Python
|
5
5
|
class RequirementParser
|
6
|
-
NAME = /[a-zA-Z0-9](?:[a-zA-Z0-9\-_\.]*[a-zA-Z0-9])
|
7
|
-
EXTRA = /[a-zA-Z0-9\-_\.]
|
8
|
-
COMPARISON =
|
9
|
-
VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)
|
10
|
-
|
11
|
-
REQUIREMENT =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
%r{[a-zA-Z0-9\s\(\)\.\{\}\-_\*#:;/\?\[\]!~`@\$%\^&=\+\|<>]}.freeze
|
19
|
-
PYTHON_STR = /('(#{PYTHON_STR_C}|")*'|"(#{PYTHON_STR_C}|')*")/.freeze
|
6
|
+
NAME = /[a-zA-Z0-9](?:[a-zA-Z0-9\-_\.]*[a-zA-Z0-9])?/
|
7
|
+
EXTRA = /[a-zA-Z0-9\-_\.]+/
|
8
|
+
COMPARISON = /===|==|>=|<=|<|>|~=|!=/
|
9
|
+
VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?/
|
10
|
+
|
11
|
+
REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*(?<version>#{VERSION})/
|
12
|
+
HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|$)/
|
13
|
+
REQUIREMENTS = /#{REQUIREMENT}(\s*,\s*\\?\s*#{REQUIREMENT})*/
|
14
|
+
HASHES = /#{HASH}(\s*\\?\s*#{HASH})*/
|
15
|
+
MARKER_OP = /\s*(#{COMPARISON}|(\s*in)|(\s*not\s*in))/
|
16
|
+
PYTHON_STR_C = %r{[a-zA-Z0-9\s\(\)\.\{\}\-_\*#:;/\?\[\]!~`@\$%\^&=\+\|<>]}
|
17
|
+
PYTHON_STR = /('(#{PYTHON_STR_C}|")*'|"(#{PYTHON_STR_C}|')*")/
|
20
18
|
ENV_VAR =
|
21
19
|
/python_version|python_full_version|os_name|sys_platform|
|
22
20
|
platform_release|platform_system|platform_version|platform_machine|
|
23
21
|
platform_python_implementation|implementation_name|
|
24
|
-
implementation_version
|
25
|
-
MARKER_VAR = /\s*(#{ENV_VAR}|#{PYTHON_STR})
|
26
|
-
MARKER_EXPR_ONE = /#{MARKER_VAR}#{MARKER_OP}#{MARKER_VAR}
|
27
|
-
MARKER_EXPR =
|
28
|
-
/(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/.freeze
|
22
|
+
implementation_version/
|
23
|
+
MARKER_VAR = /\s*(#{ENV_VAR}|#{PYTHON_STR})/
|
24
|
+
MARKER_EXPR_ONE = /#{MARKER_VAR}#{MARKER_OP}#{MARKER_VAR}/
|
25
|
+
MARKER_EXPR = /(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/
|
29
26
|
|
30
27
|
INSTALL_REQ_WITH_REQUIREMENT =
|
31
28
|
/\s*\\?\s*(?<name>#{NAME})
|
@@ -34,7 +31,7 @@ module Dependabot
|
|
34
31
|
\s*\\?\s*(;\s*(?<markers>#{MARKER_EXPR}))?
|
35
32
|
\s*\\?\s*(?<hashes>#{HASHES})?
|
36
33
|
\s*#*\s*(?<comment>.+)?
|
37
|
-
/x
|
34
|
+
/x
|
38
35
|
|
39
36
|
INSTALL_REQ_WITHOUT_REQUIREMENT =
|
40
37
|
/^\s*\\?\s*(?<name>#{NAME})
|
@@ -42,7 +39,7 @@ module Dependabot
|
|
42
39
|
\s*\\?\s*(;\s*(?<markers>#{MARKER_EXPR}))?
|
43
40
|
\s*\\?\s*(?<hashes>#{HASHES})?
|
44
41
|
\s*#*\s*(?<comment>.+)?$
|
45
|
-
/x
|
42
|
+
/x
|
46
43
|
|
47
44
|
VALID_REQ_TXT_REQUIREMENT =
|
48
45
|
/^\s*\\?\s*(?<name>#{NAME})
|
@@ -51,12 +48,12 @@ module Dependabot
|
|
51
48
|
\s*\\?\s*(;\s*(?<markers>#{MARKER_EXPR}))?
|
52
49
|
\s*\\?\s*(?<hashes>#{HASHES})?
|
53
50
|
\s*(\#+\s*(?<comment>.*))?$
|
54
|
-
/x
|
51
|
+
/x
|
55
52
|
|
56
53
|
NAME_WITH_EXTRAS =
|
57
54
|
/\s*\\?\s*(?<name>#{NAME})
|
58
55
|
(\s*\\?\s*\[\s*(?<extras>#{EXTRA}(\s*,\s*#{EXTRA})*)\s*\])?
|
59
|
-
/x
|
56
|
+
/x
|
60
57
|
end
|
61
58
|
end
|
62
59
|
end
|
@@ -9,7 +9,7 @@ module Dependabot
|
|
9
9
|
class UpdateChecker
|
10
10
|
class IndexFinder
|
11
11
|
PYPI_BASE_URL = "https://pypi.org/simple/"
|
12
|
-
ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}
|
12
|
+
ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/
|
13
13
|
|
14
14
|
def initialize(dependency_files:, credentials:)
|
15
15
|
@dependency_files = dependency_files
|
@@ -171,7 +171,7 @@ module Dependabot
|
|
171
171
|
authed_url = config_variable_urls.find { |u| u.match?(regexp) }
|
172
172
|
return authed_url if authed_url
|
173
173
|
|
174
|
-
cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}, "")
|
174
|
+
cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}o, "")
|
175
175
|
authed_url = authed_base_url(cleaned_url)
|
176
176
|
return authed_url if credential_for(cleaned_url)
|
177
177
|
|
@@ -85,14 +85,14 @@ module Dependabot
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def filter_unsupported_versions(versions_array, python_version)
|
88
|
-
versions_array.
|
88
|
+
versions_array.filter_map do |details|
|
89
89
|
python_requirement = details.fetch(:python_requirement)
|
90
90
|
next details.fetch(:version) unless python_version
|
91
91
|
next details.fetch(:version) unless python_requirement
|
92
92
|
next unless python_requirement.satisfied_by?(python_version)
|
93
93
|
|
94
94
|
details.fetch(:version)
|
95
|
-
end
|
95
|
+
end
|
96
96
|
end
|
97
97
|
|
98
98
|
def filter_prerelease_versions(versions_array)
|
@@ -118,9 +118,9 @@ module Dependabot
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def filter_out_of_range_versions(versions_array)
|
121
|
-
reqs = dependency.requirements.
|
121
|
+
reqs = dependency.requirements.filter_map do |r|
|
122
122
|
requirement_class.requirements_array(r.fetch(:requirement))
|
123
|
-
end
|
123
|
+
end
|
124
124
|
|
125
125
|
versions_array.
|
126
126
|
select { |v| reqs.all? { |r| r.any? { |o| o.satisfied_by?(v) } } }
|
@@ -144,11 +144,14 @@ module Dependabot
|
|
144
144
|
@available_versions ||=
|
145
145
|
index_urls.flat_map do |index_url|
|
146
146
|
sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted")
|
147
|
+
|
147
148
|
index_response = registry_response_for_dependency(index_url)
|
149
|
+
if index_response.status == 401 || index_response.status == 403
|
150
|
+
registry_index_response = registry_index_response(index_url)
|
148
151
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
+
if registry_index_response.status == 401 || registry_index_response.status == 403
|
153
|
+
raise PrivateSourceAuthenticationFailure, sanitized_url
|
154
|
+
end
|
152
155
|
end
|
153
156
|
|
154
157
|
version_links = []
|
@@ -11,6 +11,7 @@ require "dependabot/python/file_updater/requirement_replacer"
|
|
11
11
|
require "dependabot/python/file_updater/setup_file_sanitizer"
|
12
12
|
require "dependabot/python/version"
|
13
13
|
require "dependabot/shared_helpers"
|
14
|
+
require "dependabot/python/helpers"
|
14
15
|
require "dependabot/python/native_helpers"
|
15
16
|
require "dependabot/python/python_versions"
|
16
17
|
require "dependabot/python/name_normaliser"
|
@@ -24,16 +25,14 @@ module Dependabot
|
|
24
25
|
# - Run `pip-compile` and see what the result is
|
25
26
|
# rubocop:disable Metrics/ClassLength
|
26
27
|
class PipCompileVersionResolver
|
27
|
-
GIT_DEPENDENCY_UNREACHABLE_REGEX =
|
28
|
-
|
29
|
-
GIT_REFERENCE_NOT_FOUND_REGEX =
|
30
|
-
/Did not find branch or tag '(?<tag>[^\n"]+)'/m.freeze
|
28
|
+
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).* /
|
29
|
+
GIT_REFERENCE_NOT_FOUND_REGEX = /Did not find branch or tag '(?<tag>[^\n"]+)'/m
|
31
30
|
NATIVE_COMPILATION_ERROR =
|
32
31
|
"pip._internal.exceptions.InstallationSubprocessError: Command errored out with exit status 1:"
|
33
32
|
# See https://packaging.python.org/en/latest/tutorials/packaging-projects/#configuring-metadata
|
34
|
-
PYTHON_PACKAGE_NAME_REGEX = /[A-Za-z0-9_\-]
|
33
|
+
PYTHON_PACKAGE_NAME_REGEX = /[A-Za-z0-9_\-]+/
|
35
34
|
RESOLUTION_IMPOSSIBLE_ERROR = "ResolutionImpossible"
|
36
|
-
ERROR_REGEX = /(?<=ERROR\:\W)
|
35
|
+
ERROR_REGEX = /(?<=ERROR\:\W).*$/
|
37
36
|
|
38
37
|
attr_reader :dependency, :dependency_files, :credentials
|
39
38
|
|
@@ -72,7 +71,7 @@ module Dependabot
|
|
72
71
|
SharedHelpers.in_a_temporary_directory do
|
73
72
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
74
73
|
write_temporary_dependency_files(updated_req: requirement)
|
75
|
-
install_required_python
|
74
|
+
Helpers.install_required_python(python_version)
|
76
75
|
|
77
76
|
filenames_to_compile.each do |filename|
|
78
77
|
# Shell out to pip-compile.
|
@@ -80,9 +79,17 @@ module Dependabot
|
|
80
79
|
run_pip_compile_command(
|
81
80
|
"pyenv exec pip-compile -v #{pip_compile_options(filename)} -P #{dependency.name} #{filename}"
|
82
81
|
)
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
|
83
|
+
next if dependency.top_level?
|
84
|
+
|
85
|
+
# Run pip-compile a second time for transient dependencies
|
86
|
+
# to make sure we do not update dependencies that are
|
87
|
+
# superfluous. pip-compile does not detect these when
|
88
|
+
# updating a specific dependency with the -P option.
|
89
|
+
# Running pip-compile a second time will automatically remove
|
90
|
+
# superfluous dependencies. Dependabot then marks those with
|
91
|
+
# update_not_possible.
|
92
|
+
write_original_manifest_files
|
86
93
|
run_pip_compile_command(
|
87
94
|
"pyenv exec pip-compile #{pip_compile_options(filename)} #{filename}"
|
88
95
|
)
|
@@ -313,15 +320,6 @@ module Dependabot
|
|
313
320
|
end
|
314
321
|
end
|
315
322
|
|
316
|
-
def install_required_python
|
317
|
-
return if run_command("pyenv versions").include?("#{python_version}\n")
|
318
|
-
|
319
|
-
run_command("pyenv install -s #{python_version}")
|
320
|
-
run_command("pyenv exec pip install --upgrade pip")
|
321
|
-
run_command("pyenv exec pip install -r"\
|
322
|
-
"#{NativeHelpers.python_requirements_path}")
|
323
|
-
end
|
324
|
-
|
325
323
|
def sanitized_setup_file_content(file)
|
326
324
|
@sanitized_setup_file_content ||= {}
|
327
325
|
return @sanitized_setup_file_content[file.name] if @sanitized_setup_file_content[file.name]
|
@@ -29,21 +29,22 @@ module Dependabot
|
|
29
29
|
# just raise if the latest version can't be resolved. Knowing that is
|
30
30
|
# still better than nothing, though.
|
31
31
|
class PipenvVersionResolver
|
32
|
-
|
33
|
-
|
34
|
-
GIT_REFERENCE_NOT_FOUND_REGEX =
|
35
|
-
|
36
|
-
freeze
|
37
|
-
PIPENV_INSTALLATION_ERROR = "pipenv.patched.notpip._internal.exceptions.InstallationError: Command errored out"\
|
32
|
+
# rubocop:disable Layout/LineLength
|
33
|
+
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone -q (?<url>[^\s]+).* /
|
34
|
+
GIT_REFERENCE_NOT_FOUND_REGEX = %r{git checkout -q (?<tag>[^\n"]+)\n?[^\n]*/(?<name>.*?)(\\n'\]|$)}m
|
35
|
+
PIPENV_INSTALLATION_ERROR = "pipenv.patched.notpip._internal.exceptions.InstallationError: Command errored out" \
|
38
36
|
" with exit status 1: python setup.py egg_info"
|
39
37
|
TRACEBACK = "Traceback (most recent call last):"
|
40
38
|
PIPENV_INSTALLATION_ERROR_REGEX =
|
41
|
-
/#{Regexp.quote(TRACEBACK)}[\s\S]*^\s+import\s(?<name>.+)[\s\S]*^#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}
|
42
|
-
|
39
|
+
/#{Regexp.quote(TRACEBACK)}[\s\S]*^\s+import\s(?<name>.+)[\s\S]*^#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}/
|
40
|
+
|
43
41
|
UNSUPPORTED_DEPS = %w(pyobjc).freeze
|
44
42
|
UNSUPPORTED_DEP_REGEX =
|
45
|
-
/Could not find a version that satisfies the requirement.*(?:#{UNSUPPORTED_DEPS.join("|")})
|
46
|
-
PIPENV_RANGE_WARNING = /Warning:\sPython\s[<>].* was not found
|
43
|
+
/Could not find a version that satisfies the requirement.*(?:#{UNSUPPORTED_DEPS.join("|")})/
|
44
|
+
PIPENV_RANGE_WARNING = /Warning:\sPython\s[<>].* was not found/
|
45
|
+
# rubocop:enable Layout/LineLength
|
46
|
+
|
47
|
+
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
47
48
|
|
48
49
|
attr_reader :dependency, :dependency_files, :credentials
|
49
50
|
|
@@ -136,20 +137,20 @@ module Dependabot
|
|
136
137
|
end
|
137
138
|
|
138
139
|
if error.message.match?(UNSUPPORTED_DEP_REGEX)
|
139
|
-
msg = "Dependabot detected a dependency that can't be built on "\
|
140
|
-
"linux. Currently, all Dependabot builds happen on linux "\
|
141
|
-
"boxes, so there is no way for Dependabot to resolve your "\
|
142
|
-
"dependency files.\n\n"\
|
143
|
-
"Unless you think Dependabot has made a mistake (please "\
|
144
|
-
"tag us if so) you may wish to disable Dependabot on this "\
|
140
|
+
msg = "Dependabot detected a dependency that can't be built on " \
|
141
|
+
"linux. Currently, all Dependabot builds happen on linux " \
|
142
|
+
"boxes, so there is no way for Dependabot to resolve your " \
|
143
|
+
"dependency files.\n\n" \
|
144
|
+
"Unless you think Dependabot has made a mistake (please " \
|
145
|
+
"tag us if so) you may wish to disable Dependabot on this " \
|
145
146
|
"repo."
|
146
147
|
raise DependencyFileNotResolvable, msg
|
147
148
|
end
|
148
149
|
|
149
150
|
if error.message.match?(PIPENV_RANGE_WARNING)
|
150
|
-
msg = "Pipenv does not support specifying Python ranges "\
|
151
|
-
|
152
|
-
|
151
|
+
msg = "Pipenv does not support specifying Python ranges " \
|
152
|
+
"(see https://github.com/pypa/pipenv/issues/1050 for more " \
|
153
|
+
"details)."
|
153
154
|
raise DependencyFileNotResolvable, msg
|
154
155
|
end
|
155
156
|
|
@@ -159,7 +160,7 @@ module Dependabot
|
|
159
160
|
|
160
161
|
if error.message.include?("SyntaxError: invalid syntax")
|
161
162
|
raise DependencyFileNotResolvable,
|
162
|
-
"SyntaxError while installing dependencies. Is one of the dependencies not Python 3 compatible? "\
|
163
|
+
"SyntaxError while installing dependencies. Is one of the dependencies not Python 3 compatible? " \
|
163
164
|
"Pip v21 no longer supports Python 2."
|
164
165
|
end
|
165
166
|
|
@@ -272,9 +273,9 @@ module Dependabot
|
|
272
273
|
dependency_name = error_message.match(PIPENV_INSTALLATION_ERROR_REGEX).named_captures["name"]
|
273
274
|
raise unless dependency_name
|
274
275
|
|
275
|
-
msg = "Pipenv failed to install \"#{dependency_name}\". This could be caused by missing system "\
|
276
|
-
"dependencies that can't be installed by Dependabot or required installation flags.\n\n"\
|
277
|
-
"Error output from running \"pipenv lock\":\n"\
|
276
|
+
msg = "Pipenv failed to install \"#{dependency_name}\". This could be caused by missing system " \
|
277
|
+
"dependencies that can't be installed by Dependabot or required installation flags.\n\n" \
|
278
|
+
"Error output from running \"pipenv lock\":\n" \
|
278
279
|
"#{clean_error_message(error_message)}"
|
279
280
|
|
280
281
|
raise DependencyFileNotResolvable, msg
|
@@ -319,13 +320,7 @@ module Dependabot
|
|
319
320
|
nil
|
320
321
|
end
|
321
322
|
|
322
|
-
|
323
|
-
|
324
|
-
requirements_path = NativeHelpers.python_requirements_path
|
325
|
-
run_command("pyenv install -s #{python_version}")
|
326
|
-
run_command("pyenv exec pip install --upgrade pip")
|
327
|
-
run_command("pyenv exec pip install -r "\
|
328
|
-
"#{requirements_path}")
|
323
|
+
Helpers.install_required_python(python_version)
|
329
324
|
end
|
330
325
|
|
331
326
|
def sanitized_setup_file_content(file)
|
@@ -361,7 +356,7 @@ module Dependabot
|
|
361
356
|
|
362
357
|
pipfile_object = TomlRB.parse(pipfile_content)
|
363
358
|
|
364
|
-
|
359
|
+
DEPENDENCY_TYPES.each do |type|
|
365
360
|
names = pipfile_object[type]&.keys || []
|
366
361
|
pkg_name = names.find { |nm| normalise(nm) == dependency.name }
|
367
362
|
next unless pkg_name || subdep_type?(type)
|
@@ -429,9 +424,9 @@ module Dependabot
|
|
429
424
|
|
430
425
|
# Otherwise we have to raise, giving details of the Python versions
|
431
426
|
# that Dependabot supports
|
432
|
-
msg = "Dependabot detected the following Python requirement "\
|
433
|
-
"for your project: '#{requirement_string}'.\n\nCurrently, the "\
|
434
|
-
"following Python versions are supported in Dependabot: "\
|
427
|
+
msg = "Dependabot detected the following Python requirement " \
|
428
|
+
"for your project: '#{requirement_string}'.\n\nCurrently, the " \
|
429
|
+
"following Python versions are supported in Dependabot: " \
|
435
430
|
"#{PythonVersions::SUPPORTED_VERSIONS.join(', ')}."
|
436
431
|
raise DependencyFileNotResolvable, msg
|
437
432
|
end
|