dependabot-python 0.212.0 → 0.214.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 +5 -6
- data/helpers/lib/parser.py +59 -0
- data/helpers/requirements.txt +3 -3
- data/helpers/run.py +2 -0
- data/lib/dependabot/python/file_fetcher.rb +21 -12
- data/lib/dependabot/python/file_parser/{poetry_files_parser.rb → pyproject_files_parser.rb} +84 -2
- data/lib/dependabot/python/file_parser/setup_file_parser.rb +4 -4
- data/lib/dependabot/python/file_parser.rb +5 -29
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +7 -22
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +11 -8
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +6 -4
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +15 -7
- data/lib/dependabot/python/file_updater/pyproject_preparer.rb +16 -1
- data/lib/dependabot/python/file_updater.rb +14 -1
- data/lib/dependabot/python/helpers.rb +37 -0
- data/lib/dependabot/python/metadata_finder.rb +2 -0
- data/lib/dependabot/python/python_versions.rb +11 -7
- 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 +1 -1
- data/lib/dependabot/python/update_checker/latest_version_finder.rb +2 -2
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +19 -21
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +16 -18
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +14 -12
- data/lib/dependabot/python/update_checker/requirements_updater.rb +17 -4
- data/lib/dependabot/python/update_checker.rb +82 -25
- data/lib/dependabot/python/version.rb +2 -2
- metadata +15 -56
@@ -36,6 +36,17 @@ module Dependabot
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
def update_python_requirement(requirement)
|
40
|
+
pyproject_object = TomlRB.parse(@pyproject_content)
|
41
|
+
if (python_specification = pyproject_object.dig("tool", "poetry", "dependencies", "python"))
|
42
|
+
python_req = Python::Requirement.new(python_specification)
|
43
|
+
unless python_req.satisfied_by?(requirement)
|
44
|
+
pyproject_object["tool"]["poetry"]["dependencies"]["python"] = "~#{requirement}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
TomlRB.dump(pyproject_object)
|
48
|
+
end
|
49
|
+
|
39
50
|
def sanitize
|
40
51
|
# {{ name }} syntax not allowed
|
41
52
|
pyproject_content.
|
@@ -52,7 +63,7 @@ module Dependabot
|
|
52
63
|
poetry_object = pyproject_object["tool"]["poetry"]
|
53
64
|
excluded_names = dependencies.map(&:name) + ["python"]
|
54
65
|
|
55
|
-
Dependabot::Python::FileParser::
|
66
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |key|
|
56
67
|
next unless poetry_object[key]
|
57
68
|
|
58
69
|
source_types = %w(directory file url)
|
@@ -72,6 +83,10 @@ module Dependabot
|
|
72
83
|
}
|
73
84
|
elsif poetry_object[key][dep_name].is_a?(Hash)
|
74
85
|
poetry_object[key][dep_name]["version"] = locked_version
|
86
|
+
elsif poetry_object[key][dep_name].is_a?(Array)
|
87
|
+
# if it has multiple-constraints, locking to a single version is
|
88
|
+
# going to result in a bad lockfile, ignore
|
89
|
+
next
|
75
90
|
else
|
76
91
|
poetry_object[key][dep_name] = locked_version
|
77
92
|
end
|
@@ -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"
|
@@ -61,7 +62,13 @@ module Dependabot
|
|
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
64
|
return :pipfile if changed_req_files.any?("Pipfile")
|
64
|
-
|
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,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/logger"
|
4
|
+
require "dependabot/python/version"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Python
|
8
|
+
module Helpers
|
9
|
+
def self.install_required_python(python_version)
|
10
|
+
# The leading space is important in the version check
|
11
|
+
return if SharedHelpers.run_shell_command("pyenv versions").include?(" #{python_major_minor(python_version)}.")
|
12
|
+
|
13
|
+
if File.exist?("/usr/local/.pyenv/#{python_major_minor(python_version)}.tar.gz")
|
14
|
+
SharedHelpers.run_shell_command(
|
15
|
+
"tar xzf /usr/local/.pyenv/#{python_major_minor(python_version)}.tar.gz -C /usr/local/.pyenv/"
|
16
|
+
)
|
17
|
+
return if SharedHelpers.run_shell_command("pyenv versions").
|
18
|
+
include?(" #{python_major_minor(python_version)}.")
|
19
|
+
end
|
20
|
+
|
21
|
+
Dependabot.logger.info("Installing required Python #{python_version}.")
|
22
|
+
start = Time.now
|
23
|
+
SharedHelpers.run_shell_command("pyenv install -s #{python_version}")
|
24
|
+
SharedHelpers.run_shell_command("pyenv exec pip install --upgrade pip")
|
25
|
+
SharedHelpers.run_shell_command("pyenv exec pip install -r" \
|
26
|
+
"#{NativeHelpers.python_requirements_path}")
|
27
|
+
time_taken = Time.now - start
|
28
|
+
Dependabot.logger.info("Installing Python #{python_version} took #{time_taken}s.")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.python_major_minor(python_version)
|
32
|
+
python = Python::Version.new(python_version)
|
33
|
+
"#{python.segments[0]}.#{python.segments[1]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -4,18 +4,22 @@ module Dependabot
|
|
4
4
|
module Python
|
5
5
|
module PythonVersions
|
6
6
|
PRE_INSTALLED_PYTHON_VERSIONS = %w(
|
7
|
-
3.
|
7
|
+
3.11.0
|
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
|
+
# NOTE: When adding one version, always doublecheck for additional releases: https://www.python.org/downloads/
|
13
|
+
#
|
14
|
+
# WARNING: 3.9.3 is purposefully omitted as it was recalled: https://www.python.org/downloads/release/python-393/
|
12
15
|
SUPPORTED_VERSIONS = %w(
|
13
|
-
3.
|
14
|
-
3.
|
15
|
-
3.
|
16
|
-
3.
|
17
|
-
3.
|
18
|
-
3.6.
|
16
|
+
3.11.0
|
17
|
+
3.10.8 3.10.7 3.10.6 3.10.5 3.10.4 3.10.3 3.10.2 3.10.1 3.10.0
|
18
|
+
3.9.15 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
|
19
|
+
3.8.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
|
20
|
+
3.7.15 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
|
21
|
+
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 3.6.2 3.6.1 3.6.0
|
22
|
+
3.5.10 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3
|
19
23
|
).freeze
|
20
24
|
|
21
25
|
# This list gets iterated through to find a valid version, so we have
|
@@ -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
|
@@ -112,9 +112,9 @@ module Dependabot
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def filter_lower_versions(versions_array)
|
115
|
-
return versions_array unless dependency.
|
115
|
+
return versions_array unless dependency.numeric_version
|
116
116
|
|
117
|
-
versions_array.select { |version| version >
|
117
|
+
versions_array.select { |version| version > dependency.numeric_version }
|
118
118
|
end
|
119
119
|
|
120
120
|
def filter_out_of_range_versions(versions_array)
|
@@ -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
|
)
|
@@ -247,7 +254,7 @@ module Dependabot
|
|
247
254
|
end
|
248
255
|
|
249
256
|
def run_pip_compile_command(command)
|
250
|
-
run_command("pyenv local #{python_version}")
|
257
|
+
run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
|
251
258
|
run_command(command)
|
252
259
|
end
|
253
260
|
|
@@ -291,7 +298,7 @@ module Dependabot
|
|
291
298
|
end
|
292
299
|
|
293
300
|
# Overwrite the .python-version with updated content
|
294
|
-
File.write(".python-version", python_version)
|
301
|
+
File.write(".python-version", Helpers.python_major_minor(python_version))
|
295
302
|
|
296
303
|
setup_files.each do |file|
|
297
304
|
path = file.name
|
@@ -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]
|
@@ -30,21 +30,18 @@ module Dependabot
|
|
30
30
|
# still better than nothing, though.
|
31
31
|
class PipenvVersionResolver
|
32
32
|
# rubocop:disable Layout/LineLength
|
33
|
-
GIT_DEPENDENCY_UNREACHABLE_REGEX =
|
34
|
-
|
35
|
-
GIT_REFERENCE_NOT_FOUND_REGEX =
|
36
|
-
%r{git checkout -q (?<tag>[^\n"]+)\n?[^\n]*/(?<name>.*?)(\\n'\]|$)}m.
|
37
|
-
freeze
|
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
|
38
35
|
PIPENV_INSTALLATION_ERROR = "pipenv.patched.notpip._internal.exceptions.InstallationError: Command errored out" \
|
39
36
|
" with exit status 1: python setup.py egg_info"
|
40
37
|
TRACEBACK = "Traceback (most recent call last):"
|
41
38
|
PIPENV_INSTALLATION_ERROR_REGEX =
|
42
|
-
/#{Regexp.quote(TRACEBACK)}[\s\S]*^\s+import\s(?<name>.+)[\s\S]*^#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}
|
43
|
-
|
39
|
+
/#{Regexp.quote(TRACEBACK)}[\s\S]*^\s+import\s(?<name>.+)[\s\S]*^#{Regexp.quote(PIPENV_INSTALLATION_ERROR)}/
|
40
|
+
|
44
41
|
UNSUPPORTED_DEPS = %w(pyobjc).freeze
|
45
42
|
UNSUPPORTED_DEP_REGEX =
|
46
|
-
/Could not find a version that satisfies the requirement.*(?:#{UNSUPPORTED_DEPS.join("|")})
|
47
|
-
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/
|
48
45
|
# rubocop:enable Layout/LineLength
|
49
46
|
|
50
47
|
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
@@ -293,7 +290,7 @@ module Dependabot
|
|
293
290
|
end
|
294
291
|
|
295
292
|
# Overwrite the .python-version with updated content
|
296
|
-
File.write(".python-version", python_version)
|
293
|
+
File.write(".python-version", Helpers.python_major_minor(python_version))
|
297
294
|
|
298
295
|
setup_files.each do |file|
|
299
296
|
path = file.name
|
@@ -323,13 +320,7 @@ module Dependabot
|
|
323
320
|
nil
|
324
321
|
end
|
325
322
|
|
326
|
-
|
327
|
-
|
328
|
-
requirements_path = NativeHelpers.python_requirements_path
|
329
|
-
run_command("pyenv install -s #{python_version}")
|
330
|
-
run_command("pyenv exec pip install --upgrade pip")
|
331
|
-
run_command("pyenv exec pip install -r " \
|
332
|
-
"#{requirements_path}")
|
323
|
+
Helpers.install_required_python(python_version)
|
333
324
|
end
|
334
325
|
|
335
326
|
def sanitized_setup_file_content(file)
|
@@ -350,6 +341,7 @@ module Dependabot
|
|
350
341
|
content = freeze_other_dependencies(content)
|
351
342
|
content = set_target_dependency_req(content, updated_requirement)
|
352
343
|
content = add_private_sources(content)
|
344
|
+
content = update_python_requirement(content)
|
353
345
|
content
|
354
346
|
end
|
355
347
|
|
@@ -359,6 +351,12 @@ module Dependabot
|
|
359
351
|
freeze_top_level_dependencies_except([dependency])
|
360
352
|
end
|
361
353
|
|
354
|
+
def update_python_requirement(pipfile_content)
|
355
|
+
Python::FileUpdater::PipfilePreparer.
|
356
|
+
new(pipfile_content: pipfile_content).
|
357
|
+
update_python_requirement(Helpers.python_major_minor(python_version))
|
358
|
+
end
|
359
|
+
|
362
360
|
# rubocop:disable Metrics/PerceivedComplexity
|
363
361
|
def set_target_dependency_req(pipfile_content, updated_requirement)
|
364
362
|
return pipfile_content unless updated_requirement
|
@@ -470,7 +468,7 @@ module Dependabot
|
|
470
468
|
end
|
471
469
|
|
472
470
|
def run_pipenv_command(command, env: pipenv_env_variables)
|
473
|
-
run_command("pyenv local #{python_version}")
|
471
|
+
run_command("pyenv local #{Helpers.python_major_minor(python_version)}")
|
474
472
|
run_command(command, env: env)
|
475
473
|
end
|
476
474
|
|
@@ -28,10 +28,14 @@ module Dependabot
|
|
28
28
|
'checkout',
|
29
29
|
'(?<tag>.+?)'
|
30
30
|
|
|
31
|
+
Failed to checkout
|
32
|
+
(?<tag>.+?)
|
33
|
+
(?<url>.+?).git at '(?<tag>.+?)'
|
34
|
+
|
|
31
35
|
...Failedtoclone
|
32
36
|
(?<url>.+?).gitat'(?<tag>.+?)',
|
33
37
|
verifyrefexistsonremote)
|
34
|
-
/x
|
38
|
+
/x # TODO: remove the first clause and | when py3.6 support is EoL
|
35
39
|
GIT_DEPENDENCY_UNREACHABLE_REGEX = /
|
36
40
|
(?:'\['git',
|
37
41
|
\s+'clone',
|
@@ -43,7 +47,7 @@ module Dependabot
|
|
43
47
|
\s+Failed\sto\sclone
|
44
48
|
\s+(?<url>.+?),
|
45
49
|
\s+check\syour\sgit\sconfiguration)
|
46
|
-
/mx
|
50
|
+
/mx # TODO: remove the first clause and | when py3.6 support is EoL
|
47
51
|
|
48
52
|
attr_reader :dependency, :dependency_files, :credentials
|
49
53
|
|
@@ -88,13 +92,11 @@ module Dependabot
|
|
88
92
|
write_temporary_dependency_files(updated_req: requirement)
|
89
93
|
add_auth_env_vars
|
90
94
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
"#{NativeHelpers.python_requirements_path}"
|
97
|
-
)
|
95
|
+
Helpers.install_required_python(python_version)
|
96
|
+
|
97
|
+
# use system git instead of the pure Python dulwich
|
98
|
+
unless python_version&.start_with?("3.6")
|
99
|
+
run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
|
98
100
|
end
|
99
101
|
|
100
102
|
# Shell out to Poetry, which handles everything for us.
|
@@ -200,7 +202,7 @@ module Dependabot
|
|
200
202
|
end
|
201
203
|
|
202
204
|
# Overwrite the .python-version with updated content
|
203
|
-
File.write(".python-version", python_version) if python_version
|
205
|
+
File.write(".python-version", Helpers.python_major_minor(python_version)) if python_version
|
204
206
|
|
205
207
|
# Overwrite the pyproject with updated content
|
206
208
|
if update_pyproject
|
@@ -282,7 +284,7 @@ module Dependabot
|
|
282
284
|
pyproject_object = TomlRB.parse(pyproject_content)
|
283
285
|
poetry_object = pyproject_object.dig("tool", "poetry")
|
284
286
|
|
285
|
-
Dependabot::Python::FileParser::
|
287
|
+
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
|
286
288
|
names = poetry_object[type]&.keys || []
|
287
289
|
pkg_name = names.find { |nm| normalise(nm) == dependency.name }
|
288
290
|
next unless pkg_name
|
@@ -335,7 +337,7 @@ module Dependabot
|
|
335
337
|
stdout, process = Open3.capture2e(command)
|
336
338
|
time_taken = Time.now - start
|
337
339
|
|
338
|
-
# Raise an error with the output from the shell session if
|
340
|
+
# Raise an error with the output from the shell session if poetry
|
339
341
|
# returns a non-zero status
|
340
342
|
return if process.success?
|
341
343
|
|
@@ -9,8 +9,8 @@ module Dependabot
|
|
9
9
|
module Python
|
10
10
|
class UpdateChecker
|
11
11
|
class RequirementsUpdater
|
12
|
-
PYPROJECT_OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s
|
13
|
-
PYPROJECT_SEPARATOR = /#{PYPROJECT_OR_SEPARATOR}
|
12
|
+
PYPROJECT_OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s*\|+/
|
13
|
+
PYPROJECT_SEPARATOR = /#{PYPROJECT_OR_SEPARATOR}|,/
|
14
14
|
|
15
15
|
class UnfixableRequirement < StandardError; end
|
16
16
|
|
@@ -175,11 +175,25 @@ module Dependabot
|
|
175
175
|
end
|
176
176
|
# rubocop:enable Metrics/PerceivedComplexity
|
177
177
|
|
178
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
179
178
|
def updated_requirement(req)
|
180
179
|
return req unless latest_resolvable_version
|
181
180
|
return req unless req.fetch(:requirement)
|
182
181
|
|
182
|
+
case update_strategy
|
183
|
+
when :bump_versions
|
184
|
+
update_requirement(req)
|
185
|
+
when :bump_versions_if_necessary
|
186
|
+
update_requirement_if_needed(req)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def update_requirement_if_needed(req)
|
191
|
+
return req if new_version_satisfies?(req)
|
192
|
+
|
193
|
+
update_requirement(req)
|
194
|
+
end
|
195
|
+
|
196
|
+
def update_requirement(req)
|
183
197
|
requirement_strings = req[:requirement].split(",").map(&:strip)
|
184
198
|
|
185
199
|
new_requirement =
|
@@ -197,7 +211,6 @@ module Dependabot
|
|
197
211
|
rescue UnfixableRequirement
|
198
212
|
req.merge(requirement: :unfixable)
|
199
213
|
end
|
200
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
201
214
|
|
202
215
|
def new_version_satisfies?(req)
|
203
216
|
requirement_class.
|