dependabot-python 0.212.0 → 0.214.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 +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.
|