dependabot-python 0.215.0 → 0.216.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 -5
- data/helpers/build_for_version +21 -0
- data/helpers/requirements.txt +4 -5
- data/lib/dependabot/python/file_parser/pyproject_files_parser.rb +25 -15
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +16 -43
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +19 -59
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +17 -1
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +39 -34
- data/lib/dependabot/python/language_version_manager.rb +115 -0
- data/lib/dependabot/python/python_versions.rb +6 -6
- data/lib/dependabot/python/requirement_parser.rb +1 -1
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +16 -44
- data/lib/dependabot/python/update_checker/pip_version_resolver.rb +14 -40
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +23 -58
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +46 -44
- data/lib/dependabot/python/update_checker/requirements_updater.rb +25 -6
- data/lib/dependabot/python/update_checker.rb +21 -48
- data/lib/dependabot/python/version.rb +2 -2
- metadata +37 -33
- data/lib/dependabot/python/helpers.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62df822653f41408461f72892b9886776c124a195af5569c6af9e3bdf044e93d
|
4
|
+
data.tar.gz: 8cba64f792d1be98900fe5136d549230d5156d487d07150e0356a147402c5ca3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8c60892487b523ca5879fd08f188dfabcd4d9c4ca17b3317accb3558a22a7731f53129616494230cf62138503e7aece5e9b304fe53bbd525e484197fe74f110
|
7
|
+
data.tar.gz: d7cfe8feb5f919ec01521377366c3716954f898361cc666a1df6384afc3389b9e6db9a92dd4c91cd25fa2e043cbd1aa5a71745d7c030a9627f7c434fcfee5731
|
data/helpers/build
CHANGED
@@ -18,8 +18,8 @@ cp -r \
|
|
18
18
|
"$install_dir"
|
19
19
|
|
20
20
|
cd "$install_dir"
|
21
|
-
PYENV_VERSION=3.11.
|
22
|
-
PYENV_VERSION=3.10.
|
23
|
-
PYENV_VERSION=3.9.
|
24
|
-
PYENV_VERSION=3.8.
|
25
|
-
PYENV_VERSION=3.7.
|
21
|
+
PYENV_VERSION=3.11.3 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
22
|
+
PYENV_VERSION=3.10.11 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
23
|
+
PYENV_VERSION=3.9.16 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
24
|
+
PYENV_VERSION=3.8.16 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
25
|
+
PYENV_VERSION=3.7.16 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
if [ -z "$DEPENDABOT_NATIVE_HELPERS_PATH" ]; then
|
6
|
+
echo "Unable to build, DEPENDABOT_NATIVE_HELPERS_PATH is not set"
|
7
|
+
exit 1
|
8
|
+
fi
|
9
|
+
|
10
|
+
install_dir="$DEPENDABOT_NATIVE_HELPERS_PATH/python"
|
11
|
+
mkdir -p "$install_dir"
|
12
|
+
|
13
|
+
helpers_dir="$(dirname "${BASH_SOURCE[0]}")"
|
14
|
+
cp -r \
|
15
|
+
"$helpers_dir/lib" \
|
16
|
+
"$helpers_dir/run.py" \
|
17
|
+
"$helpers_dir/requirements.txt" \
|
18
|
+
"$install_dir"
|
19
|
+
|
20
|
+
cd "$install_dir"
|
21
|
+
PYENV_VERSION=$1 pyenv exec pip --disable-pip-version-check install --use-pep517 -r "requirements.txt"
|
data/helpers/requirements.txt
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
pip>=21.3.1,<
|
2
|
-
pip-tools>=6.4.0
|
3
|
-
flake8==5.0.4
|
1
|
+
pip>=21.3.1,<23.1.0 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
|
2
|
+
pip-tools>=6.4.0,<=6.12.3 # Range maintains py36 support TODO: Review python 3.6 support in April 2023 (eol ubuntu 18.04)
|
4
3
|
hashin==0.17.0
|
5
4
|
pipenv==2022.4.8
|
6
5
|
pipfile==0.0.2
|
7
|
-
poetry>=1.1.15,<1.
|
6
|
+
poetry>=1.1.15,<1.4.0
|
8
7
|
wheel==0.37.1
|
9
8
|
|
10
9
|
# Some dependencies will only install if Cython is present
|
11
|
-
Cython==0.29.
|
10
|
+
Cython==0.29.34
|
@@ -48,22 +48,13 @@ module Dependabot
|
|
48
48
|
|
49
49
|
POETRY_DEPENDENCY_TYPES.each do |type|
|
50
50
|
deps_hash = parsed_pyproject.dig("tool", "poetry", type) || {}
|
51
|
-
|
52
|
-
deps_hash.each do |name, req|
|
53
|
-
next if normalise(name) == "python"
|
54
|
-
|
55
|
-
requirements = parse_requirements_from(req, type)
|
56
|
-
next if requirements.empty?
|
57
|
-
|
58
|
-
dependencies << Dependency.new(
|
59
|
-
name: normalise(name),
|
60
|
-
version: version_from_lockfile(name),
|
61
|
-
requirements: requirements,
|
62
|
-
package_manager: "pip"
|
63
|
-
)
|
64
|
-
end
|
51
|
+
dependencies += parse_poetry_dependencies(type, deps_hash)
|
65
52
|
end
|
66
53
|
|
54
|
+
groups = parsed_pyproject.dig("tool", "poetry", "group") || {}
|
55
|
+
groups.each do |group, group_spec|
|
56
|
+
dependencies += parse_poetry_dependencies(group, group_spec["dependencies"])
|
57
|
+
end
|
67
58
|
dependencies
|
68
59
|
end
|
69
60
|
|
@@ -101,6 +92,25 @@ module Dependabot
|
|
101
92
|
dependencies
|
102
93
|
end
|
103
94
|
|
95
|
+
def parse_poetry_dependencies(type, deps_hash)
|
96
|
+
dependencies = Dependabot::FileParsers::Base::DependencySet.new
|
97
|
+
|
98
|
+
deps_hash.each do |name, req|
|
99
|
+
next if normalise(name) == "python"
|
100
|
+
|
101
|
+
requirements = parse_requirements_from(req, type)
|
102
|
+
next if requirements.empty?
|
103
|
+
|
104
|
+
dependencies << Dependency.new(
|
105
|
+
name: normalise(name),
|
106
|
+
version: version_from_lockfile(name),
|
107
|
+
requirements: requirements,
|
108
|
+
package_manager: "pip"
|
109
|
+
)
|
110
|
+
end
|
111
|
+
dependencies
|
112
|
+
end
|
113
|
+
|
104
114
|
def normalised_name(name, extras)
|
105
115
|
NameNormaliser.normalise_including_extras(name, extras)
|
106
116
|
end
|
@@ -108,7 +118,7 @@ module Dependabot
|
|
108
118
|
# @param req can be an Array, Hash or String that represents the constraints for a dependency
|
109
119
|
def parse_requirements_from(req, type)
|
110
120
|
[req].flatten.compact.filter_map do |requirement|
|
111
|
-
next if requirement.is_a?(Hash) && (
|
121
|
+
next if requirement.is_a?(Hash) && UNSUPPORTED_DEPENDENCY_TYPES.intersect?(requirement.keys)
|
112
122
|
|
113
123
|
check_requirements(requirement)
|
114
124
|
|
@@ -7,7 +7,7 @@ require "dependabot/python/file_fetcher"
|
|
7
7
|
require "dependabot/python/file_parser/python_requirement_parser"
|
8
8
|
require "dependabot/python/file_updater"
|
9
9
|
require "dependabot/shared_helpers"
|
10
|
-
require "dependabot/python/
|
10
|
+
require "dependabot/python/language_version_manager"
|
11
11
|
require "dependabot/python/native_helpers"
|
12
12
|
require "dependabot/python/python_versions"
|
13
13
|
require "dependabot/python/name_normaliser"
|
@@ -26,6 +26,7 @@ module Dependabot
|
|
26
26
|
INCOMPATIBLE_VERSIONS_REGEX = /There are incompatible versions in the resolved dependencies:.*\z/m
|
27
27
|
WARNINGS = /\s*# WARNING:.*\Z/m
|
28
28
|
UNSAFE_NOTE = /\s*# The following packages are considered to be unsafe.*\Z/m
|
29
|
+
RESOLVER_REGEX = /(?<=--resolver=)(\w+)/
|
29
30
|
|
30
31
|
attr_reader :dependencies, :dependency_files, :credentials
|
31
32
|
|
@@ -66,7 +67,7 @@ module Dependabot
|
|
66
67
|
def compile_new_requirement_files
|
67
68
|
SharedHelpers.in_a_temporary_directory do
|
68
69
|
write_updated_dependency_files
|
69
|
-
|
70
|
+
language_version_manager.install_required_python
|
70
71
|
|
71
72
|
filenames_to_compile.each do |filename|
|
72
73
|
# Shell out to pip-compile, generate a new set of requirements.
|
@@ -176,7 +177,7 @@ module Dependabot
|
|
176
177
|
|
177
178
|
def run_pip_compile_command(command, allow_unsafe_shell_command: false, fingerprint:)
|
178
179
|
run_command(
|
179
|
-
"pyenv local #{
|
180
|
+
"pyenv local #{language_version_manager.python_major_minor}",
|
180
181
|
fingerprint: "pyenv local <python_major_minor>"
|
181
182
|
)
|
182
183
|
|
@@ -210,7 +211,7 @@ module Dependabot
|
|
210
211
|
end
|
211
212
|
|
212
213
|
# Overwrite the .python-version with updated content
|
213
|
-
File.write(".python-version",
|
214
|
+
File.write(".python-version", language_version_manager.python_major_minor)
|
214
215
|
|
215
216
|
setup_files.each do |file|
|
216
217
|
path = file.name
|
@@ -441,6 +442,10 @@ module Dependabot
|
|
441
442
|
|
442
443
|
options << "--strip-extras" if requirements_file.content.include?("--strip-extras")
|
443
444
|
|
445
|
+
if (resolver = RESOLVER_REGEX.match(requirements_file.content))
|
446
|
+
options << "--resolver=#{resolver}"
|
447
|
+
end
|
448
|
+
|
444
449
|
options
|
445
450
|
end
|
446
451
|
|
@@ -517,9 +522,9 @@ module Dependabot
|
|
517
522
|
while (remaining_filenames = filenames - ordered_filenames).any?
|
518
523
|
ordered_filenames +=
|
519
524
|
remaining_filenames.
|
520
|
-
|
525
|
+
reject do |fn|
|
521
526
|
unupdated_reqs = requirement_map[fn] - ordered_filenames
|
522
|
-
(
|
527
|
+
unupdated_reqs.intersect?(filenames)
|
523
528
|
end
|
524
529
|
end
|
525
530
|
|
@@ -545,41 +550,6 @@ module Dependabot
|
|
545
550
|
end
|
546
551
|
end
|
547
552
|
|
548
|
-
def python_version
|
549
|
-
@python_version ||=
|
550
|
-
user_specified_python_version ||
|
551
|
-
python_version_matching_imputed_requirements ||
|
552
|
-
PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
|
553
|
-
end
|
554
|
-
|
555
|
-
def user_specified_python_version
|
556
|
-
return unless python_requirement_parser.user_specified_requirements.any?
|
557
|
-
|
558
|
-
user_specified_requirements =
|
559
|
-
python_requirement_parser.user_specified_requirements.
|
560
|
-
map { |r| Python::Requirement.requirements_array(r) }
|
561
|
-
python_version_matching(user_specified_requirements)
|
562
|
-
end
|
563
|
-
|
564
|
-
def python_version_matching_imputed_requirements
|
565
|
-
compiled_file_python_requirement_markers =
|
566
|
-
python_requirement_parser.imputed_requirements.map do |r|
|
567
|
-
Dependabot::Python::Requirement.new(r)
|
568
|
-
end
|
569
|
-
python_version_matching(compiled_file_python_requirement_markers)
|
570
|
-
end
|
571
|
-
|
572
|
-
def python_version_matching(requirements)
|
573
|
-
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version_string|
|
574
|
-
version = Python::Version.new(version_string)
|
575
|
-
requirements.all? do |req|
|
576
|
-
next req.any? { |r| r.satisfied_by?(version) } if req.is_a?(Array)
|
577
|
-
|
578
|
-
req.satisfied_by?(version)
|
579
|
-
end
|
580
|
-
end
|
581
|
-
end
|
582
|
-
|
583
553
|
def python_requirement_parser
|
584
554
|
@python_requirement_parser ||=
|
585
555
|
FileParser::PythonRequirementParser.new(
|
@@ -587,8 +557,11 @@ module Dependabot
|
|
587
557
|
)
|
588
558
|
end
|
589
559
|
|
590
|
-
def
|
591
|
-
|
560
|
+
def language_version_manager
|
561
|
+
@language_version_manager ||=
|
562
|
+
LanguageVersionManager.new(
|
563
|
+
python_requirement_parser: python_requirement_parser
|
564
|
+
)
|
592
565
|
end
|
593
566
|
|
594
567
|
def setup_files
|
@@ -6,6 +6,7 @@ require "dependabot/dependency"
|
|
6
6
|
require "dependabot/python/requirement_parser"
|
7
7
|
require "dependabot/python/file_parser/python_requirement_parser"
|
8
8
|
require "dependabot/python/file_updater"
|
9
|
+
require "dependabot/python/language_version_manager"
|
9
10
|
require "dependabot/shared_helpers"
|
10
11
|
require "dependabot/python/native_helpers"
|
11
12
|
require "dependabot/python/name_normaliser"
|
@@ -146,7 +147,7 @@ module Dependabot
|
|
146
147
|
def update_python_requirement(pipfile_content)
|
147
148
|
PipfilePreparer.
|
148
149
|
new(pipfile_content: pipfile_content).
|
149
|
-
update_python_requirement(
|
150
|
+
update_python_requirement(language_version_manager.python_major_minor)
|
150
151
|
end
|
151
152
|
|
152
153
|
# rubocop:disable Metrics/PerceivedComplexity
|
@@ -198,10 +199,6 @@ module Dependabot
|
|
198
199
|
write_temporary_dependency_files(prepared_pipfile_content)
|
199
200
|
install_required_python
|
200
201
|
|
201
|
-
# Initialize a git repo to appease pip-tools
|
202
|
-
command = SharedHelpers.escape_command("git init")
|
203
|
-
IO.popen(command, err: %i(child out)) if setup_files.any?
|
204
|
-
|
205
202
|
run_pipenv_command(
|
206
203
|
"pyenv exec pipenv lock"
|
207
204
|
)
|
@@ -271,7 +268,7 @@ module Dependabot
|
|
271
268
|
end
|
272
269
|
|
273
270
|
def run_pipenv_command(command, env: pipenv_env_variables)
|
274
|
-
run_command("pyenv local #{
|
271
|
+
run_command("pyenv local #{language_version_manager.python_major_minor}")
|
275
272
|
run_command(command, env: env)
|
276
273
|
end
|
277
274
|
|
@@ -283,7 +280,7 @@ module Dependabot
|
|
283
280
|
end
|
284
281
|
|
285
282
|
# Overwrite the .python-version with updated content
|
286
|
-
File.write(".python-version",
|
283
|
+
File.write(".python-version", language_version_manager.python_major_minor)
|
287
284
|
|
288
285
|
setup_files.each do |file|
|
289
286
|
path = file.name
|
@@ -309,7 +306,7 @@ module Dependabot
|
|
309
306
|
nil
|
310
307
|
end
|
311
308
|
|
312
|
-
|
309
|
+
language_version_manager.install_required_python
|
313
310
|
end
|
314
311
|
|
315
312
|
def sanitized_setup_file_content(file)
|
@@ -322,57 +319,6 @@ module Dependabot
|
|
322
319
|
sanitized_content
|
323
320
|
end
|
324
321
|
|
325
|
-
def python_version
|
326
|
-
@python_version ||= python_version_from_supported_versions
|
327
|
-
end
|
328
|
-
|
329
|
-
def python_version_from_supported_versions
|
330
|
-
requirement_string =
|
331
|
-
if @using_python_two then "2.7.*"
|
332
|
-
elsif user_specified_python_requirement
|
333
|
-
parts = user_specified_python_requirement.split(".")
|
334
|
-
parts.fill("*", (parts.length)..2).join(".")
|
335
|
-
else
|
336
|
-
PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
|
337
|
-
end
|
338
|
-
|
339
|
-
# Ideally, the requirement is satisfied by a Python version we support
|
340
|
-
requirement =
|
341
|
-
Python::Requirement.requirements_array(requirement_string).first
|
342
|
-
version =
|
343
|
-
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
|
344
|
-
find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
|
345
|
-
return version if version
|
346
|
-
|
347
|
-
# If not, and changing the patch version would fix things, we do that
|
348
|
-
# as the patch version is unlikely to affect resolution
|
349
|
-
requirement =
|
350
|
-
Python::Requirement.new(requirement_string.gsub(/\.\d+$/, ".*"))
|
351
|
-
version =
|
352
|
-
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
|
353
|
-
find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
|
354
|
-
return version if version
|
355
|
-
|
356
|
-
# Otherwise we have to raise, giving details of the Python versions
|
357
|
-
# that Dependabot supports
|
358
|
-
msg = "Dependabot detected the following Python requirement " \
|
359
|
-
"for your project: '#{requirement_string}'.\n\nCurrently, the " \
|
360
|
-
"following Python versions are supported in Dependabot: " \
|
361
|
-
"#{PythonVersions::SUPPORTED_VERSIONS.join(', ')}."
|
362
|
-
raise DependencyFileNotResolvable, msg
|
363
|
-
end
|
364
|
-
|
365
|
-
def user_specified_python_requirement
|
366
|
-
python_requirement_parser.user_specified_requirements.first
|
367
|
-
end
|
368
|
-
|
369
|
-
def python_requirement_parser
|
370
|
-
@python_requirement_parser ||=
|
371
|
-
FileParser::PythonRequirementParser.new(
|
372
|
-
dependency_files: dependency_files
|
373
|
-
)
|
374
|
-
end
|
375
|
-
|
376
322
|
def setup_cfg(file)
|
377
323
|
dependency_files.find do |f|
|
378
324
|
f.name == file.name.sub(/\.py$/, ".cfg")
|
@@ -400,6 +346,20 @@ module Dependabot
|
|
400
346
|
NameNormaliser.normalise(name)
|
401
347
|
end
|
402
348
|
|
349
|
+
def python_requirement_parser
|
350
|
+
@python_requirement_parser ||=
|
351
|
+
FileParser::PythonRequirementParser.new(
|
352
|
+
dependency_files: dependency_files
|
353
|
+
)
|
354
|
+
end
|
355
|
+
|
356
|
+
def language_version_manager
|
357
|
+
@language_version_manager ||=
|
358
|
+
LanguageVersionManager.new(
|
359
|
+
python_requirement_parser: python_requirement_parser
|
360
|
+
)
|
361
|
+
end
|
362
|
+
|
403
363
|
def parsed_lockfile
|
404
364
|
@parsed_lockfile ||= JSON.parse(lockfile.content)
|
405
365
|
end
|
@@ -21,7 +21,7 @@ module Dependabot
|
|
21
21
|
pipfile_object = TomlRB.parse(pipfile_content)
|
22
22
|
|
23
23
|
pipfile_object["source"] =
|
24
|
-
pipfile_sources.
|
24
|
+
pipfile_sources.filter_map { |h| sub_auth_url(h, credentials) } +
|
25
25
|
config_variable_sources(credentials)
|
26
26
|
|
27
27
|
TomlRB.dump(pipfile_object)
|
@@ -114,6 +114,22 @@ module Dependabot
|
|
114
114
|
map { |h| h.dup.merge("url" => h["url"].gsub(%r{/*$}, "") + "/") }
|
115
115
|
end
|
116
116
|
|
117
|
+
def sub_auth_url(source, credentials)
|
118
|
+
if source["url"].include?("${")
|
119
|
+
base_url = source["url"].sub(/\${.*}@/, "")
|
120
|
+
|
121
|
+
source_cred = credentials.
|
122
|
+
select { |cred| cred["type"] == "python_index" }.
|
123
|
+
find { |c| c["index-url"].sub(/\${.*}@/, "") == base_url }
|
124
|
+
|
125
|
+
return nil if source_cred.nil?
|
126
|
+
|
127
|
+
source["url"] = AuthedUrlBuilder.authed_url(credential: source_cred)
|
128
|
+
end
|
129
|
+
|
130
|
+
source
|
131
|
+
end
|
132
|
+
|
117
133
|
def config_variable_sources(credentials)
|
118
134
|
@config_variable_sources ||=
|
119
135
|
credentials.
|
@@ -4,7 +4,7 @@ require "toml-rb"
|
|
4
4
|
require "open3"
|
5
5
|
require "dependabot/dependency"
|
6
6
|
require "dependabot/shared_helpers"
|
7
|
-
require "dependabot/python/
|
7
|
+
require "dependabot/python/language_version_manager"
|
8
8
|
require "dependabot/python/version"
|
9
9
|
require "dependabot/python/requirement"
|
10
10
|
require "dependabot/python/python_versions"
|
@@ -75,10 +75,17 @@ module Dependabot
|
|
75
75
|
find { |r| r[:file] == pyproject.name }.
|
76
76
|
fetch(:requirement)
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
declaration_regex = declaration_regex(dep)
|
79
|
+
updated_content = if content.match?(declaration_regex)
|
80
|
+
content.gsub(declaration_regex(dep)) do |match|
|
81
|
+
match.gsub(old_req, updated_requirement)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
content.gsub(table_declaration_regex(dep)) do |match|
|
85
|
+
match.gsub(/(\s*version\s*=\s*["'])#{Regexp.escape(old_req)}/,
|
86
|
+
'\1' + updated_requirement)
|
87
|
+
end
|
88
|
+
end
|
82
89
|
|
83
90
|
raise "Content did not change!" if content == updated_content
|
84
91
|
|
@@ -135,7 +142,7 @@ module Dependabot
|
|
135
142
|
def update_python_requirement(pyproject_content)
|
136
143
|
PyprojectPreparer.
|
137
144
|
new(pyproject_content: pyproject_content).
|
138
|
-
update_python_requirement(
|
145
|
+
update_python_requirement(language_version_manager.python_major_minor)
|
139
146
|
end
|
140
147
|
|
141
148
|
def lock_declaration_to_new_version!(poetry_object, dep)
|
@@ -178,10 +185,10 @@ module Dependabot
|
|
178
185
|
write_temporary_dependency_files(pyproject_content)
|
179
186
|
add_auth_env_vars
|
180
187
|
|
181
|
-
|
188
|
+
language_version_manager.install_required_python
|
182
189
|
|
183
190
|
# use system git instead of the pure Python dulwich
|
184
|
-
unless python_version&.start_with?("3.6")
|
191
|
+
unless language_version_manager.python_version&.start_with?("3.6")
|
185
192
|
run_poetry_command("pyenv exec poetry config experimental.system-git-client true")
|
186
193
|
end
|
187
194
|
|
@@ -232,7 +239,7 @@ module Dependabot
|
|
232
239
|
end
|
233
240
|
|
234
241
|
# Overwrite the .python-version with updated content
|
235
|
-
File.write(".python-version",
|
242
|
+
File.write(".python-version", language_version_manager.python_major_minor)
|
236
243
|
|
237
244
|
# Overwrite the pyproject with updated content
|
238
245
|
File.write("pyproject.toml", pyproject_content)
|
@@ -244,29 +251,6 @@ module Dependabot
|
|
244
251
|
add_auth_env_vars(credentials)
|
245
252
|
end
|
246
253
|
|
247
|
-
def python_version
|
248
|
-
requirements = python_requirement_parser.user_specified_requirements
|
249
|
-
requirements = requirements.
|
250
|
-
map { |r| Python::Requirement.requirements_array(r) }
|
251
|
-
|
252
|
-
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version|
|
253
|
-
requirements.all? do |reqs|
|
254
|
-
reqs.any? { |r| r.satisfied_by?(Python::Version.new(version)) }
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
def python_requirement_parser
|
260
|
-
@python_requirement_parser ||=
|
261
|
-
FileParser::PythonRequirementParser.new(
|
262
|
-
dependency_files: dependency_files
|
263
|
-
)
|
264
|
-
end
|
265
|
-
|
266
|
-
def pre_installed_python?(version)
|
267
|
-
PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.include?(version)
|
268
|
-
end
|
269
|
-
|
270
254
|
def pyproject_hash_for(pyproject_content)
|
271
255
|
SharedHelpers.in_a_temporary_directory do |dir|
|
272
256
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
@@ -282,8 +266,15 @@ module Dependabot
|
|
282
266
|
end
|
283
267
|
|
284
268
|
def declaration_regex(dep)
|
285
|
-
|
286
|
-
|
269
|
+
/(?:^\s*|["'])#{escape(dep)}["']?\s*=.*$/i
|
270
|
+
end
|
271
|
+
|
272
|
+
def table_declaration_regex(dep)
|
273
|
+
/tool\.poetry\.[^\n]+\.#{escape(dep)}\]\n.*?\s*version\s* =.*?\n/m
|
274
|
+
end
|
275
|
+
|
276
|
+
def escape(dep)
|
277
|
+
Regexp.escape(dep.name).gsub("\\-", "[-_.]")
|
287
278
|
end
|
288
279
|
|
289
280
|
def file_changed?(file)
|
@@ -307,6 +298,20 @@ module Dependabot
|
|
307
298
|
NameNormaliser.normalise(name)
|
308
299
|
end
|
309
300
|
|
301
|
+
def python_requirement_parser
|
302
|
+
@python_requirement_parser ||=
|
303
|
+
FileParser::PythonRequirementParser.new(
|
304
|
+
dependency_files: dependency_files
|
305
|
+
)
|
306
|
+
end
|
307
|
+
|
308
|
+
def language_version_manager
|
309
|
+
@language_version_manager ||=
|
310
|
+
LanguageVersionManager.new(
|
311
|
+
python_requirement_parser: python_requirement_parser
|
312
|
+
)
|
313
|
+
end
|
314
|
+
|
310
315
|
def pyproject
|
311
316
|
@pyproject ||=
|
312
317
|
dependency_files.find { |f| f.name == "pyproject.toml" }
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/logger"
|
4
|
+
require "dependabot/python/version"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Python
|
8
|
+
class LanguageVersionManager
|
9
|
+
def initialize(python_requirement_parser:)
|
10
|
+
@python_requirement_parser = python_requirement_parser
|
11
|
+
end
|
12
|
+
|
13
|
+
def install_required_python
|
14
|
+
# The leading space is important in the version check
|
15
|
+
return if SharedHelpers.run_shell_command("pyenv versions").include?(" #{python_major_minor}.")
|
16
|
+
|
17
|
+
if File.exist?("/usr/local/.pyenv/#{python_major_minor}.tar.gz")
|
18
|
+
SharedHelpers.run_shell_command(
|
19
|
+
"tar xzf /usr/local/.pyenv/#{python_major_minor}.tar.gz -C /usr/local/.pyenv/"
|
20
|
+
)
|
21
|
+
return if SharedHelpers.run_shell_command("pyenv versions").
|
22
|
+
include?(" #{python_major_minor}.")
|
23
|
+
end
|
24
|
+
|
25
|
+
Dependabot.logger.info("Installing required Python #{python_version}.")
|
26
|
+
start = Time.now
|
27
|
+
SharedHelpers.run_shell_command("pyenv install -s #{python_version}")
|
28
|
+
SharedHelpers.run_shell_command("pyenv exec pip install --upgrade pip")
|
29
|
+
SharedHelpers.run_shell_command("pyenv exec pip install -r" \
|
30
|
+
"#{NativeHelpers.python_requirements_path}")
|
31
|
+
time_taken = Time.now - start
|
32
|
+
Dependabot.logger.info("Installing Python #{python_version} took #{time_taken}s.")
|
33
|
+
end
|
34
|
+
|
35
|
+
def python_major_minor
|
36
|
+
@python ||= Python::Version.new(python_version)
|
37
|
+
"#{@python.segments[0]}.#{@python.segments[1]}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def python_version
|
41
|
+
@python_version ||= python_version_from_supported_versions
|
42
|
+
end
|
43
|
+
|
44
|
+
def python_requirement_string
|
45
|
+
if user_specified_python_version
|
46
|
+
if user_specified_python_version.start_with?(/\d/)
|
47
|
+
parts = user_specified_python_version.split(".")
|
48
|
+
parts.fill("*", (parts.length)..2).join(".")
|
49
|
+
else
|
50
|
+
user_specified_python_version
|
51
|
+
end
|
52
|
+
elsif python_version_matching_imputed_requirements
|
53
|
+
python_version_matching_imputed_requirements
|
54
|
+
else
|
55
|
+
PythonVersions::PRE_INSTALLED_PYTHON_VERSIONS.first
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def python_version_from_supported_versions
|
60
|
+
requirement_string = python_requirement_string
|
61
|
+
|
62
|
+
# Ideally, the requirement is satisfied by a Python version we support
|
63
|
+
requirement =
|
64
|
+
Python::Requirement.requirements_array(requirement_string).first
|
65
|
+
version =
|
66
|
+
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
|
67
|
+
find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
|
68
|
+
return version if version
|
69
|
+
|
70
|
+
# If not, and we're dealing with a simple version string
|
71
|
+
# and changing the patch version would fix things, we do that
|
72
|
+
# as the patch version is unlikely to affect resolution
|
73
|
+
if requirement_string.start_with?(/\d/)
|
74
|
+
requirement =
|
75
|
+
Python::Requirement.new(requirement_string.gsub(/\.\d+$/, ".*"))
|
76
|
+
version =
|
77
|
+
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.
|
78
|
+
find { |v| requirement.satisfied_by?(Python::Version.new(v)) }
|
79
|
+
return version if version
|
80
|
+
end
|
81
|
+
|
82
|
+
# Otherwise we have to raise, giving details of the Python versions
|
83
|
+
# that Dependabot supports
|
84
|
+
msg = "Dependabot detected the following Python requirement " \
|
85
|
+
"for your project: '#{requirement_string}'.\n\nCurrently, the " \
|
86
|
+
"following Python versions are supported in Dependabot: " \
|
87
|
+
"#{PythonVersions::SUPPORTED_VERSIONS.join(', ')}."
|
88
|
+
raise DependencyFileNotResolvable, msg
|
89
|
+
end
|
90
|
+
|
91
|
+
def user_specified_python_version
|
92
|
+
@python_requirement_parser.user_specified_requirements.first
|
93
|
+
end
|
94
|
+
|
95
|
+
def python_version_matching_imputed_requirements
|
96
|
+
compiled_file_python_requirement_markers =
|
97
|
+
@python_requirement_parser.imputed_requirements.map do |r|
|
98
|
+
Dependabot::Python::Requirement.new(r)
|
99
|
+
end
|
100
|
+
python_version_matching(compiled_file_python_requirement_markers)
|
101
|
+
end
|
102
|
+
|
103
|
+
def python_version_matching(requirements)
|
104
|
+
PythonVersions::SUPPORTED_VERSIONS_TO_ITERATE.find do |version_string|
|
105
|
+
version = Python::Version.new(version_string)
|
106
|
+
requirements.all? do |req|
|
107
|
+
next req.any? { |r| r.satisfied_by?(version) } if req.is_a?(Array)
|
108
|
+
|
109
|
+
req.satisfied_by?(version)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|