dependabot-python 0.302.0 → 0.303.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/lib/dependabot/python/file_updater/pipfile_file_updater.rb +99 -20
- data/lib/dependabot/python/file_updater/pipfile_manifest_updater.rb +30 -12
- data/lib/dependabot/python/file_updater/pipfile_preparer.rb +11 -10
- data/lib/dependabot/python/file_updater/poetry_file_updater.rb +95 -24
- data/lib/dependabot/python/file_updater/pyproject_preparer.rb +21 -8
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d0c144294a45d6cd6336b7afbbf9d2fc13692b733af0cd7392f6acea4460bb0
|
4
|
+
data.tar.gz: dc280185471e8a65b47f4397eaa459b1899deace8cb0e9c9fb33a8c3dfb52fdb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e79a47e7971783b7b2f0dd3b68eade4cb1998e2f06284d0894d8c4b4e09937bd4d72b5357d4a1d4037a80da9a60c63b624e118e2f820131fc9480fbc188a2e2
|
7
|
+
data.tar.gz: ba0a2021d5c25e29adb8d59e8ba8c8f83db3dbe6518b25913518dc4f46594a28bdcc20a8a8764d39a9c8e3ade19335d555cb1be2960074034c154108d33cfe8a
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "open3"
|
@@ -10,78 +10,120 @@ require "dependabot/python/language_version_manager"
|
|
10
10
|
require "dependabot/shared_helpers"
|
11
11
|
require "dependabot/python/native_helpers"
|
12
12
|
require "dependabot/python/pipenv_runner"
|
13
|
+
require "sorbet-runtime"
|
13
14
|
|
14
15
|
module Dependabot
|
15
16
|
module Python
|
16
17
|
class FileUpdater
|
17
18
|
class PipfileFileUpdater
|
19
|
+
extend T::Sig
|
18
20
|
require_relative "pipfile_preparer"
|
19
21
|
require_relative "pipfile_manifest_updater"
|
20
22
|
require_relative "setup_file_sanitizer"
|
21
23
|
|
22
|
-
DEPENDENCY_TYPES = %w(packages dev-packages).freeze
|
24
|
+
DEPENDENCY_TYPES = T.let(%w(packages dev-packages).freeze, T::Array[String])
|
23
25
|
|
26
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
24
27
|
attr_reader :dependencies
|
28
|
+
|
29
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
25
30
|
attr_reader :dependency_files
|
31
|
+
|
32
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
26
33
|
attr_reader :credentials
|
34
|
+
|
35
|
+
sig { returns(T.nilable(String)) }
|
27
36
|
attr_reader :repo_contents_path
|
28
37
|
|
38
|
+
# rubocop:disable Metrics/AbcSize
|
39
|
+
sig do
|
40
|
+
params(
|
41
|
+
dependencies: T::Array[Dependabot::Dependency],
|
42
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
43
|
+
credentials: T::Array[Dependabot::Credential],
|
44
|
+
repo_contents_path: T.nilable(String)
|
45
|
+
).void
|
46
|
+
end
|
29
47
|
def initialize(dependencies:, dependency_files:, credentials:, repo_contents_path:)
|
30
48
|
@dependencies = dependencies
|
31
49
|
@dependency_files = dependency_files
|
32
50
|
@credentials = credentials
|
33
51
|
@repo_contents_path = repo_contents_path
|
34
|
-
|
35
|
-
|
52
|
+
@updated_pipfile_content = T.let(nil, T.nilable(String))
|
53
|
+
@updated_lockfile_content = T.let(nil, T.nilable(String))
|
54
|
+
@updated_generated_files = T.let(nil, T.nilable(T::Hash[Symbol, String]))
|
55
|
+
@pipfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
56
|
+
@lockfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
57
|
+
@setup_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
58
|
+
@setup_cfg_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
59
|
+
@requirements_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
60
|
+
@updated_dependency_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
61
|
+
@updated_pipfile_content = T.let(nil, T.nilable(String))
|
62
|
+
@parsed_lockfile = T.let(nil, T.nilable(T::Hash[String, T::Hash[String, Object]]))
|
63
|
+
@pipenv_runner = T.let(nil, T.nilable(PipenvRunner))
|
64
|
+
@language_version_manager = T.let(nil, T.nilable(LanguageVersionManager))
|
65
|
+
@sanitized_setup_file_content = T.let({}, T.untyped)
|
66
|
+
@python_requirement_parser = T.let(nil, T.nilable(FileParser::PythonRequirementParser))
|
67
|
+
end
|
68
|
+
|
69
|
+
# rubocop:enable Metrics/AbcSize
|
70
|
+
|
71
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
36
72
|
def updated_dependency_files
|
37
73
|
@updated_dependency_files ||= fetch_updated_dependency_files
|
38
74
|
end
|
39
75
|
|
40
76
|
private
|
41
77
|
|
78
|
+
sig { returns(T.nilable(Dependabot::Dependency)) }
|
42
79
|
def dependency
|
43
80
|
# For now, we'll only ever be updating a single dependency
|
44
81
|
dependencies.first
|
45
82
|
end
|
46
83
|
|
84
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
47
85
|
def fetch_updated_dependency_files
|
48
86
|
updated_files = []
|
49
87
|
|
50
|
-
if pipfile
|
88
|
+
if pipfile&.content != updated_pipfile_content
|
51
89
|
updated_files <<
|
52
|
-
updated_file(file: pipfile, content: updated_pipfile_content)
|
90
|
+
updated_file(file: T.must(pipfile), content: T.must(updated_pipfile_content))
|
53
91
|
end
|
54
92
|
|
55
93
|
if lockfile
|
56
|
-
raise "Expected Pipfile.lock to change!" if lockfile
|
94
|
+
raise "Expected Pipfile.lock to change!" if lockfile&.content == updated_lockfile_content
|
57
95
|
|
58
96
|
updated_files <<
|
59
|
-
updated_file(file: lockfile, content: updated_lockfile_content)
|
97
|
+
updated_file(file: T.must(lockfile), content: updated_lockfile_content)
|
60
98
|
end
|
61
99
|
|
62
100
|
updated_files += updated_generated_requirements_files
|
63
101
|
updated_files
|
64
102
|
end
|
65
103
|
|
104
|
+
sig { returns(T.nilable(String)) }
|
66
105
|
def updated_pipfile_content
|
67
106
|
@updated_pipfile_content ||=
|
68
107
|
PipfileManifestUpdater.new(
|
69
108
|
dependencies: dependencies,
|
70
|
-
manifest: pipfile
|
109
|
+
manifest: T.must(pipfile)
|
71
110
|
).updated_manifest_content
|
72
111
|
end
|
73
112
|
|
113
|
+
sig { returns(String) }
|
74
114
|
def updated_lockfile_content
|
75
115
|
@updated_lockfile_content ||=
|
76
116
|
updated_generated_files.fetch(:lockfile)
|
77
117
|
end
|
78
118
|
|
119
|
+
sig { returns(T::Boolean) }
|
79
120
|
def generate_updated_requirements_files?
|
80
121
|
return true if generated_requirements_files("default").any?
|
81
122
|
|
82
123
|
generated_requirements_files("develop").any?
|
83
124
|
end
|
84
125
|
|
126
|
+
sig { params(type: String).returns(T::Array[Dependabot::DependencyFile]) }
|
85
127
|
def generated_requirements_files(type)
|
86
128
|
return [] unless lockfile
|
87
129
|
|
@@ -95,12 +137,13 @@ module Dependabot
|
|
95
137
|
# generated using `pipenv requirements`
|
96
138
|
requirements_files.select do |req_file|
|
97
139
|
deps = []
|
98
|
-
req_file.content
|
140
|
+
req_file.content&.scan(regex) { deps << Regexp.last_match }
|
99
141
|
deps = deps.map { |m| m[:name] }
|
100
142
|
deps.sort == pipfile_lock_deps
|
101
143
|
end
|
102
144
|
end
|
103
145
|
|
146
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
104
147
|
def updated_generated_requirements_files
|
105
148
|
updated_files = []
|
106
149
|
|
@@ -121,49 +164,56 @@ module Dependabot
|
|
121
164
|
updated_files
|
122
165
|
end
|
123
166
|
|
167
|
+
sig { returns(String) }
|
124
168
|
def updated_req_content
|
125
169
|
updated_generated_files.fetch(:requirements_txt)
|
126
170
|
end
|
127
171
|
|
172
|
+
sig { returns(String) }
|
128
173
|
def updated_dev_req_content
|
129
174
|
updated_generated_files.fetch(:dev_requirements_txt)
|
130
175
|
end
|
131
176
|
|
177
|
+
sig { returns(String) }
|
132
178
|
def prepared_pipfile_content
|
133
179
|
content = updated_pipfile_content
|
134
|
-
content = add_private_sources(content)
|
180
|
+
content = add_private_sources(content.to_s)
|
135
181
|
content = update_python_requirement(content)
|
136
|
-
content = update_ssl_requirement(content, updated_pipfile_content)
|
182
|
+
content = update_ssl_requirement(content, updated_pipfile_content.to_s)
|
137
183
|
|
138
184
|
content
|
139
185
|
end
|
140
186
|
|
187
|
+
sig { params(pipfile_content: String).returns(String) }
|
141
188
|
def update_python_requirement(pipfile_content)
|
142
189
|
PipfilePreparer
|
143
190
|
.new(pipfile_content: pipfile_content)
|
144
191
|
.update_python_requirement(language_version_manager.python_major_minor)
|
145
192
|
end
|
146
193
|
|
194
|
+
sig { params(pipfile_content: String, parsed_file: String).returns(String) }
|
147
195
|
def update_ssl_requirement(pipfile_content, parsed_file)
|
148
196
|
Python::FileUpdater::PipfilePreparer
|
149
197
|
.new(pipfile_content: pipfile_content)
|
150
198
|
.update_ssl_requirement(parsed_file)
|
151
199
|
end
|
152
200
|
|
201
|
+
sig { params(pipfile_content: String).returns(String) }
|
153
202
|
def add_private_sources(pipfile_content)
|
154
203
|
PipfilePreparer
|
155
204
|
.new(pipfile_content: pipfile_content)
|
156
205
|
.replace_sources(credentials)
|
157
206
|
end
|
158
207
|
|
208
|
+
sig { returns(T::Hash[Symbol, String]) }
|
159
209
|
def updated_generated_files
|
160
210
|
@updated_generated_files ||=
|
161
|
-
SharedHelpers.in_a_temporary_repo_directory(dependency_files.first.directory, repo_contents_path) do
|
211
|
+
SharedHelpers.in_a_temporary_repo_directory(T.must(dependency_files.first).directory, repo_contents_path) do
|
162
212
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
163
213
|
write_temporary_dependency_files(prepared_pipfile_content)
|
164
214
|
install_required_python
|
165
215
|
|
166
|
-
pipenv_runner
|
216
|
+
pipenv_runner&.run_upgrade("==#{dependency&.version}")
|
167
217
|
|
168
218
|
result = { lockfile: File.read("Pipfile.lock") }
|
169
219
|
result[:lockfile] = post_process_lockfile(result[:lockfile])
|
@@ -181,10 +231,11 @@ module Dependabot
|
|
181
231
|
end
|
182
232
|
end
|
183
233
|
|
234
|
+
sig { params(updated_lockfile_content: String).returns(String) }
|
184
235
|
def post_process_lockfile(updated_lockfile_content)
|
185
|
-
pipfile_hash = pipfile_hash_for(updated_pipfile_content)
|
186
|
-
original_reqs = parsed_lockfile["_meta"]["requires"]
|
187
|
-
original_source = parsed_lockfile["_meta"]["sources"]
|
236
|
+
pipfile_hash = pipfile_hash_for(updated_pipfile_content.to_s)
|
237
|
+
original_reqs = T.must(parsed_lockfile["_meta"])["requires"]
|
238
|
+
original_source = T.must(parsed_lockfile["_meta"])["sources"]
|
188
239
|
|
189
240
|
new_lockfile = updated_lockfile_content.dup
|
190
241
|
new_lockfile_json = JSON.parse(new_lockfile)
|
@@ -197,6 +248,7 @@ module Dependabot
|
|
197
248
|
.gsub(/\}\z/, "}\n")
|
198
249
|
end
|
199
250
|
|
251
|
+
sig { returns(Integer) }
|
200
252
|
def generate_updated_requirements_files
|
201
253
|
req_content = run_pipenv_command(
|
202
254
|
"pyenv exec pipenv requirements"
|
@@ -209,14 +261,17 @@ module Dependabot
|
|
209
261
|
File.write("dev-req.txt", dev_req_content)
|
210
262
|
end
|
211
263
|
|
264
|
+
sig { params(command: String).returns(String) }
|
212
265
|
def run_command(command)
|
213
266
|
SharedHelpers.run_shell_command(command)
|
214
267
|
end
|
215
268
|
|
269
|
+
sig { params(command: String).returns(String) }
|
216
270
|
def run_pipenv_command(command)
|
217
|
-
pipenv_runner.run(command)
|
271
|
+
T.must(pipenv_runner).run(command)
|
218
272
|
end
|
219
273
|
|
274
|
+
sig { params(pipfile_content: Object).returns(Integer) }
|
220
275
|
def write_temporary_dependency_files(pipfile_content)
|
221
276
|
dependency_files.each do |file|
|
222
277
|
path = file.name
|
@@ -243,6 +298,7 @@ module Dependabot
|
|
243
298
|
File.write("Pipfile", pipfile_content)
|
244
299
|
end
|
245
300
|
|
301
|
+
sig { returns(T.nilable(String)) }
|
246
302
|
def install_required_python
|
247
303
|
# Initialize a git repo to appease pip-tools
|
248
304
|
begin
|
@@ -254,6 +310,7 @@ module Dependabot
|
|
254
310
|
language_version_manager.install_required_python
|
255
311
|
end
|
256
312
|
|
313
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
257
314
|
def sanitized_setup_file_content(file)
|
258
315
|
@sanitized_setup_file_content ||= {}
|
259
316
|
return @sanitized_setup_file_content[file.name] if @sanitized_setup_file_content[file.name]
|
@@ -264,12 +321,24 @@ module Dependabot
|
|
264
321
|
.sanitized_content
|
265
322
|
end
|
266
323
|
|
324
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
|
267
325
|
def setup_cfg(file)
|
268
326
|
dependency_files.find do |f|
|
269
327
|
f.name == file.name.sub(/\.py$/, ".cfg")
|
270
328
|
end
|
271
329
|
end
|
272
330
|
|
331
|
+
sig do
|
332
|
+
params(
|
333
|
+
pipfile_content: String
|
334
|
+
).returns(
|
335
|
+
T.nilable(
|
336
|
+
T.any(T::Hash[String, T.untyped],
|
337
|
+
String,
|
338
|
+
T::Array[T::Hash[String, T.untyped]])
|
339
|
+
)
|
340
|
+
)
|
341
|
+
end
|
273
342
|
def pipfile_hash_for(pipfile_content)
|
274
343
|
SharedHelpers.in_a_temporary_directory do |dir|
|
275
344
|
File.write(File.join(dir, "Pipfile"), pipfile_content)
|
@@ -281,12 +350,14 @@ module Dependabot
|
|
281
350
|
end
|
282
351
|
end
|
283
352
|
|
353
|
+
sig { params(file: Dependabot::DependencyFile, content: String).returns(Dependabot::DependencyFile) }
|
284
354
|
def updated_file(file:, content:)
|
285
355
|
updated_file = file.dup
|
286
356
|
updated_file.content = content
|
287
357
|
updated_file
|
288
358
|
end
|
289
359
|
|
360
|
+
sig { returns(FileParser::PythonRequirementParser) }
|
290
361
|
def python_requirement_parser
|
291
362
|
@python_requirement_parser ||=
|
292
363
|
FileParser::PythonRequirementParser.new(
|
@@ -294,6 +365,7 @@ module Dependabot
|
|
294
365
|
)
|
295
366
|
end
|
296
367
|
|
368
|
+
sig { returns(LanguageVersionManager) }
|
297
369
|
def language_version_manager
|
298
370
|
@language_version_manager ||=
|
299
371
|
LanguageVersionManager.new(
|
@@ -301,35 +373,42 @@ module Dependabot
|
|
301
373
|
)
|
302
374
|
end
|
303
375
|
|
376
|
+
sig { returns(T.nilable(PipenvRunner)) }
|
304
377
|
def pipenv_runner
|
305
378
|
@pipenv_runner ||=
|
306
379
|
PipenvRunner.new(
|
307
|
-
dependency: dependency,
|
380
|
+
dependency: T.must(dependency),
|
308
381
|
lockfile: lockfile,
|
309
382
|
language_version_manager: language_version_manager
|
310
383
|
)
|
311
384
|
end
|
312
385
|
|
386
|
+
sig { returns(T::Hash[String, T::Hash[String, T.untyped]]) }
|
313
387
|
def parsed_lockfile
|
314
|
-
@parsed_lockfile ||= JSON.parse(lockfile
|
388
|
+
@parsed_lockfile ||= JSON.parse(T.must(lockfile&.content))
|
315
389
|
end
|
316
390
|
|
391
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
317
392
|
def pipfile
|
318
393
|
@pipfile ||= dependency_files.find { |f| f.name == "Pipfile" }
|
319
394
|
end
|
320
395
|
|
396
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
321
397
|
def lockfile
|
322
398
|
@lockfile ||= dependency_files.find { |f| f.name == "Pipfile.lock" }
|
323
399
|
end
|
324
400
|
|
401
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
325
402
|
def setup_files
|
326
403
|
dependency_files.select { |f| f.name.end_with?("setup.py") }
|
327
404
|
end
|
328
405
|
|
406
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
329
407
|
def setup_cfg_files
|
330
408
|
dependency_files.select { |f| f.name.end_with?("setup.cfg") }
|
331
409
|
end
|
332
410
|
|
411
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
333
412
|
def requirements_files
|
334
413
|
dependency_files.select { |f| f.name.end_with?(".txt") }
|
335
414
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/python/file_updater"
|
@@ -7,11 +7,15 @@ module Dependabot
|
|
7
7
|
module Python
|
8
8
|
class FileUpdater
|
9
9
|
class PipfileManifestUpdater
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(dependencies: T::Array[Dependency], manifest: DependencyFile).void }
|
10
13
|
def initialize(dependencies:, manifest:)
|
11
14
|
@dependencies = dependencies
|
12
15
|
@manifest = manifest
|
13
16
|
end
|
14
17
|
|
18
|
+
sig { returns(T.nilable(String)) }
|
15
19
|
def updated_manifest_content
|
16
20
|
dependencies
|
17
21
|
.select { |dep| requirement_changed?(dep) }
|
@@ -19,7 +23,7 @@ module Dependabot
|
|
19
23
|
updated_content = content
|
20
24
|
|
21
25
|
updated_content = update_requirements(
|
22
|
-
content: updated_content,
|
26
|
+
content: T.must(updated_content),
|
23
27
|
dependency: dep
|
24
28
|
)
|
25
29
|
|
@@ -31,28 +35,31 @@ module Dependabot
|
|
31
35
|
|
32
36
|
private
|
33
37
|
|
38
|
+
sig { returns(T::Array[Dependency]) }
|
34
39
|
attr_reader :dependencies
|
40
|
+
sig { returns(DependencyFile) }
|
35
41
|
attr_reader :manifest
|
36
42
|
|
43
|
+
sig { params(content: String, dependency: Dependency).returns(String) }
|
37
44
|
def update_requirements(content:, dependency:)
|
38
45
|
updated_content = content.dup
|
39
46
|
|
40
47
|
# The UpdateChecker ensures the order of requirements is preserved
|
41
48
|
# when updating, so we can zip them together in new/old pairs.
|
42
49
|
reqs = dependency.requirements
|
43
|
-
.zip(dependency.previous_requirements)
|
50
|
+
.zip(T.must(dependency.previous_requirements))
|
44
51
|
.reject { |new_req, old_req| new_req == old_req }
|
45
52
|
|
46
53
|
# Loop through each changed requirement
|
47
54
|
reqs.each do |new_req, old_req|
|
48
|
-
raise "Bad req match" unless new_req[:file] == old_req[:file]
|
49
|
-
next if new_req[:requirement] == old_req[:requirement]
|
55
|
+
raise "Bad req match" unless new_req[:file] == T.must(old_req)[:file]
|
56
|
+
next if new_req[:requirement] == T.must(old_req)[:requirement]
|
50
57
|
next unless new_req[:file] == manifest.name
|
51
58
|
|
52
59
|
updated_content = update_manifest_req(
|
53
60
|
content: updated_content,
|
54
61
|
dep: dependency,
|
55
|
-
old_req: old_req.fetch(:requirement),
|
62
|
+
old_req: T.must(old_req).fetch(:requirement),
|
56
63
|
new_req: new_req.fetch(:requirement)
|
57
64
|
)
|
58
65
|
end
|
@@ -60,33 +67,43 @@ module Dependabot
|
|
60
67
|
updated_content
|
61
68
|
end
|
62
69
|
|
70
|
+
sig do
|
71
|
+
params(
|
72
|
+
content: String,
|
73
|
+
dep: Dependency,
|
74
|
+
old_req: String,
|
75
|
+
new_req: String
|
76
|
+
).returns(String)
|
77
|
+
end
|
63
78
|
def update_manifest_req(content:, dep:, old_req:, new_req:)
|
64
79
|
simple_declaration = content.scan(declaration_regex(dep))
|
65
80
|
.find { |m| m.include?(old_req) }
|
66
81
|
|
67
82
|
if simple_declaration
|
68
83
|
simple_declaration_regex =
|
69
|
-
/(?:^|["'])#{Regexp.escape(simple_declaration)}/
|
84
|
+
/(?:^|["'])#{Regexp.escape(simple_declaration.to_s)}/
|
70
85
|
content.gsub(simple_declaration_regex) do |line|
|
71
86
|
line.gsub(old_req, new_req)
|
72
87
|
end
|
73
88
|
elsif content.match?(table_declaration_version_regex(dep))
|
74
89
|
content.gsub(table_declaration_version_regex(dep)) do |part|
|
75
|
-
line = content.match(table_declaration_version_regex(dep))
|
76
|
-
|
77
|
-
new_line = line.gsub(old_req, new_req)
|
78
|
-
part.gsub(line, new_line)
|
90
|
+
line = T.must(content.match(table_declaration_version_regex(dep)))
|
91
|
+
.named_captures.fetch("version_declaration")
|
92
|
+
new_line = T.must(line).gsub(old_req, new_req)
|
93
|
+
part.gsub(T.must(line), new_line)
|
79
94
|
end
|
80
95
|
else
|
81
96
|
content
|
82
97
|
end
|
83
98
|
end
|
84
99
|
|
100
|
+
sig { params(dep: Dependency).returns(Regexp) }
|
85
101
|
def declaration_regex(dep)
|
86
102
|
escaped_name = Regexp.escape(dep.name).gsub("\\-", "[-_.]")
|
87
103
|
/(?:^|["'])#{escaped_name}["']?\s*=.*$/i
|
88
104
|
end
|
89
105
|
|
106
|
+
sig { params(dep: Dependabot::Dependency).returns(Regexp) }
|
90
107
|
def table_declaration_version_regex(dep)
|
91
108
|
/
|
92
109
|
packages\.#{Regexp.quote(dep.name)}\]
|
@@ -95,9 +112,10 @@ module Dependabot
|
|
95
112
|
/mx
|
96
113
|
end
|
97
114
|
|
115
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
|
98
116
|
def requirement_changed?(dependency)
|
99
117
|
changed_requirements =
|
100
|
-
dependency.requirements - dependency.previous_requirements
|
118
|
+
dependency.requirements - T.must(dependency.previous_requirements)
|
101
119
|
|
102
120
|
changed_requirements.any? { |f| f[:file] == manifest.name }
|
103
121
|
end
|
@@ -7,6 +7,7 @@ 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 "sorbet-runtime"
|
10
11
|
|
11
12
|
module Dependabot
|
12
13
|
module Python
|
@@ -19,7 +20,7 @@ module Dependabot
|
|
19
20
|
@pipfile_content = pipfile_content
|
20
21
|
end
|
21
22
|
|
22
|
-
sig { params(credentials: T::Array[
|
23
|
+
sig { params(credentials: T::Array[Dependabot::Credential]).returns(String) }
|
23
24
|
def replace_sources(credentials)
|
24
25
|
pipfile_object = TomlRB.parse(pipfile_content)
|
25
26
|
|
@@ -67,23 +68,23 @@ module Dependabot
|
|
67
68
|
sig { returns(String) }
|
68
69
|
attr_reader :pipfile_content
|
69
70
|
|
70
|
-
sig { returns(T::Array[T::Hash[String,
|
71
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
71
72
|
def pipfile_sources
|
72
73
|
@pipfile_sources ||= T.let(TomlRB.parse(pipfile_content).fetch("source", []),
|
73
|
-
T.nilable(T::Array[T::Hash[String,
|
74
|
+
T.nilable(T::Array[T::Hash[String, String]]))
|
74
75
|
end
|
75
76
|
|
76
77
|
sig do
|
77
|
-
params(source: T::Hash[String,
|
78
|
-
credentials: T::Array[
|
78
|
+
params(source: T::Hash[String, String],
|
79
|
+
credentials: T::Array[Dependabot::Credential]).returns(T.nilable(T::Hash[String, String]))
|
79
80
|
end
|
80
81
|
def sub_auth_url(source, credentials)
|
81
|
-
if source["url"]
|
82
|
-
base_url = source["url"]
|
82
|
+
if source["url"]&.include?("${")
|
83
|
+
base_url = source["url"]&.sub(/\${.*}@/, "")
|
83
84
|
|
84
85
|
source_cred = credentials
|
85
86
|
.select { |cred| cred["type"] == "python_index" && cred["index-url"] }
|
86
|
-
.find { |c| c["index-url"].sub(/\${.*}@/, "") == base_url }
|
87
|
+
.find { |c| T.must(c["index-url"]).sub(/\${.*}@/, "") == base_url }
|
87
88
|
|
88
89
|
return nil if source_cred.nil?
|
89
90
|
|
@@ -93,9 +94,9 @@ module Dependabot
|
|
93
94
|
source
|
94
95
|
end
|
95
96
|
|
96
|
-
sig { params(credentials: T::Array[
|
97
|
+
sig { params(credentials: T::Array[Dependabot::Credential]).returns(T::Array[T::Hash[String, String]]) }
|
97
98
|
def config_variable_sources(credentials)
|
98
|
-
@config_variable_sources = T.let([], T.nilable(T::Array[T::Hash[String,
|
99
|
+
@config_variable_sources = T.let([], T.nilable(T::Array[T::Hash[String, String]]))
|
99
100
|
@config_variable_sources =
|
100
101
|
credentials.select { |cred| cred["type"] == "python_index" }.map.with_index do |c, i|
|
101
102
|
{
|
@@ -1,6 +1,7 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
4
5
|
require "toml-rb"
|
5
6
|
require "open3"
|
6
7
|
require "dependabot/dependency"
|
@@ -18,59 +19,86 @@ module Dependabot
|
|
18
19
|
class FileUpdater
|
19
20
|
class PoetryFileUpdater
|
20
21
|
require_relative "pyproject_preparer"
|
22
|
+
extend T::Sig
|
21
23
|
|
22
|
-
|
24
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
23
25
|
attr_reader :dependency_files
|
26
|
+
|
27
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
24
28
|
attr_reader :credentials
|
25
29
|
|
30
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
31
|
+
attr_reader :dependencies
|
32
|
+
|
33
|
+
sig do
|
34
|
+
params(
|
35
|
+
dependencies: T::Array[Dependabot::Dependency],
|
36
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
37
|
+
credentials: T::Array[Dependabot::Credential]
|
38
|
+
).void
|
39
|
+
end
|
26
40
|
def initialize(dependencies:, dependency_files:, credentials:)
|
27
41
|
@dependencies = dependencies
|
28
42
|
@dependency_files = dependency_files
|
29
43
|
@credentials = credentials
|
30
|
-
|
31
|
-
|
44
|
+
@updated_dependency_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
45
|
+
@prepared_pyproject = T.let(nil, T.nilable(String))
|
46
|
+
@pyproject = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
47
|
+
@lockfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
48
|
+
@updated_lockfile_content = T.let(nil, T.nilable(String))
|
49
|
+
@language_version_manager = T.let(nil, T.nilable(LanguageVersionManager))
|
50
|
+
@python_requirement_parser = T.let(nil, T.nilable(FileParser::PythonRequirementParser))
|
51
|
+
@updated_pyproject_content = T.let(nil, T.nilable(String))
|
52
|
+
@python_helper_path = T.let(nil, T.nilable(String))
|
53
|
+
@poetry_lock = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
32
57
|
def updated_dependency_files
|
33
58
|
@updated_dependency_files ||= fetch_updated_dependency_files
|
34
59
|
end
|
35
60
|
|
36
61
|
private
|
37
62
|
|
63
|
+
sig { returns(Dependabot::Dependency) }
|
38
64
|
def dependency
|
39
65
|
# For now, we'll only ever be updating a single dependency
|
40
|
-
dependencies.first
|
66
|
+
T.must(dependencies.first)
|
41
67
|
end
|
42
68
|
|
69
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
43
70
|
def fetch_updated_dependency_files
|
44
71
|
updated_files = []
|
45
72
|
|
46
|
-
if file_changed?(pyproject)
|
73
|
+
if file_changed?(T.must(pyproject))
|
47
74
|
updated_files <<
|
48
75
|
updated_file(
|
49
|
-
file: pyproject,
|
50
|
-
content: updated_pyproject_content
|
76
|
+
file: T.must(pyproject),
|
77
|
+
content: T.must(updated_pyproject_content)
|
51
78
|
)
|
52
79
|
end
|
53
80
|
|
54
|
-
raise "Expected lockfile to change!" if lockfile && lockfile
|
81
|
+
raise "Expected lockfile to change!" if lockfile && lockfile&.content == updated_lockfile_content
|
55
82
|
|
56
83
|
if lockfile
|
57
84
|
updated_files <<
|
58
|
-
updated_file(file: lockfile, content: updated_lockfile_content)
|
85
|
+
updated_file(file: T.must(lockfile), content: updated_lockfile_content)
|
59
86
|
end
|
60
87
|
|
61
88
|
updated_files
|
62
89
|
end
|
63
90
|
|
91
|
+
sig { returns(T.nilable(String)) }
|
64
92
|
def updated_pyproject_content
|
65
|
-
content = pyproject.content
|
66
|
-
return content unless requirement_changed?(pyproject, dependency)
|
93
|
+
content = T.must(pyproject).content
|
94
|
+
return content unless requirement_changed?(T.must(pyproject), dependency)
|
67
95
|
|
68
96
|
updated_content = content.dup
|
69
97
|
|
70
|
-
dependency.requirements.zip(dependency.previous_requirements).each do |new_r, old_r|
|
71
|
-
next unless new_r[:file] == pyproject
|
98
|
+
dependency.requirements.zip(T.must(dependency.previous_requirements)).each do |new_r, old_r|
|
99
|
+
next unless new_r[:file] == pyproject&.name && T.must(old_r)[:file] == pyproject&.name
|
72
100
|
|
73
|
-
updated_content = replace_dep(dependency, updated_content, new_r, old_r)
|
101
|
+
updated_content = replace_dep(dependency, T.must(updated_content), new_r, T.must(old_r))
|
74
102
|
end
|
75
103
|
|
76
104
|
raise DependencyFileContentNotChanged, "Content did not change!" if content == updated_content
|
@@ -78,6 +106,14 @@ module Dependabot
|
|
78
106
|
updated_content
|
79
107
|
end
|
80
108
|
|
109
|
+
sig do
|
110
|
+
params(
|
111
|
+
dep: Dependabot::Dependency,
|
112
|
+
content: String,
|
113
|
+
new_r: T::Hash[Symbol, T.untyped],
|
114
|
+
old_r: T::Hash[Symbol, T.untyped]
|
115
|
+
).returns(String)
|
116
|
+
end
|
81
117
|
def replace_dep(dep, content, new_r, old_r)
|
82
118
|
new_req = new_r[:requirement]
|
83
119
|
old_req = old_r[:requirement]
|
@@ -86,8 +122,8 @@ module Dependabot
|
|
86
122
|
declaration_match = content.match(declaration_regex)
|
87
123
|
if declaration_match
|
88
124
|
declaration = declaration_match[:declaration]
|
89
|
-
new_declaration = declaration.sub(old_req, new_req)
|
90
|
-
content.sub(declaration, new_declaration)
|
125
|
+
new_declaration = T.must(declaration).sub(old_req, new_req)
|
126
|
+
content.sub(T.must(declaration), new_declaration)
|
91
127
|
else
|
92
128
|
content.gsub(table_declaration_regex(dep, new_r)) do |match|
|
93
129
|
match.gsub(/(\s*version\s*=\s*["'])#{Regexp.escape(old_req)}/,
|
@@ -96,12 +132,13 @@ module Dependabot
|
|
96
132
|
end
|
97
133
|
end
|
98
134
|
|
135
|
+
sig { returns(String) }
|
99
136
|
def updated_lockfile_content
|
100
137
|
@updated_lockfile_content ||=
|
101
138
|
begin
|
102
139
|
new_lockfile = updated_lockfile_content_for(prepared_pyproject)
|
103
140
|
|
104
|
-
original_locked_python = TomlRB.parse(lockfile.content)["metadata"]["python-versions"]
|
141
|
+
original_locked_python = TomlRB.parse(T.must(lockfile).content)["metadata"]["python-versions"]
|
105
142
|
|
106
143
|
new_lockfile.gsub!(/\[metadata\]\n.*python-versions[^\n]+\n/m) do |match|
|
107
144
|
match.gsub(/(["']).*(['"])\n\Z/, '\1' + original_locked_python + '\1' + "\n")
|
@@ -109,17 +146,18 @@ module Dependabot
|
|
109
146
|
|
110
147
|
tmp_hash =
|
111
148
|
TomlRB.parse(new_lockfile)["metadata"]["content-hash"]
|
112
|
-
correct_hash = pyproject_hash_for(updated_pyproject_content)
|
149
|
+
correct_hash = pyproject_hash_for(updated_pyproject_content.to_s)
|
113
150
|
|
114
|
-
new_lockfile.gsub(tmp_hash, correct_hash)
|
151
|
+
new_lockfile.gsub(tmp_hash, T.must(correct_hash).to_s)
|
115
152
|
end
|
116
153
|
end
|
117
154
|
|
155
|
+
sig { returns(String) }
|
118
156
|
def prepared_pyproject
|
119
157
|
@prepared_pyproject ||=
|
120
158
|
begin
|
121
159
|
content = updated_pyproject_content
|
122
|
-
content = sanitize(content)
|
160
|
+
content = sanitize(T.must(content))
|
123
161
|
content = freeze_other_dependencies(content)
|
124
162
|
content = freeze_dependencies_being_updated(content)
|
125
163
|
content = update_python_requirement(content)
|
@@ -127,18 +165,20 @@ module Dependabot
|
|
127
165
|
end
|
128
166
|
end
|
129
167
|
|
168
|
+
sig { params(pyproject_content: String).returns(String) }
|
130
169
|
def freeze_other_dependencies(pyproject_content)
|
131
170
|
PyprojectPreparer
|
132
171
|
.new(pyproject_content: pyproject_content, lockfile: lockfile)
|
133
172
|
.freeze_top_level_dependencies_except(dependencies)
|
134
173
|
end
|
135
174
|
|
175
|
+
sig { params(pyproject_content: String).returns(String) }
|
136
176
|
def freeze_dependencies_being_updated(pyproject_content)
|
137
177
|
pyproject_object = TomlRB.parse(pyproject_content)
|
138
178
|
poetry_object = pyproject_object.fetch("tool").fetch("poetry")
|
139
179
|
|
140
180
|
dependencies.each do |dep|
|
141
|
-
if dep.requirements.find { |r| r[:file] == pyproject
|
181
|
+
if dep.requirements.find { |r| r[:file] == pyproject&.name }
|
142
182
|
lock_declaration_to_new_version!(poetry_object, dep)
|
143
183
|
else
|
144
184
|
create_declaration_at_new_version!(poetry_object, dep)
|
@@ -148,12 +188,14 @@ module Dependabot
|
|
148
188
|
TomlRB.dump(pyproject_object)
|
149
189
|
end
|
150
190
|
|
191
|
+
sig { params(pyproject_content: String).returns(String) }
|
151
192
|
def update_python_requirement(pyproject_content)
|
152
193
|
PyprojectPreparer
|
153
194
|
.new(pyproject_content: pyproject_content)
|
154
195
|
.update_python_requirement(language_version_manager.python_version)
|
155
196
|
end
|
156
197
|
|
198
|
+
sig { params(poetry_object: T::Hash[String, T.untyped], dep: Dependabot::Dependency).returns(T::Array[String]) }
|
157
199
|
def lock_declaration_to_new_version!(poetry_object, dep)
|
158
200
|
Dependabot::Python::FileParser::PyprojectFilesParser::POETRY_DEPENDENCY_TYPES.each do |type|
|
159
201
|
names = poetry_object[type]&.keys || []
|
@@ -168,6 +210,7 @@ module Dependabot
|
|
168
210
|
end
|
169
211
|
end
|
170
212
|
|
213
|
+
sig { params(poetry_object: T::Hash[String, T.untyped], dep: Dependabot::Dependency).void }
|
171
214
|
def create_declaration_at_new_version!(poetry_object, dep)
|
172
215
|
subdep_type = dep.production? ? "dependencies" : "dev-dependencies"
|
173
216
|
|
@@ -175,12 +218,14 @@ module Dependabot
|
|
175
218
|
poetry_object[subdep_type][dep.name] = dep.version
|
176
219
|
end
|
177
220
|
|
221
|
+
sig { params(pyproject_content: String).returns(String) }
|
178
222
|
def sanitize(pyproject_content)
|
179
223
|
PyprojectPreparer
|
180
224
|
.new(pyproject_content: pyproject_content)
|
181
225
|
.sanitize
|
182
226
|
end
|
183
227
|
|
228
|
+
sig { params(pyproject_content: String).returns(String) }
|
184
229
|
def updated_lockfile_content_for(pyproject_content)
|
185
230
|
SharedHelpers.in_a_temporary_directory do
|
186
231
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
@@ -201,6 +246,7 @@ module Dependabot
|
|
201
246
|
|
202
247
|
# Using `--lock` avoids doing an install.
|
203
248
|
# Using `--no-interaction` avoids asking for passwords.
|
249
|
+
sig { returns(String) }
|
204
250
|
def run_poetry_update_command
|
205
251
|
run_poetry_command(
|
206
252
|
"pyenv exec poetry update #{dependency.name} --lock --no-interaction",
|
@@ -208,10 +254,12 @@ module Dependabot
|
|
208
254
|
)
|
209
255
|
end
|
210
256
|
|
257
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
211
258
|
def run_poetry_command(command, fingerprint: nil)
|
212
259
|
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
213
260
|
end
|
214
261
|
|
262
|
+
sig { params(pyproject_content: Object).returns(Integer) }
|
215
263
|
def write_temporary_dependency_files(pyproject_content)
|
216
264
|
dependency_files.each do |file|
|
217
265
|
path = file.name
|
@@ -226,12 +274,22 @@ module Dependabot
|
|
226
274
|
File.write("pyproject.toml", pyproject_content)
|
227
275
|
end
|
228
276
|
|
277
|
+
sig { void }
|
229
278
|
def add_auth_env_vars
|
230
279
|
Python::FileUpdater::PyprojectPreparer
|
231
|
-
.new(pyproject_content: pyproject
|
280
|
+
.new(pyproject_content: T.must(pyproject&.content))
|
232
281
|
.add_auth_env_vars(credentials)
|
233
282
|
end
|
234
283
|
|
284
|
+
sig do
|
285
|
+
params(
|
286
|
+
pyproject_content: String
|
287
|
+
).returns(T.nilable(T.any(
|
288
|
+
T::Hash[String, T.untyped],
|
289
|
+
String,
|
290
|
+
T::Array[T::Hash[String, T.untyped]]
|
291
|
+
)))
|
292
|
+
end
|
235
293
|
def pyproject_hash_for(pyproject_content)
|
236
294
|
SharedHelpers.in_a_temporary_directory do |dir|
|
237
295
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
@@ -246,6 +304,7 @@ module Dependabot
|
|
246
304
|
end
|
247
305
|
end
|
248
306
|
|
307
|
+
sig { params(dep: Dependabot::Dependency, old_req: T::Hash[Symbol, T.untyped]).returns(Regexp) }
|
249
308
|
def declaration_regex(dep, old_req)
|
250
309
|
group = old_req[:groups].first
|
251
310
|
|
@@ -253,35 +312,42 @@ module Dependabot
|
|
253
312
|
/#{header_regex}\n.*?(?<declaration>(?:^\s*|["'])#{escape(dep)}["']?\s*=[^\n]*)$/mi
|
254
313
|
end
|
255
314
|
|
315
|
+
sig { params(dep: Dependabot::Dependency, old_req: T::Hash[Symbol, T.untyped]).returns(Regexp) }
|
256
316
|
def table_declaration_regex(dep, old_req)
|
257
317
|
/tool\.poetry\.#{old_req[:groups].first}\.#{escape(dep)}\]\n.*?\s*version\s* =.*?\n/m
|
258
318
|
end
|
259
319
|
|
320
|
+
sig { params(dep: Dependency).returns(String) }
|
260
321
|
def escape(dep)
|
261
322
|
Regexp.escape(dep.name).gsub("\\-", "[-_.]")
|
262
323
|
end
|
263
324
|
|
325
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
|
264
326
|
def file_changed?(file)
|
265
327
|
dependencies.any? { |dep| requirement_changed?(file, dep) }
|
266
328
|
end
|
267
329
|
|
330
|
+
sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
|
268
331
|
def requirement_changed?(file, dependency)
|
269
332
|
changed_requirements =
|
270
|
-
dependency.requirements - dependency.previous_requirements
|
333
|
+
dependency.requirements - T.must(dependency.previous_requirements)
|
271
334
|
|
272
335
|
changed_requirements.any? { |f| f[:file] == file.name }
|
273
336
|
end
|
274
337
|
|
338
|
+
sig { params(file: Dependabot::DependencyFile, content: String).returns(Dependabot::DependencyFile) }
|
275
339
|
def updated_file(file:, content:)
|
276
340
|
updated_file = file.dup
|
277
341
|
updated_file.content = content
|
278
342
|
updated_file
|
279
343
|
end
|
280
344
|
|
345
|
+
sig { params(name: String).returns(String) }
|
281
346
|
def normalise(name)
|
282
347
|
NameNormaliser.normalise(name)
|
283
348
|
end
|
284
349
|
|
350
|
+
sig { returns(FileParser::PythonRequirementParser) }
|
285
351
|
def python_requirement_parser
|
286
352
|
@python_requirement_parser ||=
|
287
353
|
FileParser::PythonRequirementParser.new(
|
@@ -289,6 +355,7 @@ module Dependabot
|
|
289
355
|
)
|
290
356
|
end
|
291
357
|
|
358
|
+
sig { returns(Dependabot::Python::LanguageVersionManager) }
|
292
359
|
def language_version_manager
|
293
360
|
@language_version_manager ||=
|
294
361
|
LanguageVersionManager.new(
|
@@ -296,19 +363,23 @@ module Dependabot
|
|
296
363
|
)
|
297
364
|
end
|
298
365
|
|
366
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
299
367
|
def pyproject
|
300
368
|
@pyproject ||=
|
301
369
|
dependency_files.find { |f| f.name == "pyproject.toml" }
|
302
370
|
end
|
303
371
|
|
372
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
304
373
|
def lockfile
|
305
374
|
@lockfile ||= poetry_lock
|
306
375
|
end
|
307
376
|
|
377
|
+
sig { returns(String) }
|
308
378
|
def python_helper_path
|
309
379
|
NativeHelpers.python_helper_path
|
310
380
|
end
|
311
381
|
|
382
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
312
383
|
def poetry_lock
|
313
384
|
dependency_files.find { |f| f.name == "poetry.lock" }
|
314
385
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "toml-rb"
|
5
|
-
|
5
|
+
require "sorbet-runtime"
|
6
6
|
require "dependabot/dependency"
|
7
7
|
require "dependabot/python/file_parser"
|
8
8
|
require "dependabot/python/file_updater"
|
@@ -14,13 +14,18 @@ module Dependabot
|
|
14
14
|
module Python
|
15
15
|
class FileUpdater
|
16
16
|
class PyprojectPreparer
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
sig { params(pyproject_content: String, lockfile: T.nilable(Dependabot::DependencyFile)).void }
|
17
20
|
def initialize(pyproject_content:, lockfile: nil)
|
18
21
|
@pyproject_content = pyproject_content
|
19
22
|
@lockfile = lockfile
|
23
|
+
@parsed_lockfile = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
20
24
|
end
|
21
25
|
|
22
26
|
# For hosted Dependabot token will be nil since the credentials aren't present.
|
23
27
|
# This is for those running Dependabot themselves and for dry-run.
|
28
|
+
sig { params(credentials: T.nilable(T::Array[Dependabot::Credential])).void }
|
24
29
|
def add_auth_env_vars(credentials)
|
25
30
|
TomlRB.parse(@pyproject_content).dig("tool", "poetry", "source")&.each do |source|
|
26
31
|
cred = credentials&.find { |c| c["index-url"] == source["url"] }
|
@@ -37,6 +42,7 @@ module Dependabot
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
45
|
+
sig { params(requirement: String).returns(String) }
|
40
46
|
def update_python_requirement(requirement)
|
41
47
|
pyproject_object = TomlRB.parse(@pyproject_content)
|
42
48
|
if (python_specification = pyproject_object.dig("tool", "poetry", "dependencies", "python"))
|
@@ -48,6 +54,7 @@ module Dependabot
|
|
48
54
|
TomlRB.dump(pyproject_object)
|
49
55
|
end
|
50
56
|
|
57
|
+
sig { returns(String) }
|
51
58
|
def sanitize
|
52
59
|
# {{ name }} syntax not allowed
|
53
60
|
pyproject_content
|
@@ -57,6 +64,7 @@ module Dependabot
|
|
57
64
|
|
58
65
|
# rubocop:disable Metrics/PerceivedComplexity
|
59
66
|
# rubocop:disable Metrics/AbcSize
|
67
|
+
sig { params(dependencies: T::Array[Dependabot::Dependency]).returns(String) }
|
60
68
|
def freeze_top_level_dependencies_except(dependencies)
|
61
69
|
return pyproject_content unless lockfile
|
62
70
|
|
@@ -75,14 +83,14 @@ module Dependabot
|
|
75
83
|
|
76
84
|
next unless (locked_version = locked_details&.fetch("version"))
|
77
85
|
|
78
|
-
next if source_types.include?(locked_details
|
86
|
+
next if source_types.include?(locked_details.dig("source", "type"))
|
79
87
|
|
80
|
-
if locked_details
|
88
|
+
if locked_details.dig("source", "type") == "git"
|
81
89
|
poetry_object[key][dep_name] = {
|
82
|
-
"git" => locked_details
|
83
|
-
"rev" => locked_details
|
90
|
+
"git" => locked_details.dig("source", "url"),
|
91
|
+
"rev" => locked_details.dig("source", "reference")
|
84
92
|
}
|
85
|
-
subdirectory = locked_details
|
93
|
+
subdirectory = locked_details.dig("source", "subdirectory")
|
86
94
|
poetry_object[key][dep_name]["subdirectory"] = subdirectory if subdirectory
|
87
95
|
elsif poetry_object[key][dep_name].is_a?(Hash)
|
88
96
|
poetry_object[key][dep_name]["version"] = locked_version
|
@@ -103,20 +111,25 @@ module Dependabot
|
|
103
111
|
|
104
112
|
private
|
105
113
|
|
114
|
+
sig { returns(String) }
|
106
115
|
attr_reader :pyproject_content
|
116
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
107
117
|
attr_reader :lockfile
|
108
118
|
|
119
|
+
sig { params(dep_name: String).returns(T.nilable(T::Hash[String, T.untyped])) }
|
109
120
|
def locked_details(dep_name)
|
110
121
|
parsed_lockfile.fetch("package")
|
111
122
|
.find { |d| d["name"] == normalise(dep_name) }
|
112
123
|
end
|
113
124
|
|
125
|
+
sig { params(name: String).returns(String) }
|
114
126
|
def normalise(name)
|
115
127
|
NameNormaliser.normalise(name)
|
116
128
|
end
|
117
129
|
|
130
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
118
131
|
def parsed_lockfile
|
119
|
-
@parsed_lockfile ||= TomlRB.parse(lockfile
|
132
|
+
@parsed_lockfile ||= TomlRB.parse(lockfile&.content)
|
120
133
|
end
|
121
134
|
end
|
122
135
|
end
|
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.303.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-27 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.303.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.303.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.8.
|
159
|
+
version: 0.8.7
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.8.
|
166
|
+
version: 0.8.7
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: simplecov
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -291,7 +291,7 @@ licenses:
|
|
291
291
|
- MIT
|
292
292
|
metadata:
|
293
293
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
294
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
294
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.303.0
|
295
295
|
post_install_message:
|
296
296
|
rdoc_options: []
|
297
297
|
require_paths:
|