dependabot-python 0.320.0 → 0.320.1
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/lib/dependabot/python/authed_url_builder.rb +8 -3
- data/lib/dependabot/python/file_updater/pip_compile_file_updater.rb +5 -5
- data/lib/dependabot/python/file_updater/requirement_file_updater.rb +37 -10
- data/lib/dependabot/python/file_updater/requirement_replacer.rb +59 -25
- data/lib/dependabot/python/file_updater/setup_file_sanitizer.rb +39 -13
- data/lib/dependabot/python/file_updater.rb +1 -1
- data/lib/dependabot/python/package/package_registry_finder.rb +97 -55
- data/lib/dependabot/python/pipenv_runner.rb +2 -2
- data/lib/dependabot/python/requirement.rb +29 -20
- data/lib/dependabot/python/update_checker/pip_compile_version_resolver.rb +121 -50
- data/lib/dependabot/python/update_checker/pip_version_resolver.rb +41 -10
- data/lib/dependabot/python/update_checker/pipenv_version_resolver.rb +80 -23
- data/lib/dependabot/python/update_checker/poetry_version_resolver.rb +80 -25
- data/lib/dependabot/python/update_checker/requirements_updater.rb +80 -34
- data/lib/dependabot/python/version.rb +21 -14
- metadata +4 -4
@@ -1,6 +1,7 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
4
5
|
require "excon"
|
5
6
|
require "open3"
|
6
7
|
require "dependabot/dependency"
|
@@ -19,12 +20,14 @@ module Dependabot
|
|
19
20
|
module Python
|
20
21
|
class UpdateChecker
|
21
22
|
class PipenvVersionResolver
|
23
|
+
extend T::Sig
|
24
|
+
|
22
25
|
GIT_DEPENDENCY_UNREACHABLE_REGEX = /git clone --filter=blob:none --quiet (?<url>[^\s]+).*/
|
23
26
|
GIT_REFERENCE_NOT_FOUND_REGEX = /git checkout -q (?<tag>[^\s]+).*/
|
24
27
|
PIPENV_INSTALLATION_ERROR_NEW = "Getting requirements to build wheel exited with 1"
|
25
28
|
|
26
29
|
# Can be removed when Python 3.11 support is dropped
|
27
|
-
PIPENV_INSTALLATION_ERROR_OLD = Regexp.quote("python setup.py egg_info exited with 1")
|
30
|
+
PIPENV_INSTALLATION_ERROR_OLD = T.let(Regexp.quote("python setup.py egg_info exited with 1"), String)
|
28
31
|
|
29
32
|
PIPENV_INSTALLATION_ERROR = /#{PIPENV_INSTALLATION_ERROR_NEW}|#{PIPENV_INSTALLATION_ERROR_OLD}/
|
30
33
|
PIPENV_INSTALLATION_ERROR_REGEX =
|
@@ -32,18 +35,34 @@ module Dependabot
|
|
32
35
|
|
33
36
|
PIPENV_RANGE_WARNING = /Python version range specifier '(?<ver>.*)' is not supported/
|
34
37
|
|
38
|
+
sig { returns(Dependabot::Dependency) }
|
35
39
|
attr_reader :dependency
|
40
|
+
|
41
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
36
42
|
attr_reader :dependency_files
|
43
|
+
|
44
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
37
45
|
attr_reader :credentials
|
46
|
+
|
47
|
+
sig { returns(T.nilable(String)) }
|
38
48
|
attr_reader :repo_contents_path
|
39
49
|
|
50
|
+
sig do
|
51
|
+
params(
|
52
|
+
dependency: Dependabot::Dependency,
|
53
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
54
|
+
credentials: T::Array[Dependabot::Credential],
|
55
|
+
repo_contents_path: T.nilable(String)
|
56
|
+
).void
|
57
|
+
end
|
40
58
|
def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
|
41
|
-
@dependency
|
42
|
-
@dependency_files
|
43
|
-
@credentials
|
44
|
-
@repo_contents_path
|
59
|
+
@dependency = T.let(dependency, Dependabot::Dependency)
|
60
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
61
|
+
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
62
|
+
@repo_contents_path = T.let(repo_contents_path, T.nilable(String))
|
45
63
|
end
|
46
64
|
|
65
|
+
sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Python::Version)) }
|
47
66
|
def latest_resolvable_version(requirement: nil)
|
48
67
|
version_string =
|
49
68
|
fetch_latest_resolvable_version_string(requirement: requirement)
|
@@ -51,17 +70,19 @@ module Dependabot
|
|
51
70
|
version_string.nil? ? nil : Python::Version.new(version_string)
|
52
71
|
end
|
53
72
|
|
73
|
+
sig { params(version: Gem::Version).returns(T::Boolean) }
|
54
74
|
def resolvable?(version:)
|
55
|
-
@resolvable ||= {}
|
56
|
-
return @resolvable[version] if @resolvable.key?(version)
|
75
|
+
@resolvable ||= T.let({}, T.nilable(T::Hash[Gem::Version, T::Boolean]))
|
76
|
+
return T.must(@resolvable[version]) if @resolvable.key?(version)
|
57
77
|
|
58
78
|
@resolvable[version] = !!fetch_latest_resolvable_version_string(requirement: "==#{version}")
|
59
79
|
end
|
60
80
|
|
61
81
|
private
|
62
82
|
|
83
|
+
sig { params(requirement: T.nilable(String)).returns(T.nilable(String)) }
|
63
84
|
def fetch_latest_resolvable_version_string(requirement:)
|
64
|
-
@latest_resolvable_version_string ||= {}
|
85
|
+
@latest_resolvable_version_string ||= T.let({}, T.nilable(T::Hash[T.nilable(String), T.nilable(String)]))
|
65
86
|
return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
|
66
87
|
|
67
88
|
@latest_resolvable_version_string[requirement] ||=
|
@@ -81,6 +102,7 @@ module Dependabot
|
|
81
102
|
# rubocop:disable Metrics/PerceivedComplexity
|
82
103
|
# rubocop:disable Metrics/AbcSize
|
83
104
|
# rubocop:disable Metrics/MethodLength
|
105
|
+
sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T.nilable(String)) }
|
84
106
|
def handle_pipenv_errors(error)
|
85
107
|
if error.message.include?("no version found at all") ||
|
86
108
|
error.message.include?("Invalid specifier:") ||
|
@@ -99,16 +121,17 @@ module Dependabot
|
|
99
121
|
end
|
100
122
|
|
101
123
|
if error.message.match?(GIT_REFERENCE_NOT_FOUND_REGEX)
|
102
|
-
|
124
|
+
match_result = error.message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
|
125
|
+
tag = T.must(match_result).named_captures.fetch("tag")
|
103
126
|
# Unfortunately the error message doesn't include the package name.
|
104
127
|
# TODO: Talk with pipenv maintainers about exposing the package name, it used to be part of the error output
|
105
128
|
raise GitDependencyReferenceNotFound, "(unknown package at #{tag})"
|
106
129
|
end
|
107
130
|
|
108
131
|
if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
109
|
-
|
110
|
-
|
111
|
-
raise GitDependenciesNotReachable, url
|
132
|
+
match_result = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
133
|
+
url = T.must(match_result).named_captures.fetch("url")
|
134
|
+
raise GitDependenciesNotReachable, T.must(url)
|
112
135
|
end
|
113
136
|
|
114
137
|
if error.message.include?("Could not find a version") || error.message.include?("ResolutionFailure")
|
@@ -151,6 +174,7 @@ module Dependabot
|
|
151
174
|
# Note: We raise errors from this method, rather than returning a
|
152
175
|
# boolean, so that all deps for this repo will raise identical
|
153
176
|
# errors when failing to update
|
177
|
+
sig { returns(T::Boolean) }
|
154
178
|
def check_original_requirements_resolvable
|
155
179
|
SharedHelpers.in_a_temporary_repo_directory(base_directory, repo_contents_path) do
|
156
180
|
write_temporary_dependency_files(update_pipfile: false)
|
@@ -160,13 +184,16 @@ module Dependabot
|
|
160
184
|
true
|
161
185
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
162
186
|
handle_pipenv_errors_resolving_original_reqs(e)
|
187
|
+
false
|
163
188
|
end
|
164
189
|
end
|
165
190
|
|
191
|
+
sig { returns(String) }
|
166
192
|
def base_directory
|
167
|
-
dependency_files.first.directory
|
193
|
+
T.must(dependency_files.first).directory
|
168
194
|
end
|
169
195
|
|
196
|
+
sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).void }
|
170
197
|
def handle_pipenv_errors_resolving_original_reqs(error)
|
171
198
|
if error.message.include?("Could not find a version") ||
|
172
199
|
error.message.include?("package versions have conflicting dependencies")
|
@@ -193,6 +220,7 @@ module Dependabot
|
|
193
220
|
raise
|
194
221
|
end
|
195
222
|
|
223
|
+
sig { params(message: String).returns(String) }
|
196
224
|
def clean_error_message(message)
|
197
225
|
# Pipenv outputs a lot of things to STDERR, so we need to clean
|
198
226
|
# up the error message
|
@@ -213,9 +241,11 @@ module Dependabot
|
|
213
241
|
msg.gsub(/http.*?(?=\s)/, "<redacted>")
|
214
242
|
end
|
215
243
|
|
244
|
+
sig { params(error_message: String).void }
|
216
245
|
def handle_pipenv_installation_error(error_message)
|
217
246
|
# Find the dependency that's causing resolution to fail
|
218
|
-
|
247
|
+
match_result = error_message.match(PIPENV_INSTALLATION_ERROR_REGEX)
|
248
|
+
dependency_name = T.must(match_result).named_captures["name"]
|
219
249
|
raise unless dependency_name
|
220
250
|
|
221
251
|
msg = "Pipenv failed to install \"#{dependency_name}\". This could be caused by missing system " \
|
@@ -226,6 +256,7 @@ module Dependabot
|
|
226
256
|
raise DependencyFileNotResolvable, msg
|
227
257
|
end
|
228
258
|
|
259
|
+
sig { params(update_pipfile: T::Boolean).void }
|
229
260
|
def write_temporary_dependency_files(update_pipfile: true)
|
230
261
|
dependency_files.each do |file|
|
231
262
|
path = file.name
|
@@ -256,6 +287,7 @@ module Dependabot
|
|
256
287
|
)
|
257
288
|
end
|
258
289
|
|
290
|
+
sig { void }
|
259
291
|
def install_required_python
|
260
292
|
# Initialize a git repo to appease pip-tools
|
261
293
|
begin
|
@@ -267,7 +299,12 @@ module Dependabot
|
|
267
299
|
language_version_manager.install_required_python
|
268
300
|
end
|
269
301
|
|
302
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
270
303
|
def sanitized_setup_file_content(file)
|
304
|
+
@sanitized_setup_file_content = T.let(
|
305
|
+
@sanitized_setup_file_content,
|
306
|
+
T.nilable(T::Hash[String, String])
|
307
|
+
)
|
271
308
|
@sanitized_setup_file_content ||= {}
|
272
309
|
@sanitized_setup_file_content[file.name] ||=
|
273
310
|
Python::FileUpdater::SetupFileSanitizer
|
@@ -275,77 +312,97 @@ module Dependabot
|
|
275
312
|
.sanitized_content
|
276
313
|
end
|
277
314
|
|
315
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
|
278
316
|
def setup_cfg(file)
|
279
317
|
config_name = file.name.sub(/\.py$/, ".cfg")
|
280
318
|
dependency_files.find { |f| f.name == config_name }
|
281
319
|
end
|
282
320
|
|
321
|
+
sig { returns(String) }
|
283
322
|
def pipfile_content
|
284
|
-
|
323
|
+
pipfile_obj = T.must(pipfile)
|
324
|
+
content = T.must(pipfile_obj.content)
|
285
325
|
content = add_private_sources(content)
|
286
326
|
content = update_python_requirement(content)
|
287
|
-
content = update_ssl_requirement(content,
|
327
|
+
content = update_ssl_requirement(content, T.must(pipfile_obj.content))
|
288
328
|
|
289
329
|
content
|
290
330
|
end
|
291
331
|
|
332
|
+
sig { params(pipfile_content: String).returns(String) }
|
292
333
|
def update_python_requirement(pipfile_content)
|
293
334
|
Python::FileUpdater::PipfilePreparer
|
294
335
|
.new(pipfile_content: pipfile_content)
|
295
336
|
.update_python_requirement(language_version_manager.python_major_minor)
|
296
337
|
end
|
297
338
|
|
339
|
+
sig { params(pipfile_content: String, parsed_file: String).returns(String) }
|
298
340
|
def update_ssl_requirement(pipfile_content, parsed_file)
|
299
341
|
Python::FileUpdater::PipfilePreparer
|
300
342
|
.new(pipfile_content: pipfile_content)
|
301
343
|
.update_ssl_requirement(parsed_file)
|
302
344
|
end
|
303
345
|
|
346
|
+
sig { params(pipfile_content: String).returns(String) }
|
304
347
|
def add_private_sources(pipfile_content)
|
305
348
|
Python::FileUpdater::PipfilePreparer
|
306
349
|
.new(pipfile_content: pipfile_content)
|
307
350
|
.replace_sources(credentials)
|
308
351
|
end
|
309
352
|
|
353
|
+
sig { params(command: String).returns(String) }
|
310
354
|
def run_command(command)
|
311
355
|
SharedHelpers.run_shell_command(command, stderr_to_stdout: true)
|
312
356
|
end
|
313
357
|
|
358
|
+
sig { returns(Dependabot::Python::FileParser::PythonRequirementParser) }
|
314
359
|
def python_requirement_parser
|
315
|
-
@python_requirement_parser ||=
|
360
|
+
@python_requirement_parser ||= T.let(
|
316
361
|
FileParser::PythonRequirementParser.new(
|
317
362
|
dependency_files: dependency_files
|
318
|
-
)
|
363
|
+
),
|
364
|
+
T.nilable(Dependabot::Python::FileParser::PythonRequirementParser)
|
365
|
+
)
|
319
366
|
end
|
320
367
|
|
368
|
+
sig { returns(Dependabot::Python::LanguageVersionManager) }
|
321
369
|
def language_version_manager
|
322
|
-
@language_version_manager ||=
|
370
|
+
@language_version_manager ||= T.let(
|
323
371
|
LanguageVersionManager.new(
|
324
372
|
python_requirement_parser: python_requirement_parser
|
325
|
-
)
|
373
|
+
),
|
374
|
+
T.nilable(Dependabot::Python::LanguageVersionManager)
|
375
|
+
)
|
326
376
|
end
|
327
377
|
|
378
|
+
sig { returns(Dependabot::Python::PipenvRunner) }
|
328
379
|
def pipenv_runner
|
329
|
-
@pipenv_runner ||=
|
380
|
+
@pipenv_runner ||= T.let(
|
330
381
|
PipenvRunner.new(
|
331
382
|
dependency: dependency,
|
332
383
|
lockfile: lockfile,
|
333
384
|
language_version_manager: language_version_manager
|
334
|
-
)
|
385
|
+
),
|
386
|
+
T.nilable(Dependabot::Python::PipenvRunner)
|
387
|
+
)
|
335
388
|
end
|
336
389
|
|
390
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
337
391
|
def pipfile
|
338
392
|
dependency_files.find { |f| f.name == "Pipfile" }
|
339
393
|
end
|
340
394
|
|
395
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
341
396
|
def lockfile
|
342
397
|
dependency_files.find { |f| f.name == "Pipfile.lock" }
|
343
398
|
end
|
344
399
|
|
400
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
345
401
|
def setup_files
|
346
402
|
dependency_files.select { |f| f.name.end_with?("setup.py") }
|
347
403
|
end
|
348
404
|
|
405
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
349
406
|
def setup_cfg_files
|
350
407
|
dependency_files.select { |f| f.name.end_with?("setup.cfg") }
|
351
408
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
4
5
|
require "excon"
|
5
6
|
require "toml-rb"
|
6
7
|
require "open3"
|
@@ -43,23 +44,50 @@ module Dependabot
|
|
43
44
|
|
44
45
|
INCOMPATIBLE_CONSTRAINTS = /Incompatible constraints in requirements of (?<dep>.+?) ((?<ver>.+?)):/
|
45
46
|
|
47
|
+
PACKAGE_RESOLVER_ERRORS = T.let({
|
48
|
+
package_info_error: /Unable to determine package info/,
|
49
|
+
self_dep_error: /Package '(?<path>.*)' is listed as a dependency of itself./,
|
50
|
+
incompatible_constraints: /Incompatible constraints in requirements/
|
51
|
+
}.freeze, T::Hash[T.nilable(String), Regexp])
|
52
|
+
|
53
|
+
sig { returns(Dependabot::Dependency) }
|
46
54
|
attr_reader :dependency
|
55
|
+
|
56
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
47
57
|
attr_reader :dependency_files
|
58
|
+
|
59
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
48
60
|
attr_reader :credentials
|
61
|
+
|
62
|
+
sig { returns(T.nilable(String)) }
|
49
63
|
attr_reader :repo_contents_path
|
50
64
|
|
51
65
|
sig { returns(Dependabot::Python::PoetryErrorHandler) }
|
52
66
|
attr_reader :error_handler
|
53
67
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
68
|
+
sig do
|
69
|
+
params(
|
70
|
+
dependency: Dependabot::Dependency,
|
71
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
72
|
+
credentials: T::Array[Dependabot::Credential],
|
73
|
+
repo_contents_path: T.nilable(String)
|
74
|
+
).void
|
61
75
|
end
|
62
|
-
|
76
|
+
def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
|
77
|
+
@dependency = T.let(dependency, Dependabot::Dependency)
|
78
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
79
|
+
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
80
|
+
@repo_contents_path = T.let(repo_contents_path, T.nilable(String))
|
81
|
+
@error_handler = T.let(PoetryErrorHandler.new(dependencies: dependency, dependency_files: dependency_files),
|
82
|
+
Dependabot::Python::PoetryErrorHandler)
|
83
|
+
@resolvable = T.let({}, T::Hash[Gem::Version, T::Boolean])
|
84
|
+
@latest_resolvable_version_string = T.let({}, T::Hash[T.nilable(String), T.nilable(String)])
|
85
|
+
@original_reqs_resolvable = T.let(nil, T.nilable(T::Boolean))
|
86
|
+
@python_requirement_parser = T.let(nil, T.nilable(FileParser::PythonRequirementParser))
|
87
|
+
@language_version_manager = T.let(nil, T.nilable(LanguageVersionManager))
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { params(requirement: T.nilable(String)).returns(T.nilable(Dependabot::Python::Version)) }
|
63
91
|
def latest_resolvable_version(requirement: nil)
|
64
92
|
version_string =
|
65
93
|
fetch_latest_resolvable_version_string(requirement: requirement)
|
@@ -67,9 +95,9 @@ module Dependabot
|
|
67
95
|
version_string.nil? ? nil : Python::Version.new(version_string)
|
68
96
|
end
|
69
97
|
|
98
|
+
sig { params(version: Gem::Version).returns(T::Boolean) }
|
70
99
|
def resolvable?(version:)
|
71
|
-
@resolvable
|
72
|
-
return @resolvable[version] if @resolvable.key?(version)
|
100
|
+
return T.must(@resolvable[version]) if @resolvable.key?(version)
|
73
101
|
|
74
102
|
@resolvable[version] = if fetch_latest_resolvable_version_string(requirement: "==#{version}")
|
75
103
|
true
|
@@ -84,8 +112,8 @@ module Dependabot
|
|
84
112
|
|
85
113
|
private
|
86
114
|
|
115
|
+
sig { params(requirement: T.nilable(String)).returns(T.nilable(String)) }
|
87
116
|
def fetch_latest_resolvable_version_string(requirement:)
|
88
|
-
@latest_resolvable_version_string ||= {}
|
89
117
|
return @latest_resolvable_version_string[requirement] if @latest_resolvable_version_string.key?(requirement)
|
90
118
|
|
91
119
|
@latest_resolvable_version_string[requirement] ||=
|
@@ -112,6 +140,7 @@ module Dependabot
|
|
112
140
|
end
|
113
141
|
end
|
114
142
|
|
143
|
+
sig { params(updated_lockfile: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
|
115
144
|
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
116
145
|
version =
|
117
146
|
updated_lockfile.fetch("package", [])
|
@@ -124,25 +153,26 @@ module Dependabot
|
|
124
153
|
end
|
125
154
|
|
126
155
|
# rubocop:disable Metrics/AbcSize
|
156
|
+
sig { params(error: StandardError).returns(T.nilable(String)) }
|
127
157
|
def handle_poetry_errors(error)
|
128
158
|
error_handler.handle_poetry_error(error)
|
129
159
|
|
130
160
|
if error.message.gsub(/\s/, "").match?(GIT_REFERENCE_NOT_FOUND_REGEX)
|
131
161
|
message = error.message.gsub(/\s/, "")
|
132
162
|
match = message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
|
133
|
-
name = if (url = match.named_captures.fetch("url"))
|
163
|
+
name = if (url = T.must(match).named_captures.fetch("url"))
|
134
164
|
File.basename(T.must(URI.parse(url).path))
|
135
165
|
else
|
136
|
-
message.match(GIT_REFERENCE_NOT_FOUND_REGEX)
|
137
|
-
|
166
|
+
T.must(message.match(GIT_REFERENCE_NOT_FOUND_REGEX))
|
167
|
+
.named_captures.fetch("name")
|
138
168
|
end
|
139
|
-
raise GitDependencyReferenceNotFound, name
|
169
|
+
raise GitDependencyReferenceNotFound, T.must(name)
|
140
170
|
end
|
141
171
|
|
142
172
|
if error.message.match?(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
143
|
-
url = error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX)
|
144
|
-
|
145
|
-
raise GitDependenciesNotReachable, url
|
173
|
+
url = T.must(error.message.match(GIT_DEPENDENCY_UNREACHABLE_REGEX))
|
174
|
+
.named_captures.fetch("url")
|
175
|
+
raise GitDependenciesNotReachable, T.must(url)
|
146
176
|
end
|
147
177
|
|
148
178
|
raise unless error.message.include?("SolverProblemError") ||
|
@@ -163,6 +193,7 @@ module Dependabot
|
|
163
193
|
|
164
194
|
# Using `--lock` avoids doing an install.
|
165
195
|
# Using `--no-interaction` avoids asking for passwords.
|
196
|
+
sig { void }
|
166
197
|
def run_poetry_update_command
|
167
198
|
run_poetry_command(
|
168
199
|
"pyenv exec poetry update #{dependency.name} --lock --no-interaction",
|
@@ -170,6 +201,7 @@ module Dependabot
|
|
170
201
|
)
|
171
202
|
end
|
172
203
|
|
204
|
+
sig { returns(T::Boolean) }
|
173
205
|
def check_original_requirements_resolvable
|
174
206
|
return @original_reqs_resolvable if @original_reqs_resolvable
|
175
207
|
|
@@ -189,11 +221,18 @@ module Dependabot
|
|
189
221
|
end
|
190
222
|
end
|
191
223
|
|
224
|
+
sig { params(message: String).returns(String) }
|
192
225
|
def clean_error_message(message)
|
193
226
|
# Redact any URLs, as they may include credentials
|
194
227
|
message.gsub(/http.*?(?=\s)/, "<redacted>")
|
195
228
|
end
|
196
229
|
|
230
|
+
sig do
|
231
|
+
params(
|
232
|
+
updated_req: T.nilable(String),
|
233
|
+
update_pyproject: T::Boolean
|
234
|
+
).void
|
235
|
+
end
|
197
236
|
def write_temporary_dependency_files(updated_req: nil,
|
198
237
|
update_pyproject: true)
|
199
238
|
dependency_files.each do |file|
|
@@ -216,14 +255,16 @@ module Dependabot
|
|
216
255
|
end
|
217
256
|
end
|
218
257
|
|
258
|
+
sig { void }
|
219
259
|
def add_auth_env_vars
|
220
260
|
Python::FileUpdater::PyprojectPreparer
|
221
|
-
.new(pyproject_content: pyproject.content)
|
261
|
+
.new(pyproject_content: T.must(T.must(pyproject).content))
|
222
262
|
.add_auth_env_vars(credentials)
|
223
263
|
end
|
224
264
|
|
265
|
+
sig { params(updated_requirement: T.nilable(String)).returns(String) }
|
225
266
|
def updated_pyproject_content(updated_requirement:)
|
226
|
-
content = pyproject.content
|
267
|
+
content = T.must(T.must(pyproject).content)
|
227
268
|
content = sanitize_pyproject_content(content)
|
228
269
|
content = update_python_requirement(content)
|
229
270
|
content = freeze_other_dependencies(content)
|
@@ -231,31 +272,36 @@ module Dependabot
|
|
231
272
|
content
|
232
273
|
end
|
233
274
|
|
275
|
+
sig { returns(String) }
|
234
276
|
def sanitized_pyproject_content
|
235
|
-
content = pyproject.content
|
277
|
+
content = T.must(T.must(pyproject).content)
|
236
278
|
content = sanitize_pyproject_content(content)
|
237
279
|
content = update_python_requirement(content)
|
238
280
|
content
|
239
281
|
end
|
240
282
|
|
283
|
+
sig { params(pyproject_content: String).returns(String) }
|
241
284
|
def sanitize_pyproject_content(pyproject_content)
|
242
285
|
Python::FileUpdater::PyprojectPreparer
|
243
286
|
.new(pyproject_content: pyproject_content)
|
244
287
|
.sanitize
|
245
288
|
end
|
246
289
|
|
290
|
+
sig { params(pyproject_content: String).returns(String) }
|
247
291
|
def update_python_requirement(pyproject_content)
|
248
292
|
Python::FileUpdater::PyprojectPreparer
|
249
293
|
.new(pyproject_content: pyproject_content)
|
250
294
|
.update_python_requirement(language_version_manager.python_version)
|
251
295
|
end
|
252
296
|
|
297
|
+
sig { params(pyproject_content: String).returns(String) }
|
253
298
|
def freeze_other_dependencies(pyproject_content)
|
254
299
|
Python::FileUpdater::PyprojectPreparer
|
255
300
|
.new(pyproject_content: pyproject_content, lockfile: lockfile)
|
256
301
|
.freeze_top_level_dependencies_except([dependency])
|
257
302
|
end
|
258
303
|
|
304
|
+
sig { params(pyproject_content: String, updated_requirement: T.nilable(String)).returns(String) }
|
259
305
|
def set_target_dependency_req(pyproject_content, updated_requirement)
|
260
306
|
return pyproject_content unless updated_requirement
|
261
307
|
|
@@ -275,7 +321,7 @@ module Dependabot
|
|
275
321
|
end
|
276
322
|
|
277
323
|
# If this is a sub-dependency, add the new requirement
|
278
|
-
unless dependency.requirements.find { |r| r[:file] == pyproject.name }
|
324
|
+
unless dependency.requirements.find { |r| r[:file] == T.must(pyproject).name }
|
279
325
|
poetry_object[subdep_type] ||= {}
|
280
326
|
poetry_object[subdep_type][dependency.name] = updated_requirement
|
281
327
|
end
|
@@ -283,6 +329,7 @@ module Dependabot
|
|
283
329
|
TomlRB.dump(pyproject_object)
|
284
330
|
end
|
285
331
|
|
332
|
+
sig { params(toml_node: T::Hash[String, T.untyped], requirement: String).void }
|
286
333
|
def update_dependency_requirement(toml_node, requirement)
|
287
334
|
names = toml_node.keys
|
288
335
|
pkg_name = names.find { |nm| normalise(nm) == dependency.name }
|
@@ -295,10 +342,12 @@ module Dependabot
|
|
295
342
|
end
|
296
343
|
end
|
297
344
|
|
345
|
+
sig { returns(String) }
|
298
346
|
def subdep_type
|
299
347
|
dependency.production? ? "dependencies" : "dev-dependencies"
|
300
348
|
end
|
301
349
|
|
350
|
+
sig { returns(FileParser::PythonRequirementParser) }
|
302
351
|
def python_requirement_parser
|
303
352
|
@python_requirement_parser ||=
|
304
353
|
FileParser::PythonRequirementParser.new(
|
@@ -306,6 +355,7 @@ module Dependabot
|
|
306
355
|
)
|
307
356
|
end
|
308
357
|
|
358
|
+
sig { returns(LanguageVersionManager) }
|
309
359
|
def language_version_manager
|
310
360
|
@language_version_manager ||=
|
311
361
|
LanguageVersionManager.new(
|
@@ -313,22 +363,27 @@ module Dependabot
|
|
313
363
|
)
|
314
364
|
end
|
315
365
|
|
366
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
316
367
|
def pyproject
|
317
368
|
dependency_files.find { |f| f.name == "pyproject.toml" }
|
318
369
|
end
|
319
370
|
|
371
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
320
372
|
def poetry_lock
|
321
373
|
dependency_files.find { |f| f.name == "poetry.lock" }
|
322
374
|
end
|
323
375
|
|
376
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
324
377
|
def lockfile
|
325
378
|
poetry_lock
|
326
379
|
end
|
327
380
|
|
381
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
328
382
|
def run_poetry_command(command, fingerprint: nil)
|
329
383
|
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
330
384
|
end
|
331
385
|
|
386
|
+
sig { params(name: String).returns(String) }
|
332
387
|
def normalise(name)
|
333
388
|
NameNormaliser.normalise(name)
|
334
389
|
end
|
@@ -395,8 +450,8 @@ module Dependabot
|
|
395
450
|
).void
|
396
451
|
end
|
397
452
|
def initialize(dependencies:, dependency_files:)
|
398
|
-
@dependencies = dependencies
|
399
|
-
@dependency_files = dependency_files
|
453
|
+
@dependencies = T.let(dependencies, Dependabot::Dependency)
|
454
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
400
455
|
end
|
401
456
|
|
402
457
|
private
|