dependabot-python 0.211.0 → 0.213.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|