dependabot-common 0.387.0 → 0.389.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/clients/github_release.rb +61 -0
- data/lib/dependabot/dependency.rb +167 -36
- data/lib/dependabot/dependency_graphers/generic.rb +2 -2
- data/lib/dependabot/dependency_requirement.rb +81 -17
- data/lib/dependabot/error_details.rb +51 -0
- data/lib/dependabot/errors.rb +10 -6
- data/lib/dependabot/file_fetchers/base.rb +2 -2
- data/lib/dependabot/git_commit_checker.rb +33 -11
- data/lib/dependabot/git_cooldown_date_resolver.rb +13 -5
- data/lib/dependabot/metadata_finders/base/release_finder.rb +111 -77
- data/lib/dependabot/package/release_cooldown_options.rb +1 -1
- data/lib/dependabot/pull_request_creator/message_builder.rb +5 -2
- data/lib/dependabot/version.rb +9 -0
- data/lib/dependabot.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9781413c952e486c9417d63fc70301e7ba10d4283555831df79a6e9b5f2067d
|
|
4
|
+
data.tar.gz: f54e1d3bda0a1126fefa4246c4a7f0f30951bf5995711b4b376fdd152a81352b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48591da2ac8712a8f2db6f9a8cd8c7c5debe954fa1470d7e6a625f18d5249e668cf5d48a338813c4c9ffe080d43628bf168d1dc81e463fa8753fc4edb819568e
|
|
7
|
+
data.tar.gz: 276ee23ea7c643fb0daac14664c83dea4c4fd4070bda5d616278d94eb01cad4722bd32d022667e5f9affe802dcb986c1131dbcb4c5a5eebef359854f0b83d136
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sawyer"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
require "time"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Clients
|
|
10
|
+
# Typed view over the release fields returned by Octokit.
|
|
11
|
+
class GithubRelease < T::ImmutableStruct
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
const :id, T.nilable(Integer)
|
|
15
|
+
const :name, T.nilable(String)
|
|
16
|
+
const :tag_name, String
|
|
17
|
+
const :body, T.nilable(String)
|
|
18
|
+
const :html_url, T.nilable(String)
|
|
19
|
+
const :prerelease, T::Boolean
|
|
20
|
+
const :published_at, T.nilable(Time)
|
|
21
|
+
|
|
22
|
+
sig { params(resource: Sawyer::Resource).returns(T.nilable(GithubRelease)) }
|
|
23
|
+
def self.from_resource(resource)
|
|
24
|
+
tag_name = T.cast(resource[:tag_name], Object)
|
|
25
|
+
return unless tag_name.is_a?(String)
|
|
26
|
+
|
|
27
|
+
id = T.cast(resource[:id], Object)
|
|
28
|
+
prerelease = T.cast(resource[:prerelease], Object)
|
|
29
|
+
|
|
30
|
+
new(
|
|
31
|
+
id: id.is_a?(Integer) ? id : nil,
|
|
32
|
+
name: string_value(resource, :name),
|
|
33
|
+
tag_name: tag_name,
|
|
34
|
+
body: string_value(resource, :body),
|
|
35
|
+
html_url: string_value(resource, :html_url),
|
|
36
|
+
prerelease: prerelease == true,
|
|
37
|
+
published_at: time_value(resource, :published_at)
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(String)) }
|
|
42
|
+
def self.string_value(resource, key)
|
|
43
|
+
value = T.cast(resource[key], Object)
|
|
44
|
+
value.is_a?(String) ? value : nil
|
|
45
|
+
end
|
|
46
|
+
private_class_method :string_value
|
|
47
|
+
|
|
48
|
+
sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(Time)) }
|
|
49
|
+
def self.time_value(resource, key)
|
|
50
|
+
value = T.cast(resource[key], Object)
|
|
51
|
+
return value if value.is_a?(Time)
|
|
52
|
+
return unless value.is_a?(String)
|
|
53
|
+
|
|
54
|
+
Time.parse(value)
|
|
55
|
+
rescue ArgumentError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
private_class_method :time_value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -9,9 +9,17 @@ module Dependabot
|
|
|
9
9
|
class Dependency
|
|
10
10
|
extend T::Sig
|
|
11
11
|
|
|
12
|
+
Metadata = T.type_alias { T::Hash[Symbol, Object] }
|
|
13
|
+
ProductionCheck = T.type_alias do
|
|
14
|
+
T.proc.params(groups: T::Array[String]).returns(T::Boolean)
|
|
15
|
+
end
|
|
16
|
+
RequirementInput = T.type_alias { DependencyRequirement::Input }
|
|
17
|
+
ObjectHash = T.type_alias { DependencyRequirement::ObjectHash }
|
|
18
|
+
RequirementSource = T.type_alias { DependencyRequirement::Source }
|
|
19
|
+
|
|
12
20
|
@production_checks = T.let(
|
|
13
21
|
{},
|
|
14
|
-
T::Hash[String,
|
|
22
|
+
T::Hash[String, ProductionCheck]
|
|
15
23
|
)
|
|
16
24
|
@display_name_builders = T.let({}, T::Hash[String, T.proc.params(arg0: String).returns(String)])
|
|
17
25
|
@name_normalisers = T.let({}, T::Hash[String, T.proc.params(arg0: String).returns(String)])
|
|
@@ -19,9 +27,13 @@ module Dependabot
|
|
|
19
27
|
{},
|
|
20
28
|
T::Hash[String, T.proc.params(arg0: Dependency).returns(T.nilable(String))]
|
|
21
29
|
)
|
|
30
|
+
IDENTITY_NAME_NORMALISER = T.let(
|
|
31
|
+
->(name) { name },
|
|
32
|
+
T.proc.params(name: String).returns(String)
|
|
33
|
+
)
|
|
22
34
|
|
|
23
35
|
sig do
|
|
24
|
-
params(package_manager: String).returns(
|
|
36
|
+
params(package_manager: String).returns(ProductionCheck)
|
|
25
37
|
end
|
|
26
38
|
def self.production_check_for_package_manager(package_manager)
|
|
27
39
|
production_check = @production_checks[package_manager]
|
|
@@ -33,9 +45,9 @@ module Dependabot
|
|
|
33
45
|
sig do
|
|
34
46
|
params(
|
|
35
47
|
package_manager: String,
|
|
36
|
-
production_check:
|
|
48
|
+
production_check: ProductionCheck
|
|
37
49
|
)
|
|
38
|
-
.returns(
|
|
50
|
+
.returns(ProductionCheck)
|
|
39
51
|
end
|
|
40
52
|
def self.register_production_check(package_manager, production_check)
|
|
41
53
|
@production_checks[package_manager] = production_check
|
|
@@ -53,7 +65,7 @@ module Dependabot
|
|
|
53
65
|
|
|
54
66
|
sig { params(package_manager: String).returns(T.nilable(T.proc.params(arg0: String).returns(String))) }
|
|
55
67
|
def self.name_normaliser_for_package_manager(package_manager)
|
|
56
|
-
@name_normalisers[package_manager] ||
|
|
68
|
+
@name_normalisers[package_manager] || IDENTITY_NAME_NORMALISER
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
sig do
|
|
@@ -106,10 +118,10 @@ module Dependabot
|
|
|
106
118
|
sig { returns(T.nilable(String)) }
|
|
107
119
|
attr_accessor :directory
|
|
108
120
|
|
|
109
|
-
sig { returns(T.nilable(T::Array[
|
|
121
|
+
sig { returns(T.nilable(T::Array[Metadata])) }
|
|
110
122
|
attr_reader :subdependency_metadata
|
|
111
123
|
|
|
112
|
-
sig { returns(
|
|
124
|
+
sig { returns(Metadata) }
|
|
113
125
|
attr_reader :metadata
|
|
114
126
|
|
|
115
127
|
# Attribution metadata for group membership tracking
|
|
@@ -129,16 +141,16 @@ module Dependabot
|
|
|
129
141
|
sig do
|
|
130
142
|
params(
|
|
131
143
|
name: String,
|
|
132
|
-
requirements: T::Array[
|
|
144
|
+
requirements: T::Array[RequirementInput],
|
|
133
145
|
package_manager: String,
|
|
134
146
|
# TODO: Make version a Dependabot::Version everywhere
|
|
135
147
|
version: T.nilable(T.any(String, Dependabot::Version)),
|
|
136
148
|
previous_version: T.nilable(String),
|
|
137
|
-
previous_requirements: T.nilable(T::Array[
|
|
149
|
+
previous_requirements: T.nilable(T::Array[RequirementInput]),
|
|
138
150
|
directory: T.nilable(String),
|
|
139
|
-
subdependency_metadata: T.nilable(T::Array[
|
|
151
|
+
subdependency_metadata: T.nilable(T::Array[ObjectHash]),
|
|
140
152
|
removed: T::Boolean,
|
|
141
|
-
metadata: T.nilable(
|
|
153
|
+
metadata: T.nilable(ObjectHash)
|
|
142
154
|
).void
|
|
143
155
|
end
|
|
144
156
|
def initialize(
|
|
@@ -174,14 +186,16 @@ module Dependabot
|
|
|
174
186
|
)
|
|
175
187
|
@package_manager = package_manager
|
|
176
188
|
@directory = directory
|
|
189
|
+
@subdependency_metadata = T.let(nil, T.nilable(T::Array[Metadata]))
|
|
177
190
|
unless top_level? || subdependency_metadata == []
|
|
178
191
|
@subdependency_metadata = T.let(
|
|
179
192
|
subdependency_metadata&.map { |h| symbolize_keys(h) },
|
|
180
|
-
T.nilable(T::Array[
|
|
193
|
+
T.nilable(T::Array[Metadata])
|
|
181
194
|
)
|
|
182
195
|
end
|
|
183
196
|
@removed = removed
|
|
184
|
-
@metadata = T.let(symbolize_keys(metadata || {}),
|
|
197
|
+
@metadata = T.let(symbolize_keys(metadata || {}), Metadata)
|
|
198
|
+
@numeric_version = T.let(nil, T.nilable(Dependabot::Version))
|
|
185
199
|
check_values
|
|
186
200
|
end
|
|
187
201
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
@@ -203,7 +217,7 @@ module Dependabot
|
|
|
203
217
|
@numeric_version ||= T.let(version_class.new(T.must(version)), T.nilable(Dependabot::Version))
|
|
204
218
|
end
|
|
205
219
|
|
|
206
|
-
sig { returns(T::Hash[String,
|
|
220
|
+
sig { returns(T::Hash[String, Object]) }
|
|
207
221
|
def to_h
|
|
208
222
|
{
|
|
209
223
|
"name" => name,
|
|
@@ -227,7 +241,12 @@ module Dependabot
|
|
|
227
241
|
def production?
|
|
228
242
|
return subdependency_production_check unless top_level?
|
|
229
243
|
|
|
230
|
-
groups = requirements.flat_map
|
|
244
|
+
groups = requirements.flat_map do |requirement|
|
|
245
|
+
requirement_groups = requirement.groups
|
|
246
|
+
raise TypeError, "groups must be an array of strings or symbols" unless requirement_groups
|
|
247
|
+
|
|
248
|
+
requirement_groups.map(&:to_s)
|
|
249
|
+
end
|
|
231
250
|
|
|
232
251
|
self.class
|
|
233
252
|
.production_check_for_package_manager(package_manager)
|
|
@@ -277,7 +296,10 @@ module Dependabot
|
|
|
277
296
|
sig { params(requirements: T::Array[Dependabot::DependencyRequirement]).returns(T.nilable(String)) }
|
|
278
297
|
def docker_digest_from_reqs(requirements)
|
|
279
298
|
requirements
|
|
280
|
-
.filter_map
|
|
299
|
+
.filter_map do |requirement|
|
|
300
|
+
source = requirement.source
|
|
301
|
+
optional_source_string(source, "digest") if source.is_a?(Hash)
|
|
302
|
+
end
|
|
281
303
|
.first
|
|
282
304
|
end
|
|
283
305
|
|
|
@@ -286,7 +308,8 @@ module Dependabot
|
|
|
286
308
|
return nil if previous_requirements.nil?
|
|
287
309
|
|
|
288
310
|
previous_refs = T.must(previous_requirements).filter_map do |r|
|
|
289
|
-
|
|
311
|
+
source = r.source
|
|
312
|
+
optional_source_string(source, "ref") if source.is_a?(Hash)
|
|
290
313
|
end.uniq
|
|
291
314
|
previous_refs.first if previous_refs.one?
|
|
292
315
|
end
|
|
@@ -294,7 +317,8 @@ module Dependabot
|
|
|
294
317
|
sig { returns(T.nilable(String)) }
|
|
295
318
|
def new_ref
|
|
296
319
|
new_refs = requirements.filter_map do |r|
|
|
297
|
-
|
|
320
|
+
source = r.source
|
|
321
|
+
optional_source_string(source, "ref") if source.is_a?(Hash)
|
|
298
322
|
end.uniq
|
|
299
323
|
new_refs.first if new_refs.one?
|
|
300
324
|
end
|
|
@@ -308,10 +332,10 @@ module Dependabot
|
|
|
308
332
|
# support this feature will return more than the current version.
|
|
309
333
|
sig { returns(T::Array[T.nilable(String)]) }
|
|
310
334
|
def all_versions
|
|
311
|
-
|
|
312
|
-
return [version].compact unless
|
|
335
|
+
versions = metadata_dependencies(:all_versions)
|
|
336
|
+
return [version].compact unless versions
|
|
313
337
|
|
|
314
|
-
|
|
338
|
+
versions.filter_map(&:version)
|
|
315
339
|
end
|
|
316
340
|
|
|
317
341
|
# This dependency is being indirectly updated by an update to another
|
|
@@ -319,10 +343,63 @@ module Dependabot
|
|
|
319
343
|
# surface it to the user in the PR.
|
|
320
344
|
sig { returns(T.nilable(T::Boolean)) }
|
|
321
345
|
def informational_only?
|
|
322
|
-
|
|
346
|
+
metadata_boolean(:information_only)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
sig { params(key: Symbol).returns(T.nilable(String)) }
|
|
350
|
+
def metadata_string(key)
|
|
351
|
+
value = metadata[key]
|
|
352
|
+
return if value.nil?
|
|
353
|
+
return value if value.is_a?(String)
|
|
354
|
+
|
|
355
|
+
raise TypeError, "#{key} metadata must be a string or nil"
|
|
323
356
|
end
|
|
324
357
|
|
|
325
|
-
sig { params(
|
|
358
|
+
sig { params(key: Symbol).returns(T.nilable(T::Array[String])) }
|
|
359
|
+
def metadata_string_array(key)
|
|
360
|
+
value = metadata[key]
|
|
361
|
+
return if value.nil?
|
|
362
|
+
raise TypeError, "#{key} metadata must be an array of strings or nil" unless value.is_a?(Array)
|
|
363
|
+
|
|
364
|
+
value.map do |raw_entry|
|
|
365
|
+
entry = T.cast(raw_entry, Object)
|
|
366
|
+
raise TypeError, "#{key} metadata must be an array of strings or nil" unless entry.is_a?(String)
|
|
367
|
+
|
|
368
|
+
entry
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
sig { params(key: Symbol).returns(T.nilable(T::Array[Dependency])) }
|
|
373
|
+
def metadata_dependencies(key)
|
|
374
|
+
value = metadata_array(key)
|
|
375
|
+
return unless value
|
|
376
|
+
|
|
377
|
+
value.map do |raw_dependency|
|
|
378
|
+
raise TypeError, "#{key} metadata must be an array of dependencies" unless raw_dependency.is_a?(Dependency)
|
|
379
|
+
|
|
380
|
+
raw_dependency
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
sig { params(key: Symbol).returns(T.nilable(T::Array[Object])) }
|
|
385
|
+
def metadata_array(key)
|
|
386
|
+
value = metadata[key]
|
|
387
|
+
return if value.nil?
|
|
388
|
+
raise TypeError, "#{key} metadata must be an array" unless value.is_a?(Array)
|
|
389
|
+
|
|
390
|
+
value.map { |entry| T.cast(entry, Object) }
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
sig { params(key: Symbol).returns(T.nilable(T::Boolean)) }
|
|
394
|
+
def metadata_boolean(key)
|
|
395
|
+
value = metadata[key]
|
|
396
|
+
return if value.nil?
|
|
397
|
+
return value if value == true || value == false
|
|
398
|
+
|
|
399
|
+
raise TypeError, "#{key} metadata must be a boolean or nil"
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
sig { params(other: Object).returns(T::Boolean) }
|
|
326
403
|
def ==(other)
|
|
327
404
|
case other
|
|
328
405
|
when Dependency
|
|
@@ -337,7 +414,7 @@ module Dependabot
|
|
|
337
414
|
to_h.hash
|
|
338
415
|
end
|
|
339
416
|
|
|
340
|
-
sig { params(other:
|
|
417
|
+
sig { params(other: Object).returns(T::Boolean) }
|
|
341
418
|
def eql?(other)
|
|
342
419
|
self == other
|
|
343
420
|
end
|
|
@@ -361,19 +438,27 @@ module Dependabot
|
|
|
361
438
|
params(
|
|
362
439
|
allowed_types: T.nilable(T::Array[String])
|
|
363
440
|
)
|
|
364
|
-
.returns(T.nilable(
|
|
441
|
+
.returns(T.nilable(ObjectHash))
|
|
365
442
|
end
|
|
366
443
|
def source_details(allowed_types: nil)
|
|
367
444
|
sources = all_sources.uniq.compact
|
|
368
|
-
sources.
|
|
445
|
+
source_hashes = sources.map do |source|
|
|
446
|
+
raise TypeError, "source details must be a hash with string or symbol keys" unless source.is_a?(Hash)
|
|
447
|
+
|
|
448
|
+
source
|
|
449
|
+
end
|
|
450
|
+
if allowed_types
|
|
451
|
+
source_hashes.select! { |source| allowed_types.include?(optional_source_string(source, "type").to_s) }
|
|
452
|
+
end
|
|
369
453
|
|
|
370
454
|
git = allowed_types == ["git"]
|
|
371
455
|
|
|
372
|
-
if (git &&
|
|
373
|
-
|
|
456
|
+
if (git && source_hashes.map { |source| optional_source_string(source, "url") }.uniq.count > 1) ||
|
|
457
|
+
(!git && source_hashes.count > 1)
|
|
458
|
+
raise "Multiple sources! #{source_hashes.join(', ')}"
|
|
374
459
|
end
|
|
375
460
|
|
|
376
|
-
|
|
461
|
+
source_hashes.first
|
|
377
462
|
end
|
|
378
463
|
|
|
379
464
|
sig { returns(T.nilable(String)) }
|
|
@@ -381,15 +466,26 @@ module Dependabot
|
|
|
381
466
|
details = source_details
|
|
382
467
|
return "default" if details.nil?
|
|
383
468
|
|
|
384
|
-
details
|
|
469
|
+
required_source_string(details, "type")
|
|
385
470
|
end
|
|
386
471
|
|
|
387
|
-
sig
|
|
472
|
+
sig do
|
|
473
|
+
params(
|
|
474
|
+
key: String,
|
|
475
|
+
allowed_types: T.nilable(T::Array[String])
|
|
476
|
+
).returns(T.nilable(String))
|
|
477
|
+
end
|
|
478
|
+
def source_string(key, allowed_types: nil)
|
|
479
|
+
details = source_details(allowed_types: allowed_types)
|
|
480
|
+
optional_source_string(details, key) if details
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
sig { returns(T::Array[T.nilable(RequirementSource)]) }
|
|
388
484
|
def all_sources
|
|
389
485
|
if top_level?
|
|
390
|
-
requirements.map
|
|
486
|
+
requirements.map(&:source)
|
|
391
487
|
elsif subdependency_metadata
|
|
392
|
-
T.must(subdependency_metadata).filter_map { |data| data
|
|
488
|
+
T.must(subdependency_metadata).filter_map { |data| source_from_metadata(data) }
|
|
393
489
|
else
|
|
394
490
|
[]
|
|
395
491
|
end
|
|
@@ -461,7 +557,7 @@ module Dependabot
|
|
|
461
557
|
"#{optional_keys.join(', ')}."
|
|
462
558
|
end
|
|
463
559
|
|
|
464
|
-
return if requirement_fields.flatten.none? { |r| r
|
|
560
|
+
return if requirement_fields.flatten.none? { |r| r.requirement == "" }
|
|
465
561
|
|
|
466
562
|
raise ArgumentError, "blank strings must not be provided as requirements"
|
|
467
563
|
end
|
|
@@ -476,9 +572,44 @@ module Dependabot
|
|
|
476
572
|
end
|
|
477
573
|
end
|
|
478
574
|
|
|
479
|
-
sig { params(hash:
|
|
575
|
+
sig { params(hash: ObjectHash).returns(Metadata) }
|
|
480
576
|
def symbolize_keys(hash)
|
|
481
577
|
hash.keys.to_h { |k| [k.to_sym, hash[k]] }
|
|
482
578
|
end
|
|
579
|
+
|
|
580
|
+
sig { params(metadata: Metadata).returns(T.nilable(RequirementSource)) }
|
|
581
|
+
def source_from_metadata(metadata)
|
|
582
|
+
raw_source = metadata[:source]
|
|
583
|
+
return if raw_source.nil?
|
|
584
|
+
return raw_source if raw_source.is_a?(String)
|
|
585
|
+
unless raw_source.is_a?(Hash)
|
|
586
|
+
raise TypeError, "source must be a string or hash with string or symbol keys, or nil"
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
raw_source.each_key do |raw_key|
|
|
590
|
+
key = T.cast(raw_key, Object)
|
|
591
|
+
next if key.is_a?(String) || key.is_a?(Symbol)
|
|
592
|
+
|
|
593
|
+
raise TypeError, "source must be a string or hash with string or symbol keys, or nil"
|
|
594
|
+
end
|
|
595
|
+
raw_source
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
sig { params(source: ObjectHash, key: String).returns(T.nilable(String)) }
|
|
599
|
+
def optional_source_string(source, key)
|
|
600
|
+
value = source[key.to_sym] || source[key]
|
|
601
|
+
return if value.nil?
|
|
602
|
+
return value if value.is_a?(String)
|
|
603
|
+
|
|
604
|
+
raise TypeError, "source #{key} must be a string or nil"
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
sig { params(source: ObjectHash, key: String).returns(String) }
|
|
608
|
+
def required_source_string(source, key)
|
|
609
|
+
raw_value = source.key?(key.to_sym) ? source[key.to_sym] : source.fetch(key)
|
|
610
|
+
return raw_value if raw_value.is_a?(String)
|
|
611
|
+
|
|
612
|
+
raise TypeError, "source #{key} must be a string"
|
|
613
|
+
end
|
|
483
614
|
end
|
|
484
615
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -32,7 +32,7 @@ module Dependabot
|
|
|
32
32
|
# metadata, but in most cases this will be empty.
|
|
33
33
|
sig { override.params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
|
|
34
34
|
def fetch_subdependencies(dependency)
|
|
35
|
-
dependency.
|
|
35
|
+
dependency.metadata_string_array(:depends_on) || []
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# TODO: Delegate this to ecosystem-specific base classes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -33,18 +33,24 @@ module Dependabot
|
|
|
33
33
|
extend T::Sig
|
|
34
34
|
extend T::Generic
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
Group = T.type_alias { T.any(String, Symbol) }
|
|
37
|
+
ObjectHash = T.type_alias { T::Hash[T.any(Symbol, String), Object] }
|
|
38
|
+
Requirement = T.type_alias { T.any(String, Symbol) }
|
|
39
|
+
Source = T.type_alias { T.any(String, ObjectHash) }
|
|
40
|
+
Input = T.type_alias { T.any(DependencyRequirement, ObjectHash) }
|
|
41
|
+
|
|
40
42
|
K = type_member { { fixed: Symbol } }
|
|
43
|
+
# Hash-style access remains dynamic until ecosystem callers migrate to the
|
|
44
|
+
# typed readers. Keeping that compatibility here avoids a flag-day change.
|
|
45
|
+
# rubocop:disable Sorbet/ForbidTUntyped
|
|
41
46
|
V = type_member { { fixed: T.untyped } }
|
|
42
47
|
Elem = type_member { { fixed: [Symbol, T.untyped] } }
|
|
48
|
+
# rubocop:enable Sorbet/ForbidTUntyped
|
|
43
49
|
|
|
44
50
|
# Builds a DependencyRequirement from a requirement hash, symbolising
|
|
45
51
|
# top-level keys. Accepts both plain hashes and existing
|
|
46
52
|
# DependencyRequirement instances and always returns a new instance.
|
|
47
|
-
sig { params(hash:
|
|
53
|
+
sig { params(hash: Input).returns(DependencyRequirement) }
|
|
48
54
|
def self.create(hash)
|
|
49
55
|
requirement = new
|
|
50
56
|
requirement.replace(hash.keys.to_h { |k| [k.to_sym, hash[k]] })
|
|
@@ -52,16 +58,21 @@ module Dependabot
|
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
# The version constraint string, e.g. ">= 1.0, < 2.0". Nil when the
|
|
55
|
-
# dependency is pinned by a lockfile rather than a manifest constraint
|
|
56
|
-
|
|
61
|
+
# dependency is pinned by a lockfile rather than a manifest constraint,
|
|
62
|
+
# or :unfixable when no valid updated requirement can be generated.
|
|
63
|
+
sig { returns(T.nilable(Requirement)) }
|
|
57
64
|
def requirement
|
|
58
|
-
self[:requirement]
|
|
65
|
+
value = T.cast(self[:requirement], T.nilable(Object))
|
|
66
|
+
return if value.nil?
|
|
67
|
+
return value if value.is_a?(String) || value == :unfixable
|
|
68
|
+
|
|
69
|
+
raise TypeError, "requirement must be a string, :unfixable, or nil"
|
|
59
70
|
end
|
|
60
71
|
|
|
61
72
|
# The manifest file this requirement was declared in, e.g. "Gemfile".
|
|
62
73
|
sig { returns(T.nilable(String)) }
|
|
63
74
|
def file
|
|
64
|
-
|
|
75
|
+
optional_string(:file)
|
|
65
76
|
end
|
|
66
77
|
|
|
67
78
|
# The dependency groups this requirement belongs to, e.g. ["dev"] or
|
|
@@ -69,26 +80,79 @@ module Dependabot
|
|
|
69
80
|
# Nilable because some requirement entries are constructed with
|
|
70
81
|
# groups: nil, and the reader must reflect that to stay a drop-in for
|
|
71
82
|
# the underlying hash access under sorbet-runtime.
|
|
72
|
-
sig { returns(T.nilable(T::Array[
|
|
83
|
+
sig { returns(T.nilable(T::Array[Group])) }
|
|
73
84
|
def groups
|
|
74
|
-
self[:groups]
|
|
85
|
+
value = T.cast(self[:groups], T.nilable(Object))
|
|
86
|
+
return if value.nil?
|
|
87
|
+
raise TypeError, "groups must be an array of strings or symbols, or nil" unless value.is_a?(Array)
|
|
88
|
+
|
|
89
|
+
value.each do |raw_group|
|
|
90
|
+
group = T.cast(raw_group, Object)
|
|
91
|
+
next if group.is_a?(String) || group.is_a?(Symbol)
|
|
92
|
+
|
|
93
|
+
raise TypeError, "groups must be an array of strings or symbols, or nil"
|
|
94
|
+
end
|
|
95
|
+
value
|
|
75
96
|
end
|
|
76
97
|
|
|
77
98
|
# The source details for the dependency, e.g.
|
|
78
99
|
# { type: "git", url: "https://github.com/..." }. Keys may be symbols
|
|
79
100
|
# or strings depending on whether the requirement was built by a file
|
|
80
101
|
# parser or deserialised from a job definition.
|
|
81
|
-
sig { returns(T.nilable(
|
|
102
|
+
sig { returns(T.nilable(Source)) }
|
|
82
103
|
def source
|
|
83
|
-
self[:source]
|
|
104
|
+
value = T.cast(self[:source], T.nilable(Object))
|
|
105
|
+
return if value.nil?
|
|
106
|
+
return value if value.is_a?(String)
|
|
107
|
+
|
|
108
|
+
object_hash(value, :source)
|
|
84
109
|
end
|
|
85
110
|
|
|
86
111
|
# Optional ecosystem-specific metadata about the requirement, e.g.
|
|
87
112
|
# { property_name: "rails.version" }.
|
|
88
|
-
sig { returns(T.nilable(
|
|
113
|
+
sig { returns(T.nilable(ObjectHash)) }
|
|
89
114
|
def metadata
|
|
90
|
-
|
|
115
|
+
optional_object_hash(:metadata)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
sig { params(key: Symbol).returns(T.nilable(String)) }
|
|
121
|
+
def optional_string(key)
|
|
122
|
+
value = T.cast(self[key], T.nilable(Object))
|
|
123
|
+
return if value.nil?
|
|
124
|
+
return value if value.is_a?(String)
|
|
125
|
+
|
|
126
|
+
raise TypeError, "#{key} must be a string or nil"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
sig { params(key: Symbol).returns(T.nilable(ObjectHash)) }
|
|
130
|
+
def optional_object_hash(key)
|
|
131
|
+
value = T.cast(self[key], T.nilable(Object))
|
|
132
|
+
return if value.nil?
|
|
133
|
+
|
|
134
|
+
object_hash(value, key)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
sig { params(value: Object, key: Symbol).returns(ObjectHash) }
|
|
138
|
+
def object_hash(value, key)
|
|
139
|
+
message = object_hash_message(key)
|
|
140
|
+
raise TypeError, "#{key} must be #{message}" unless value.is_a?(Hash)
|
|
141
|
+
|
|
142
|
+
value.each_key do |raw_nested_key|
|
|
143
|
+
nested_key = T.cast(raw_nested_key, Object)
|
|
144
|
+
next if nested_key.is_a?(String) || nested_key.is_a?(Symbol)
|
|
145
|
+
|
|
146
|
+
raise TypeError, "#{key} must be #{message}"
|
|
147
|
+
end
|
|
148
|
+
value
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
sig { params(key: Symbol).returns(String) }
|
|
152
|
+
def object_hash_message(key)
|
|
153
|
+
return "a string or hash with string or symbol keys, or nil" if key == :source
|
|
154
|
+
|
|
155
|
+
"a hash with string or symbol keys, or nil"
|
|
91
156
|
end
|
|
92
|
-
# rubocop:enable Sorbet/ForbidTUntyped
|
|
93
157
|
end
|
|
94
158
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
class ErrorDetails < T::ImmutableStruct
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
DetailHash = T.type_alias { T::Hash[T.any(String, Symbol), T.anything] }
|
|
11
|
+
Detail = T.type_alias { T.any(String, DetailHash) }
|
|
12
|
+
|
|
13
|
+
const :error_type, String
|
|
14
|
+
const :error_detail, T.nilable(Detail)
|
|
15
|
+
|
|
16
|
+
sig { params(hash: T::Hash[Symbol, T.anything]).returns(ErrorDetails) }
|
|
17
|
+
def self.from_hash(hash)
|
|
18
|
+
raw_type = T.cast(hash[:"error-type"], Object)
|
|
19
|
+
raise TypeError, "error-type must be a string" unless raw_type.is_a?(String)
|
|
20
|
+
|
|
21
|
+
new(
|
|
22
|
+
error_type: raw_type,
|
|
23
|
+
error_detail: parse_detail(T.cast(hash[:"error-detail"], T.nilable(Object)))
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
|
28
|
+
def to_h
|
|
29
|
+
result = T.let({ "error-type": error_type }, T::Hash[Symbol, Object])
|
|
30
|
+
result[:"error-detail"] = error_detail if error_detail
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sig { params(value: T.nilable(Object)).returns(T.nilable(Detail)) }
|
|
35
|
+
def self.parse_detail(value)
|
|
36
|
+
return if value.nil?
|
|
37
|
+
return value if value.is_a?(String)
|
|
38
|
+
raise TypeError, "error-detail must be a string or hash" unless value.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
result = T.let({}, DetailHash)
|
|
41
|
+
value.each do |raw_key, raw_value|
|
|
42
|
+
key = T.cast(raw_key, Object)
|
|
43
|
+
raise TypeError, "error-detail keys must be strings or symbols" unless key.is_a?(String) || key.is_a?(Symbol)
|
|
44
|
+
|
|
45
|
+
result[key] = T.cast(raw_value, Object)
|
|
46
|
+
end
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
private_class_method :parse_detail
|
|
50
|
+
end
|
|
51
|
+
end
|