dependabot-python 0.236.0 → 0.238.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/requirements.txt +4 -4
- data/lib/dependabot/python/file_fetcher.rb +7 -2
- data/lib/dependabot/python/file_parser/pipfile_files_parser.rb +2 -1
- data/lib/dependabot/python/file_parser/pyproject_files_parser.rb +15 -6
- data/lib/dependabot/python/file_parser/python_requirement_parser.rb +24 -0
- data/lib/dependabot/python/file_updater/pipfile_file_updater.rb +21 -78
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +1 -67
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +3 -2
- data/lib/dependabot/python/file_updater.rb +2 -1
- data/lib/dependabot/python/pipenv_runner.rb +84 -0
- data/lib/dependabot/python/update_checker/index_finder.rb +8 -3
- data/lib/dependabot/python/update_checker/latest_version_finder.rb +2 -1
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +3 -2
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +43 -147
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +3 -2
- data/lib/dependabot/python/update_checker.rb +5 -4
- data/lib/dependabot/python.rb +3 -0
- metadata +22 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b05bd36f835c00c6533153183b23d848d6b06759d20dc4e643cc2fd5e9a8c5e6
|
|
4
|
+
data.tar.gz: 0c08d7fdb367c16636cd558dca7ad36e16bba8851b4f906ac076d8a28b45bbdb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a7faaff71e67be34ad8d152bb5f869313f69a7a031cf4ffe4f1b2e5cc565e40ca0d9e9e5e2c470ff5477b5ab8c166ac29fc2323ac2c7548ec7e684348fac98b
|
|
7
|
+
data.tar.gz: 78adf7610fff0b22a46ac7d105a6463e1040505de29ecb15e9b087930bdfe5ba54cf6755bdefbff610a9009ae6a6bac6ae59997cae050c9dae06cd31ec1eda0d
|
data/helpers/requirements.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
pip==23.
|
|
1
|
+
pip==23.3.1
|
|
2
2
|
pip-tools==7.3.0
|
|
3
3
|
flake8==6.1.0
|
|
4
4
|
hashin==0.17.0
|
|
5
|
-
pipenv
|
|
5
|
+
pipenv@git+https://github.com/pypa/pipenv@main
|
|
6
6
|
pipfile==0.0.2
|
|
7
|
-
poetry==1.
|
|
7
|
+
poetry==1.7.1
|
|
8
8
|
|
|
9
9
|
# Some dependencies will only install if Cython is present
|
|
10
|
-
Cython==3.0.
|
|
10
|
+
Cython==3.0.5
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "toml-rb"
|
|
5
|
+
require "sorbet-runtime"
|
|
5
6
|
|
|
6
7
|
require "dependabot/file_fetchers"
|
|
7
8
|
require "dependabot/file_fetchers/base"
|
|
@@ -15,6 +16,9 @@ require "dependabot/errors"
|
|
|
15
16
|
module Dependabot
|
|
16
17
|
module Python
|
|
17
18
|
class FileFetcher < Dependabot::FileFetchers::Base
|
|
19
|
+
extend T::Sig
|
|
20
|
+
extend T::Helpers
|
|
21
|
+
|
|
18
22
|
CHILD_REQUIREMENT_REGEX = /^-r\s?(?<path>.*\.(?:txt|in))/
|
|
19
23
|
CONSTRAINT_REGEX = /^-c\s?(?<path>.*\.(?:txt|in))/
|
|
20
24
|
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
|
@@ -63,8 +67,7 @@ module Dependabot
|
|
|
63
67
|
}
|
|
64
68
|
end
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
|
68
71
|
def fetch_files
|
|
69
72
|
fetched_files = []
|
|
70
73
|
|
|
@@ -84,6 +87,8 @@ module Dependabot
|
|
|
84
87
|
uniq_files(fetched_files)
|
|
85
88
|
end
|
|
86
89
|
|
|
90
|
+
private
|
|
91
|
+
|
|
87
92
|
def uniq_files(fetched_files)
|
|
88
93
|
uniq_files = fetched_files.reject(&:support_file?).uniq
|
|
89
94
|
uniq_files += fetched_files
|
|
@@ -137,12 +137,21 @@ module Dependabot
|
|
|
137
137
|
|
|
138
138
|
check_requirements(requirement)
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
if requirement.is_a?(String)
|
|
141
|
+
{
|
|
142
|
+
requirement: requirement,
|
|
143
|
+
file: pyproject.name,
|
|
144
|
+
source: nil,
|
|
145
|
+
groups: [type]
|
|
146
|
+
}
|
|
147
|
+
else
|
|
148
|
+
{
|
|
149
|
+
requirement: requirement["version"],
|
|
150
|
+
file: pyproject.name,
|
|
151
|
+
source: requirement.fetch("source", nil),
|
|
152
|
+
groups: [type]
|
|
153
|
+
}
|
|
154
|
+
end
|
|
146
155
|
end
|
|
147
156
|
end
|
|
148
157
|
|
|
@@ -6,6 +6,7 @@ require "open3"
|
|
|
6
6
|
require "dependabot/errors"
|
|
7
7
|
require "dependabot/shared_helpers"
|
|
8
8
|
require "dependabot/python/file_parser"
|
|
9
|
+
require "dependabot/python/pip_compile_file_matcher"
|
|
9
10
|
require "dependabot/python/requirement"
|
|
10
11
|
|
|
11
12
|
module Dependabot
|
|
@@ -22,6 +23,7 @@ module Dependabot
|
|
|
22
23
|
[
|
|
23
24
|
pipfile_python_requirement,
|
|
24
25
|
pyproject_python_requirement,
|
|
26
|
+
pip_compile_python_requirement,
|
|
25
27
|
python_version_file_version,
|
|
26
28
|
runtime_file_python_version,
|
|
27
29
|
setup_file_requirement
|
|
@@ -64,6 +66,20 @@ module Dependabot
|
|
|
64
66
|
poetry_object&.dig("dev-dependencies", "python")
|
|
65
67
|
end
|
|
66
68
|
|
|
69
|
+
def pip_compile_python_requirement
|
|
70
|
+
requirement_files.each do |file|
|
|
71
|
+
next unless pip_compile_file_matcher.lockfile_for_pip_compile_file?(file)
|
|
72
|
+
|
|
73
|
+
marker = /^# This file is autogenerated by pip-compile with [pP]ython (?<version>\d+.\d+)$/m
|
|
74
|
+
match = marker.match(file.content)
|
|
75
|
+
next unless match
|
|
76
|
+
|
|
77
|
+
return match[:version]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
67
83
|
def python_version_file_version
|
|
68
84
|
return unless python_version_file
|
|
69
85
|
|
|
@@ -106,6 +122,10 @@ module Dependabot
|
|
|
106
122
|
SharedHelpers.run_shell_command(command, env: env, stderr_to_stdout: true)
|
|
107
123
|
end
|
|
108
124
|
|
|
125
|
+
def pip_compile_file_matcher
|
|
126
|
+
@pip_compile_file_matcher ||= PipCompileFileMatcher.new(pip_compile_files)
|
|
127
|
+
end
|
|
128
|
+
|
|
109
129
|
def requirement_class
|
|
110
130
|
Dependabot::Python::Requirement
|
|
111
131
|
end
|
|
@@ -144,6 +164,10 @@ module Dependabot
|
|
|
144
164
|
def requirement_files
|
|
145
165
|
dependency_files.select { |f| f.name.end_with?(".txt") }
|
|
146
166
|
end
|
|
167
|
+
|
|
168
|
+
def pip_compile_files
|
|
169
|
+
dependency_files.select { |f| f.name.end_with?(".in") }
|
|
170
|
+
end
|
|
147
171
|
end
|
|
148
172
|
end
|
|
149
173
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "toml-rb"
|
|
5
4
|
require "open3"
|
|
6
5
|
require "dependabot/dependency"
|
|
7
6
|
require "dependabot/python/requirement_parser"
|
|
@@ -10,7 +9,7 @@ require "dependabot/python/file_updater"
|
|
|
10
9
|
require "dependabot/python/language_version_manager"
|
|
11
10
|
require "dependabot/shared_helpers"
|
|
12
11
|
require "dependabot/python/native_helpers"
|
|
13
|
-
require "dependabot/python/
|
|
12
|
+
require "dependabot/python/pipenv_runner"
|
|
14
13
|
|
|
15
14
|
module Dependabot
|
|
16
15
|
module Python
|
|
@@ -22,12 +21,13 @@ module Dependabot
|
|
|
22
21
|
|
|
23
22
|
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
|
24
23
|
|
|
25
|
-
attr_reader :dependencies, :dependency_files, :credentials
|
|
24
|
+
attr_reader :dependencies, :dependency_files, :credentials, :repo_contents_path
|
|
26
25
|
|
|
27
|
-
def initialize(dependencies:, dependency_files:, credentials:)
|
|
26
|
+
def initialize(dependencies:, dependency_files:, credentials:, repo_contents_path:)
|
|
28
27
|
@dependencies = dependencies
|
|
29
28
|
@dependency_files = dependency_files
|
|
30
29
|
@credentials = credentials
|
|
30
|
+
@repo_contents_path = repo_contents_path
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def updated_dependency_files
|
|
@@ -83,7 +83,6 @@ module Dependabot
|
|
|
83
83
|
return [] unless lockfile
|
|
84
84
|
|
|
85
85
|
pipfile_lock_deps = parsed_lockfile[type]&.keys&.sort || []
|
|
86
|
-
pipfile_lock_deps = pipfile_lock_deps.map { |n| normalise(n) }
|
|
87
86
|
return [] unless pipfile_lock_deps.any?
|
|
88
87
|
|
|
89
88
|
regex = RequirementParser::INSTALL_REQ_WITH_REQUIREMENT
|
|
@@ -94,7 +93,7 @@ module Dependabot
|
|
|
94
93
|
requirements_files.select do |req_file|
|
|
95
94
|
deps = []
|
|
96
95
|
req_file.content.scan(regex) { deps << Regexp.last_match }
|
|
97
|
-
deps = deps.map { |m|
|
|
96
|
+
deps = deps.map { |m| m[:name] }
|
|
98
97
|
deps.sort == pipfile_lock_deps
|
|
99
98
|
end
|
|
100
99
|
end
|
|
@@ -129,61 +128,17 @@ module Dependabot
|
|
|
129
128
|
|
|
130
129
|
def prepared_pipfile_content
|
|
131
130
|
content = updated_pipfile_content
|
|
132
|
-
content = freeze_other_dependencies(content)
|
|
133
|
-
content = freeze_dependencies_being_updated(content)
|
|
134
131
|
content = add_private_sources(content)
|
|
135
132
|
content = update_python_requirement(content)
|
|
136
133
|
content
|
|
137
134
|
end
|
|
138
135
|
|
|
139
|
-
def freeze_other_dependencies(pipfile_content)
|
|
140
|
-
PipfilePreparer
|
|
141
|
-
.new(pipfile_content: pipfile_content, lockfile: lockfile)
|
|
142
|
-
.freeze_top_level_dependencies_except(dependencies)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
136
|
def update_python_requirement(pipfile_content)
|
|
146
137
|
PipfilePreparer
|
|
147
138
|
.new(pipfile_content: pipfile_content)
|
|
148
139
|
.update_python_requirement(language_version_manager.python_major_minor)
|
|
149
140
|
end
|
|
150
141
|
|
|
151
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
152
|
-
def freeze_dependencies_being_updated(pipfile_content)
|
|
153
|
-
pipfile_object = TomlRB.parse(pipfile_content)
|
|
154
|
-
|
|
155
|
-
dependencies.each do |dep|
|
|
156
|
-
DEPENDENCY_TYPES.each do |type|
|
|
157
|
-
names = pipfile_object[type]&.keys || []
|
|
158
|
-
pkg_name = names.find { |nm| normalise(nm) == dep.name }
|
|
159
|
-
next unless pkg_name || subdep_type?(type)
|
|
160
|
-
|
|
161
|
-
pkg_name ||= dependency.name
|
|
162
|
-
if pipfile_object[type][pkg_name].is_a?(Hash)
|
|
163
|
-
pipfile_object[type][pkg_name]["version"] =
|
|
164
|
-
"==#{dep.version}"
|
|
165
|
-
else
|
|
166
|
-
pipfile_object[type][pkg_name] = "==#{dep.version}"
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
TomlRB.dump(pipfile_object)
|
|
172
|
-
end
|
|
173
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
174
|
-
|
|
175
|
-
def subdep_type?(type)
|
|
176
|
-
return false if dependency.top_level?
|
|
177
|
-
|
|
178
|
-
lockfile_type = Python::FileParser::DEPENDENCY_GROUP_KEYS
|
|
179
|
-
.find { |i| i.fetch(:pipfile) == type }
|
|
180
|
-
.fetch(:lockfile)
|
|
181
|
-
|
|
182
|
-
JSON.parse(lockfile.content)
|
|
183
|
-
.fetch(lockfile_type, {})
|
|
184
|
-
.keys.any? { |k| normalise(k) == dependency.name }
|
|
185
|
-
end
|
|
186
|
-
|
|
187
142
|
def add_private_sources(pipfile_content)
|
|
188
143
|
PipfilePreparer
|
|
189
144
|
.new(pipfile_content: pipfile_content)
|
|
@@ -192,14 +147,12 @@ module Dependabot
|
|
|
192
147
|
|
|
193
148
|
def updated_generated_files
|
|
194
149
|
@updated_generated_files ||=
|
|
195
|
-
SharedHelpers.
|
|
150
|
+
SharedHelpers.in_a_temporary_repo_directory(dependency_files.first.directory, repo_contents_path) do
|
|
196
151
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
197
152
|
write_temporary_dependency_files(prepared_pipfile_content)
|
|
198
153
|
install_required_python
|
|
199
154
|
|
|
200
|
-
|
|
201
|
-
"pyenv exec pipenv lock"
|
|
202
|
-
)
|
|
155
|
+
pipenv_runner.run_upgrade("==#{dependency.version}")
|
|
203
156
|
|
|
204
157
|
result = { lockfile: File.read("Pipfile.lock") }
|
|
205
158
|
result[:lockfile] = post_process_lockfile(result[:lockfile])
|
|
@@ -245,17 +198,12 @@ module Dependabot
|
|
|
245
198
|
File.write("dev-req.txt", dev_req_content)
|
|
246
199
|
end
|
|
247
200
|
|
|
248
|
-
def run_command(command
|
|
249
|
-
SharedHelpers.run_shell_command(command
|
|
201
|
+
def run_command(command)
|
|
202
|
+
SharedHelpers.run_shell_command(command)
|
|
250
203
|
end
|
|
251
204
|
|
|
252
|
-
def run_pipenv_command(command
|
|
253
|
-
|
|
254
|
-
"pyenv local #{language_version_manager.python_major_minor}",
|
|
255
|
-
fingerprint: "pyenv local <python_major_minor>"
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
run_command(command, env: env)
|
|
205
|
+
def run_pipenv_command(command)
|
|
206
|
+
pipenv_runner.run(command)
|
|
259
207
|
end
|
|
260
208
|
|
|
261
209
|
def write_temporary_dependency_files(pipfile_content)
|
|
@@ -317,7 +265,7 @@ module Dependabot
|
|
|
317
265
|
SharedHelpers.run_helper_subprocess(
|
|
318
266
|
command: "pyenv exec python3 #{NativeHelpers.python_helper_path}",
|
|
319
267
|
function: "get_pipfile_hash",
|
|
320
|
-
args: [dir]
|
|
268
|
+
args: [T.cast(dir, Pathname).to_s]
|
|
321
269
|
)
|
|
322
270
|
end
|
|
323
271
|
end
|
|
@@ -328,10 +276,6 @@ module Dependabot
|
|
|
328
276
|
updated_file
|
|
329
277
|
end
|
|
330
278
|
|
|
331
|
-
def normalise(name)
|
|
332
|
-
NameNormaliser.normalise(name)
|
|
333
|
-
end
|
|
334
|
-
|
|
335
279
|
def python_requirement_parser
|
|
336
280
|
@python_requirement_parser ||=
|
|
337
281
|
FileParser::PythonRequirementParser.new(
|
|
@@ -346,6 +290,15 @@ module Dependabot
|
|
|
346
290
|
)
|
|
347
291
|
end
|
|
348
292
|
|
|
293
|
+
def pipenv_runner
|
|
294
|
+
@pipenv_runner ||=
|
|
295
|
+
PipenvRunner.new(
|
|
296
|
+
dependency: dependency,
|
|
297
|
+
lockfile: lockfile,
|
|
298
|
+
language_version_manager: language_version_manager
|
|
299
|
+
)
|
|
300
|
+
end
|
|
301
|
+
|
|
349
302
|
def parsed_lockfile
|
|
350
303
|
@parsed_lockfile ||= JSON.parse(lockfile.content)
|
|
351
304
|
end
|
|
@@ -369,16 +322,6 @@ module Dependabot
|
|
|
369
322
|
def requirements_files
|
|
370
323
|
dependency_files.select { |f| f.name.end_with?(".txt") }
|
|
371
324
|
end
|
|
372
|
-
|
|
373
|
-
def pipenv_env_variables
|
|
374
|
-
{
|
|
375
|
-
"PIPENV_YES" => "true", # Install new Python ver if needed
|
|
376
|
-
"PIPENV_MAX_RETRIES" => "3", # Retry timeouts
|
|
377
|
-
"PIPENV_NOSPIN" => "1", # Don't pollute logs with spinner
|
|
378
|
-
"PIPENV_TIMEOUT" => "600", # Set install timeout to 10 minutes
|
|
379
|
-
"PIP_DEFAULT_TIMEOUT" => "60" # Set pip timeout to 1 minute
|
|
380
|
-
}
|
|
381
|
-
end
|
|
382
325
|
end
|
|
383
326
|
end
|
|
384
327
|
end
|
|
@@ -7,15 +7,13 @@ require "dependabot/dependency"
|
|
|
7
7
|
require "dependabot/python/file_parser"
|
|
8
8
|
require "dependabot/python/file_updater"
|
|
9
9
|
require "dependabot/python/authed_url_builder"
|
|
10
|
-
require "dependabot/python/name_normaliser"
|
|
11
10
|
|
|
12
11
|
module Dependabot
|
|
13
12
|
module Python
|
|
14
13
|
class FileUpdater
|
|
15
14
|
class PipfilePreparer
|
|
16
|
-
def initialize(pipfile_content
|
|
15
|
+
def initialize(pipfile_content:)
|
|
17
16
|
@pipfile_content = pipfile_content
|
|
18
|
-
@lockfile = lockfile
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
def replace_sources(credentials)
|
|
@@ -28,45 +26,6 @@ module Dependabot
|
|
|
28
26
|
TomlRB.dump(pipfile_object)
|
|
29
27
|
end
|
|
30
28
|
|
|
31
|
-
def freeze_top_level_dependencies_except(dependencies)
|
|
32
|
-
return pipfile_content unless lockfile
|
|
33
|
-
|
|
34
|
-
pipfile_object = TomlRB.parse(pipfile_content)
|
|
35
|
-
excluded_names = dependencies.map(&:name)
|
|
36
|
-
|
|
37
|
-
Python::FileParser::DEPENDENCY_GROUP_KEYS.each do |keys|
|
|
38
|
-
next unless pipfile_object[keys[:pipfile]]
|
|
39
|
-
|
|
40
|
-
pipfile_object.fetch(keys[:pipfile]).each do |dep_name, _|
|
|
41
|
-
next if excluded_names.include?(normalise(dep_name))
|
|
42
|
-
|
|
43
|
-
freeze_dependency(dep_name, pipfile_object, keys)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
TomlRB.dump(pipfile_object)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def freeze_dependency(dep_name, pipfile_object, keys)
|
|
51
|
-
locked_version = version_from_lockfile(
|
|
52
|
-
keys[:lockfile],
|
|
53
|
-
normalise(dep_name)
|
|
54
|
-
)
|
|
55
|
-
locked_ref = ref_from_lockfile(
|
|
56
|
-
keys[:lockfile],
|
|
57
|
-
normalise(dep_name)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
pipfile_req = pipfile_object[keys[:pipfile]][dep_name]
|
|
61
|
-
if pipfile_req.is_a?(Hash) && locked_version
|
|
62
|
-
pipfile_req["version"] = "==#{locked_version}"
|
|
63
|
-
elsif pipfile_req.is_a?(Hash) && locked_ref && !pipfile_req["ref"]
|
|
64
|
-
pipfile_req["ref"] = locked_ref
|
|
65
|
-
elsif locked_version
|
|
66
|
-
pipfile_object[keys[:pipfile]][dep_name] = "==#{locked_version}"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
29
|
def update_python_requirement(requirement)
|
|
71
30
|
pipfile_object = TomlRB.parse(pipfile_content)
|
|
72
31
|
|
|
@@ -84,31 +43,6 @@ module Dependabot
|
|
|
84
43
|
|
|
85
44
|
attr_reader :pipfile_content, :lockfile
|
|
86
45
|
|
|
87
|
-
def version_from_lockfile(dep_type, dep_name)
|
|
88
|
-
details = parsed_lockfile.dig(dep_type, normalise(dep_name))
|
|
89
|
-
|
|
90
|
-
case details
|
|
91
|
-
when String then details.gsub(/^==/, "")
|
|
92
|
-
when Hash then details["version"]&.gsub(/^==/, "")
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def ref_from_lockfile(dep_type, dep_name)
|
|
97
|
-
details = parsed_lockfile.dig(dep_type, normalise(dep_name))
|
|
98
|
-
|
|
99
|
-
case details
|
|
100
|
-
when Hash then details["ref"]
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def parsed_lockfile
|
|
105
|
-
@parsed_lockfile ||= JSON.parse(lockfile.content)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def normalise(name)
|
|
109
|
-
NameNormaliser.normalise(name)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
46
|
def pipfile_sources
|
|
113
47
|
@pipfile_sources ||= TomlRB.parse(pipfile_content).fetch("source", [])
|
|
114
48
|
end
|
|
@@ -238,7 +238,7 @@ module Dependabot
|
|
|
238
238
|
SharedHelpers.run_helper_subprocess(
|
|
239
239
|
command: "pyenv exec python3 #{python_helper_path}",
|
|
240
240
|
function: "get_pyproject_hash",
|
|
241
|
-
args: [dir]
|
|
241
|
+
args: [T.cast(dir, Pathname).to_s]
|
|
242
242
|
)
|
|
243
243
|
end
|
|
244
244
|
end
|
|
@@ -247,7 +247,8 @@ module Dependabot
|
|
|
247
247
|
def declaration_regex(dep, old_req)
|
|
248
248
|
group = old_req[:groups].first
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
header_regex = "#{group}(?:\\.dependencies)?\\]\s*(?:\s*#.*?)*?"
|
|
251
|
+
/#{header_regex}\n.*?(?<declaration>(?:^\s*|["'])#{escape(dep)}["']?\s*=[^\n]*)$/mi
|
|
251
252
|
end
|
|
252
253
|
|
|
253
254
|
def table_declaration_regex(dep, old_req)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/shared_helpers"
|
|
5
|
+
require "dependabot/python/file_parser"
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Python
|
|
10
|
+
class PipenvRunner
|
|
11
|
+
def initialize(dependency:, lockfile:, language_version_manager:)
|
|
12
|
+
@dependency = dependency
|
|
13
|
+
@lockfile = lockfile
|
|
14
|
+
@language_version_manager = language_version_manager
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run_upgrade(constraint)
|
|
18
|
+
constraint = "" if constraint == "*"
|
|
19
|
+
command = "pyenv exec pipenv upgrade --verbose #{dependency_name}#{constraint}"
|
|
20
|
+
command << " --dev" if lockfile_section == "develop"
|
|
21
|
+
|
|
22
|
+
run(command, fingerprint: "pyenv exec pipenv upgrade --verbose <dependency_name><constraint>")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run_upgrade_and_fetch_version(constraint)
|
|
26
|
+
run_upgrade(constraint)
|
|
27
|
+
|
|
28
|
+
updated_lockfile = JSON.parse(File.read("Pipfile.lock"))
|
|
29
|
+
|
|
30
|
+
fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def run(command, fingerprint: nil)
|
|
34
|
+
run_command(
|
|
35
|
+
"pyenv local #{language_version_manager.python_major_minor}",
|
|
36
|
+
fingerprint: "pyenv local <python_major_minor>"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
run_command(command, fingerprint: fingerprint)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
attr_reader :dependency, :lockfile, :language_version_manager
|
|
45
|
+
|
|
46
|
+
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
47
|
+
deps = updated_lockfile[lockfile_section] || {}
|
|
48
|
+
|
|
49
|
+
deps.dig(dependency_name, "version")
|
|
50
|
+
&.gsub(/^==/, "")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def run_command(command, fingerprint: nil)
|
|
54
|
+
SharedHelpers.run_shell_command(command, env: pipenv_env_variables, fingerprint: fingerprint)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def lockfile_section
|
|
58
|
+
if dependency.requirements.any?
|
|
59
|
+
dependency.requirements.first[:groups].first
|
|
60
|
+
else
|
|
61
|
+
Python::FileParser::DEPENDENCY_GROUP_KEYS.each do |keys|
|
|
62
|
+
section = keys.fetch(:lockfile)
|
|
63
|
+
return section if JSON.parse(lockfile.content)[section].keys.any?(dependency_name)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def dependency_name
|
|
69
|
+
dependency.metadata[:original_name] || dependency.name
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def pipenv_env_variables
|
|
73
|
+
{
|
|
74
|
+
"PIPENV_YES" => "true", # Install new Python ver if needed
|
|
75
|
+
"PIPENV_MAX_RETRIES" => "3", # Retry timeouts
|
|
76
|
+
"PIPENV_NOSPIN" => "1", # Don't pollute logs with spinner
|
|
77
|
+
"PIPENV_TIMEOUT" => "600", # Set install timeout to 10 minutes
|
|
78
|
+
"PIP_DEFAULT_TIMEOUT" => "60", # Set pip timeout to 1 minute
|
|
79
|
+
"COLUMNS" => "250" # Avoid line wrapping
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -12,9 +12,10 @@ module Dependabot
|
|
|
12
12
|
PYPI_BASE_URL = "https://pypi.org/simple/"
|
|
13
13
|
ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/
|
|
14
14
|
|
|
15
|
-
def initialize(dependency_files:, credentials:)
|
|
15
|
+
def initialize(dependency_files:, credentials:, dependency:)
|
|
16
16
|
@dependency_files = dependency_files
|
|
17
17
|
@credentials = credentials
|
|
18
|
+
@dependency = dependency
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def index_urls
|
|
@@ -122,9 +123,13 @@ module Dependabot
|
|
|
122
123
|
# If source is PyPI, skip it, and let it pick the default URI
|
|
123
124
|
next if source["name"].casecmp?("PyPI")
|
|
124
125
|
|
|
125
|
-
if source["
|
|
126
|
+
if @dependency.all_sources.include?(source["name"])
|
|
127
|
+
# If dependency has specified this source, use it
|
|
128
|
+
return { main: source["url"], extra: [] }
|
|
129
|
+
elsif source["default"]
|
|
126
130
|
urls[:main] = source["url"]
|
|
127
|
-
|
|
131
|
+
elsif source["priority"] != "explicit"
|
|
132
|
+
# if source is not explicit, add it to extra
|
|
128
133
|
urls[:extra] << source["url"]
|
|
129
134
|
end
|
|
130
135
|
end
|
|
@@ -33,12 +33,13 @@ module Dependabot
|
|
|
33
33
|
RESOLUTION_IMPOSSIBLE_ERROR = "ResolutionImpossible"
|
|
34
34
|
ERROR_REGEX = /(?<=ERROR\:\W).*$/
|
|
35
35
|
|
|
36
|
-
attr_reader :dependency, :dependency_files, :credentials
|
|
36
|
+
attr_reader :dependency, :dependency_files, :credentials, :repo_contents_path
|
|
37
37
|
|
|
38
|
-
def initialize(dependency:, dependency_files:, credentials:)
|
|
38
|
+
def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
|
|
39
39
|
@dependency = dependency
|
|
40
40
|
@dependency_files = dependency_files
|
|
41
41
|
@credentials = credentials
|
|
42
|
+
@repo_contents_path = repo_contents_path
|
|
42
43
|
@build_isolation = true
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "excon"
|
|
5
|
-
require "toml-rb"
|
|
6
5
|
require "open3"
|
|
7
6
|
require "dependabot/dependency"
|
|
8
7
|
require "dependabot/errors"
|
|
@@ -13,23 +12,14 @@ require "dependabot/python/file_updater/pipfile_preparer"
|
|
|
13
12
|
require "dependabot/python/file_updater/setup_file_sanitizer"
|
|
14
13
|
require "dependabot/python/update_checker"
|
|
15
14
|
require "dependabot/python/native_helpers"
|
|
16
|
-
require "dependabot/python/
|
|
15
|
+
require "dependabot/python/pipenv_runner"
|
|
17
16
|
require "dependabot/python/version"
|
|
18
17
|
|
|
19
18
|
module Dependabot
|
|
20
19
|
module Python
|
|
21
20
|
class UpdateChecker
|
|
22
|
-
# This class does version resolution for Pipfiles. Its current approach
|
|
23
|
-
# is somewhat crude:
|
|
24
|
-
# - Unlock the dependency we're checking in the Pipfile
|
|
25
|
-
# - Freeze all of the other dependencies in the Pipfile
|
|
26
|
-
# - Run `pipenv lock` and see what the result is
|
|
27
|
-
#
|
|
28
|
-
# Unfortunately, Pipenv doesn't resolve how we'd expect - it appears to
|
|
29
|
-
# just raise if the latest version can't be resolved. Knowing that is
|
|
30
|
-
# still better than nothing, though.
|
|
31
21
|
class PipenvVersionResolver
|
|
32
|
-
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none (?<url>[^\s]+).*/
|
|
22
|
+
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).*/
|
|
33
23
|
GIT_REFERENCE_NOT_FOUND_REGEX = /git checkout -q (?<tag>[^\s]+).*/
|
|
34
24
|
PIPENV_INSTALLATION_ERROR = "python setup.py egg_info exited with 1"
|
|
35
25
|
PIPENV_INSTALLATION_ERROR_REGEX =
|
|
@@ -37,14 +27,13 @@ module Dependabot
|
|
|
37
27
|
|
|
38
28
|
PIPENV_RANGE_WARNING = /Warning:\sPython\s[<>].* was not found/
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
attr_reader :dependency, :dependency_files, :credentials, :repo_contents_path
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def initialize(dependency:, dependency_files:, credentials:)
|
|
32
|
+
def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
|
|
45
33
|
@dependency = dependency
|
|
46
34
|
@dependency_files = dependency_files
|
|
47
35
|
@credentials = credentials
|
|
36
|
+
@repo_contents_path = repo_contents_path
|
|
48
37
|
end
|
|
49
38
|
|
|
50
39
|
def latest_resolvable_version(requirement: nil)
|
|
@@ -68,53 +57,18 @@ module Dependabot
|
|
|
68
57
|
return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
|
|
69
58
|
|
|
70
59
|
@latest_resolvable_version_string[requirement] ||=
|
|
71
|
-
SharedHelpers.
|
|
60
|
+
SharedHelpers.in_a_temporary_repo_directory(base_directory, repo_contents_path) do
|
|
72
61
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
73
|
-
write_temporary_dependency_files
|
|
62
|
+
write_temporary_dependency_files
|
|
74
63
|
install_required_python
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
# Whilst calling `lock` avoids doing an install as part of the
|
|
78
|
-
# pipenv flow, an install is still done by pip-tools in order
|
|
79
|
-
# to resolve the dependencies. That means this is slow.
|
|
80
|
-
run_pipenv_command("pyenv exec pipenv lock")
|
|
81
|
-
|
|
82
|
-
updated_lockfile = JSON.parse(File.read("Pipfile.lock"))
|
|
83
|
-
|
|
84
|
-
fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
65
|
+
pipenv_runner.run_upgrade_and_fetch_version(requirement)
|
|
85
66
|
end
|
|
86
67
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
87
68
|
handle_pipenv_errors(e)
|
|
88
69
|
end
|
|
89
70
|
end
|
|
90
71
|
|
|
91
|
-
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
92
|
-
if dependency.requirements.any?
|
|
93
|
-
group = dependency.requirements.first[:groups].first
|
|
94
|
-
deps = updated_lockfile[group] || {}
|
|
95
|
-
|
|
96
|
-
version =
|
|
97
|
-
deps.transform_keys { |k| normalise(k) }
|
|
98
|
-
.dig(dependency.name, "version")
|
|
99
|
-
&.gsub(/^==/, "")
|
|
100
|
-
|
|
101
|
-
return version
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
Python::FileParser::DEPENDENCY_GROUP_KEYS.each do |keys|
|
|
105
|
-
deps = updated_lockfile[keys.fetch(:lockfile)] || {}
|
|
106
|
-
version =
|
|
107
|
-
deps.transform_keys { |k| normalise(k) }
|
|
108
|
-
.dig(dependency.name, "version")
|
|
109
|
-
&.gsub(/^==/, "")
|
|
110
|
-
|
|
111
|
-
return version if version
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# If the sub-dependency no longer appears in the lockfile return nil
|
|
115
|
-
nil
|
|
116
|
-
end
|
|
117
|
-
|
|
118
72
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
119
73
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
120
74
|
# rubocop:disable Metrics/AbcSize
|
|
@@ -136,6 +90,19 @@ module Dependabot
|
|
|
136
90
|
raise DependencyFileNotResolvable, msg
|
|
137
91
|
end
|
|
138
92
|
|
|
93
|
+
if error.message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
|
|
94
|
+
tag = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
|
|
95
|
+
# Unfortunately the error message doesn't include the package name.
|
|
96
|
+
# TODO: Talk with pipenv maintainers about exposing the package name, it used to be part of the error output
|
|
97
|
+
raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
|
101
|
+
url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
|
102
|
+
.named_captures.fetch("url")
|
|
103
|
+
raise GitDependenciesNotReachable, url
|
|
104
|
+
end
|
|
105
|
+
|
|
139
106
|
if error.message.include?("Could not find a version") || error.message.include?("ResolutionFailure")
|
|
140
107
|
check_original_requirements_resolvable
|
|
141
108
|
end
|
|
@@ -165,20 +132,7 @@ module Dependabot
|
|
|
165
132
|
return if error.message.match?(/#{Regexp.quote(dependency.name)}/i)
|
|
166
133
|
end
|
|
167
134
|
|
|
168
|
-
|
|
169
|
-
tag = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX).named_captures.fetch("tag")
|
|
170
|
-
# Unfortunately the error message doesn't include the package name.
|
|
171
|
-
# TODO: Talk with pipenv maintainers about exposing the package name, it used to be part of the error output
|
|
172
|
-
raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
|
176
|
-
url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
|
177
|
-
.named_captures.fetch("url")
|
|
178
|
-
raise GitDependenciesNotReachable, url
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
raise unless error.message.include?("could not be resolved")
|
|
135
|
+
raise unless error.message.include?("ResolutionFailure")
|
|
182
136
|
end
|
|
183
137
|
# rubocop:enable Metrics/CyclomaticComplexity
|
|
184
138
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
@@ -190,10 +144,10 @@ module Dependabot
|
|
|
190
144
|
# boolean, so that all deps for this repo will raise identical
|
|
191
145
|
# errors when failing to update
|
|
192
146
|
def check_original_requirements_resolvable
|
|
193
|
-
SharedHelpers.
|
|
147
|
+
SharedHelpers.in_a_temporary_repo_directory(base_directory, repo_contents_path) do
|
|
194
148
|
write_temporary_dependency_files(update_pipfile: false)
|
|
195
149
|
|
|
196
|
-
|
|
150
|
+
pipenv_runner.run_upgrade("==#{dependency.version}")
|
|
197
151
|
|
|
198
152
|
true
|
|
199
153
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
@@ -201,6 +155,10 @@ module Dependabot
|
|
|
201
155
|
end
|
|
202
156
|
end
|
|
203
157
|
|
|
158
|
+
def base_directory
|
|
159
|
+
dependency_files.first.directory
|
|
160
|
+
end
|
|
161
|
+
|
|
204
162
|
def handle_pipenv_errors_resolving_original_reqs(error)
|
|
205
163
|
if error.message.include?("Could not find a version") ||
|
|
206
164
|
error.message.include?("package versions have conflicting dependencies")
|
|
@@ -220,10 +178,6 @@ module Dependabot
|
|
|
220
178
|
raise DependencyFileNotResolvable, msg
|
|
221
179
|
end
|
|
222
180
|
|
|
223
|
-
# NOTE: Pipenv masks the actual error, see this issue for updates:
|
|
224
|
-
# https://github.com/pypa/pipenv/issues/2791
|
|
225
|
-
# TODO: This may no longer be reproducible on latest pipenv, see linked issue,
|
|
226
|
-
# so investigate when we next bump to newer pipenv...
|
|
227
181
|
handle_pipenv_installation_error(error.message) if error.message.match?(PIPENV_INSTALLATION_ERROR_REGEX)
|
|
228
182
|
|
|
229
183
|
# Raise an unhandled error, as this could be a problem with
|
|
@@ -264,8 +218,7 @@ module Dependabot
|
|
|
264
218
|
raise DependencyFileNotResolvable, msg
|
|
265
219
|
end
|
|
266
220
|
|
|
267
|
-
def write_temporary_dependency_files(
|
|
268
|
-
update_pipfile: true)
|
|
221
|
+
def write_temporary_dependency_files(update_pipfile: true)
|
|
269
222
|
dependency_files.each do |file|
|
|
270
223
|
path = file.name
|
|
271
224
|
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
|
@@ -291,7 +244,7 @@ module Dependabot
|
|
|
291
244
|
# Overwrite the pipfile with updated content
|
|
292
245
|
File.write(
|
|
293
246
|
"Pipfile",
|
|
294
|
-
pipfile_content
|
|
247
|
+
pipfile_content
|
|
295
248
|
)
|
|
296
249
|
end
|
|
297
250
|
|
|
@@ -319,93 +272,27 @@ module Dependabot
|
|
|
319
272
|
dependency_files.find { |f| f.name == config_name }
|
|
320
273
|
end
|
|
321
274
|
|
|
322
|
-
def pipfile_content
|
|
275
|
+
def pipfile_content
|
|
323
276
|
content = pipfile.content
|
|
324
|
-
content = freeze_other_dependencies(content)
|
|
325
|
-
content = set_target_dependency_req(content, updated_requirement)
|
|
326
277
|
content = add_private_sources(content)
|
|
327
278
|
content = update_python_requirement(content)
|
|
328
279
|
content
|
|
329
280
|
end
|
|
330
281
|
|
|
331
|
-
def freeze_other_dependencies(pipfile_content)
|
|
332
|
-
Python::FileUpdater::PipfilePreparer
|
|
333
|
-
.new(pipfile_content: pipfile_content, lockfile: lockfile)
|
|
334
|
-
.freeze_top_level_dependencies_except([dependency])
|
|
335
|
-
end
|
|
336
|
-
|
|
337
282
|
def update_python_requirement(pipfile_content)
|
|
338
283
|
Python::FileUpdater::PipfilePreparer
|
|
339
284
|
.new(pipfile_content: pipfile_content)
|
|
340
285
|
.update_python_requirement(language_version_manager.python_major_minor)
|
|
341
286
|
end
|
|
342
287
|
|
|
343
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
344
|
-
def set_target_dependency_req(pipfile_content, updated_requirement)
|
|
345
|
-
return pipfile_content unless updated_requirement
|
|
346
|
-
|
|
347
|
-
pipfile_object = TomlRB.parse(pipfile_content)
|
|
348
|
-
|
|
349
|
-
DEPENDENCY_TYPES.each do |type|
|
|
350
|
-
names = pipfile_object[type]&.keys || []
|
|
351
|
-
pkg_name = names.find { |nm| normalise(nm) == dependency.name }
|
|
352
|
-
next unless pkg_name || subdep_type?(type)
|
|
353
|
-
|
|
354
|
-
pkg_name ||= dependency.name
|
|
355
|
-
if pipfile_object.dig(type, pkg_name).is_a?(Hash)
|
|
356
|
-
pipfile_object[type][pkg_name]["version"] = updated_requirement
|
|
357
|
-
else
|
|
358
|
-
pipfile_object[type][pkg_name] = updated_requirement
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
TomlRB.dump(pipfile_object)
|
|
363
|
-
end
|
|
364
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
365
|
-
|
|
366
|
-
def subdep_type?(type)
|
|
367
|
-
return false if dependency.top_level?
|
|
368
|
-
|
|
369
|
-
lockfile_type = Python::FileParser::DEPENDENCY_GROUP_KEYS
|
|
370
|
-
.find { |i| i.fetch(:pipfile) == type }
|
|
371
|
-
.fetch(:lockfile)
|
|
372
|
-
|
|
373
|
-
JSON.parse(lockfile.content)
|
|
374
|
-
.fetch(lockfile_type, {})
|
|
375
|
-
.keys.any? { |k| normalise(k) == dependency.name }
|
|
376
|
-
end
|
|
377
|
-
|
|
378
288
|
def add_private_sources(pipfile_content)
|
|
379
289
|
Python::FileUpdater::PipfilePreparer
|
|
380
290
|
.new(pipfile_content: pipfile_content)
|
|
381
291
|
.replace_sources(credentials)
|
|
382
292
|
end
|
|
383
293
|
|
|
384
|
-
def run_command(command
|
|
385
|
-
SharedHelpers.run_shell_command(command,
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
def run_pipenv_command(command, env: pipenv_env_variables)
|
|
389
|
-
run_command(
|
|
390
|
-
"pyenv local #{language_version_manager.python_major_minor}",
|
|
391
|
-
fingerprint: "pyenv local <python_major_minor>"
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
run_command(command, env: env)
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def pipenv_env_variables
|
|
398
|
-
{
|
|
399
|
-
"PIPENV_YES" => "true", # Install new Python ver if needed
|
|
400
|
-
"PIPENV_MAX_RETRIES" => "3", # Retry timeouts
|
|
401
|
-
"PIPENV_NOSPIN" => "1", # Don't pollute logs with spinner
|
|
402
|
-
"PIPENV_TIMEOUT" => "600", # Set install timeout to 10 minutes
|
|
403
|
-
"PIP_DEFAULT_TIMEOUT" => "60" # Set pip timeout to 1 minute
|
|
404
|
-
}
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
def normalise(name)
|
|
408
|
-
NameNormaliser.normalise(name)
|
|
294
|
+
def run_command(command)
|
|
295
|
+
SharedHelpers.run_shell_command(command, stderr_to_stdout: true)
|
|
409
296
|
end
|
|
410
297
|
|
|
411
298
|
def python_requirement_parser
|
|
@@ -422,6 +309,15 @@ module Dependabot
|
|
|
422
309
|
)
|
|
423
310
|
end
|
|
424
311
|
|
|
312
|
+
def pipenv_runner
|
|
313
|
+
@pipenv_runner ||=
|
|
314
|
+
PipenvRunner.new(
|
|
315
|
+
dependency: dependency,
|
|
316
|
+
lockfile: lockfile,
|
|
317
|
+
language_version_manager: language_version_manager
|
|
318
|
+
)
|
|
319
|
+
end
|
|
320
|
+
|
|
425
321
|
def pipfile
|
|
426
322
|
dependency_files.find { |f| f.name == "Pipfile" }
|
|
427
323
|
end
|
|
@@ -38,12 +38,13 @@ module Dependabot
|
|
|
38
38
|
\s+check\syour\sgit\sconfiguration
|
|
39
39
|
/mx
|
|
40
40
|
|
|
41
|
-
attr_reader :dependency, :dependency_files, :credentials
|
|
41
|
+
attr_reader :dependency, :dependency_files, :credentials, :repo_contents_path
|
|
42
42
|
|
|
43
|
-
def initialize(dependency:, dependency_files:, credentials:)
|
|
43
|
+
def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
|
|
44
44
|
@dependency = dependency
|
|
45
45
|
@dependency_files = dependency_files
|
|
46
46
|
@credentials = credentials
|
|
47
|
+
@repo_contents_path = repo_contents_path
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def latest_resolvable_version(requirement: nil)
|
|
@@ -191,7 +191,8 @@ module Dependabot
|
|
|
191
191
|
{
|
|
192
192
|
dependency: dependency,
|
|
193
193
|
dependency_files: dependency_files,
|
|
194
|
-
credentials: credentials
|
|
194
|
+
credentials: credentials,
|
|
195
|
+
repo_contents_path: repo_contents_path
|
|
195
196
|
}
|
|
196
197
|
end
|
|
197
198
|
|
|
@@ -221,11 +222,11 @@ module Dependabot
|
|
|
221
222
|
return lower_bound_req if latest_version.nil?
|
|
222
223
|
return lower_bound_req unless Python::Version.correct?(latest_version)
|
|
223
224
|
|
|
224
|
-
lower_bound_req + "
|
|
225
|
+
lower_bound_req + ",<=#{latest_version}"
|
|
225
226
|
end
|
|
226
227
|
|
|
227
228
|
def updated_version_req_lower_bound
|
|
228
|
-
return "
|
|
229
|
+
return ">=#{dependency.version}" if dependency.version
|
|
229
230
|
|
|
230
231
|
version_for_requirement =
|
|
231
232
|
requirements.filter_map { |r| r[:requirement] }
|
|
@@ -235,7 +236,7 @@ module Dependabot
|
|
|
235
236
|
.select { |version| Gem::Version.correct?(version) }
|
|
236
237
|
.max_by { |version| Gem::Version.new(version) }
|
|
237
238
|
|
|
238
|
-
"
|
|
239
|
+
">=#{version_for_requirement || 0}"
|
|
239
240
|
end
|
|
240
241
|
|
|
241
242
|
def fetch_latest_version
|
data/lib/dependabot/python.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-python
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.238.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-12-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dependabot-common
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.238.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - '='
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.238.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: debug
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,20 +94,34 @@ dependencies:
|
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '1.3'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rspec-sorbet
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 1.9.2
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 1.9.2
|
|
97
111
|
- !ruby/object:Gem::Dependency
|
|
98
112
|
name: rubocop
|
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
|
100
114
|
requirements:
|
|
101
115
|
- - "~>"
|
|
102
116
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: 1.
|
|
117
|
+
version: 1.57.2
|
|
104
118
|
type: :development
|
|
105
119
|
prerelease: false
|
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
121
|
requirements:
|
|
108
122
|
- - "~>"
|
|
109
123
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: 1.
|
|
124
|
+
version: 1.57.2
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: rubocop-performance
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -229,6 +243,7 @@ files:
|
|
|
229
243
|
- lib/dependabot/python/name_normaliser.rb
|
|
230
244
|
- lib/dependabot/python/native_helpers.rb
|
|
231
245
|
- lib/dependabot/python/pip_compile_file_matcher.rb
|
|
246
|
+
- lib/dependabot/python/pipenv_runner.rb
|
|
232
247
|
- lib/dependabot/python/requirement.rb
|
|
233
248
|
- lib/dependabot/python/requirement_parser.rb
|
|
234
249
|
- lib/dependabot/python/update_checker.rb
|
|
@@ -245,7 +260,7 @@ licenses:
|
|
|
245
260
|
- Nonstandard
|
|
246
261
|
metadata:
|
|
247
262
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
248
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
263
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.238.0
|
|
249
264
|
post_install_message:
|
|
250
265
|
rdoc_options: []
|
|
251
266
|
require_paths:
|