dependabot-common 0.237.0 → 0.238.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/codecommit.rb +1 -0
- data/lib/dependabot/dependency.rb +137 -27
- data/lib/dependabot/dependency_file.rb +1 -1
- data/lib/dependabot/errors.rb +234 -0
- data/lib/dependabot/file_updaters/base.rb +1 -1
- data/lib/dependabot/git_commit_checker.rb +6 -0
- data/lib/dependabot/git_metadata_fetcher.rb +58 -20
- data/lib/dependabot/git_ref.rb +71 -0
- data/lib/dependabot/pull_request_creator/message_builder.rb +54 -4
- data/lib/dependabot/workspace/base.rb +1 -1
- data/lib/dependabot.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77312fe42bc6241de9c474fa2a1bab0dd3955bff4c2846bc057e52684f1b48bf
|
|
4
|
+
data.tar.gz: 72fbe948d041e0d1e2fd717fa98a2358e32413408ed2d147d6cbd58107d8d5ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f108dbeb6f04a42d5b5b4e30baecad3d376bd82615e067b3ca2696bbdcefd875c55c4dd18da926b597641691e47edb034244d4d170030cbf8055d3c29e9de3cf
|
|
7
|
+
data.tar.gz: e14a429b7dadcd27eccd9e049fb6ebd4a405e8e86229eb683c47d1cb72e911a0da4ae3cab50a3a883000f49ef19e33716cfc2a1fd17181d89858da9644ff80e2
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require "sorbet-runtime"
|
|
4
5
|
require "dependabot/version"
|
|
5
6
|
|
|
6
7
|
module Dependabot
|
|
7
8
|
class Dependency
|
|
8
|
-
|
|
9
|
-
@display_name_builders = {}
|
|
10
|
-
@name_normalisers = {}
|
|
9
|
+
extend T::Sig
|
|
11
10
|
|
|
11
|
+
@production_checks = T.let(
|
|
12
|
+
{},
|
|
13
|
+
T::Hash[String, T.proc.params(arg0: T::Array[T.untyped]).returns(T::Boolean)]
|
|
14
|
+
)
|
|
15
|
+
@display_name_builders = T.let({}, T::Hash[String, T.proc.params(arg0: String).returns(String)])
|
|
16
|
+
@name_normalisers = T.let({}, T::Hash[String, T.proc.params(arg0: String).returns(String)])
|
|
17
|
+
|
|
18
|
+
sig do
|
|
19
|
+
params(package_manager: String).returns(T.proc.params(arg0: T::Array[T.untyped]).returns(T::Boolean))
|
|
20
|
+
end
|
|
12
21
|
def self.production_check_for_package_manager(package_manager)
|
|
13
22
|
production_check = @production_checks[package_manager]
|
|
14
23
|
return production_check if production_check
|
|
@@ -16,62 +25,128 @@ module Dependabot
|
|
|
16
25
|
raise "Unsupported package_manager #{package_manager}"
|
|
17
26
|
end
|
|
18
27
|
|
|
28
|
+
sig do
|
|
29
|
+
params(
|
|
30
|
+
package_manager: String,
|
|
31
|
+
production_check: T.proc.params(arg0: T::Array[T.untyped]).returns(T::Boolean)
|
|
32
|
+
)
|
|
33
|
+
.returns(T.proc.params(arg0: T::Array[T.untyped]).returns(T::Boolean))
|
|
34
|
+
end
|
|
19
35
|
def self.register_production_check(package_manager, production_check)
|
|
20
36
|
@production_checks[package_manager] = production_check
|
|
21
37
|
end
|
|
22
38
|
|
|
39
|
+
sig { params(package_manager: String).returns(T.nilable(T.proc.params(arg0: String).returns(String))) }
|
|
23
40
|
def self.display_name_builder_for_package_manager(package_manager)
|
|
24
41
|
@display_name_builders[package_manager]
|
|
25
42
|
end
|
|
26
43
|
|
|
44
|
+
sig { params(package_manager: String, name_builder: T.proc.params(arg0: String).returns(String)).void }
|
|
27
45
|
def self.register_display_name_builder(package_manager, name_builder)
|
|
28
46
|
@display_name_builders[package_manager] = name_builder
|
|
29
47
|
end
|
|
30
48
|
|
|
49
|
+
sig { params(package_manager: String).returns(T.nilable(T.proc.params(arg0: String).returns(String))) }
|
|
31
50
|
def self.name_normaliser_for_package_manager(package_manager)
|
|
32
51
|
@name_normalisers[package_manager] || ->(name) { name }
|
|
33
52
|
end
|
|
34
53
|
|
|
54
|
+
sig do
|
|
55
|
+
params(
|
|
56
|
+
package_manager: String,
|
|
57
|
+
name_builder: T.proc.params(arg0: String).returns(String)
|
|
58
|
+
).void
|
|
59
|
+
end
|
|
35
60
|
def self.register_name_normaliser(package_manager, name_builder)
|
|
36
61
|
@name_normalisers[package_manager] = name_builder
|
|
37
62
|
end
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
sig { returns(String) }
|
|
65
|
+
attr_reader :name
|
|
66
|
+
|
|
67
|
+
sig { returns(T.nilable(String)) }
|
|
68
|
+
attr_reader :version
|
|
69
|
+
|
|
70
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
71
|
+
attr_reader :requirements
|
|
72
|
+
|
|
73
|
+
sig { returns(String) }
|
|
74
|
+
attr_reader :package_manager
|
|
75
|
+
|
|
76
|
+
sig { returns(T.nilable(String)) }
|
|
77
|
+
attr_reader :previous_version
|
|
42
78
|
|
|
79
|
+
sig { returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
|
|
80
|
+
attr_reader :previous_requirements
|
|
81
|
+
|
|
82
|
+
sig { returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
|
|
83
|
+
attr_reader :subdependency_metadata
|
|
84
|
+
|
|
85
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
86
|
+
attr_reader :metadata
|
|
87
|
+
|
|
88
|
+
sig do
|
|
89
|
+
params(
|
|
90
|
+
name: String,
|
|
91
|
+
requirements: T::Array[T::Hash[String, String]],
|
|
92
|
+
package_manager: String,
|
|
93
|
+
# TODO: Make version a Dependabot::Version everywhere
|
|
94
|
+
version: T.nilable(T.any(String, Dependabot::Version)),
|
|
95
|
+
previous_version: T.nilable(String),
|
|
96
|
+
previous_requirements: T.nilable(T::Array[T::Hash[String, String]]),
|
|
97
|
+
subdependency_metadata: T.nilable(T::Array[T::Hash[String, String]]),
|
|
98
|
+
removed: T::Boolean,
|
|
99
|
+
metadata: T.nilable(T::Hash[String, String])
|
|
100
|
+
).void
|
|
101
|
+
end
|
|
43
102
|
def initialize(name:, requirements:, package_manager:, version: nil,
|
|
44
103
|
previous_version: nil, previous_requirements: nil,
|
|
45
104
|
subdependency_metadata: [], removed: false, metadata: {})
|
|
46
105
|
@name = name
|
|
47
|
-
@version =
|
|
48
|
-
|
|
106
|
+
@version = T.let(
|
|
107
|
+
case version
|
|
108
|
+
when Dependabot::Version then version.to_s
|
|
109
|
+
when String then version
|
|
110
|
+
end,
|
|
111
|
+
T.nilable(String)
|
|
112
|
+
)
|
|
113
|
+
@requirements = T.let(requirements.map { |req| symbolize_keys(req) }, T::Array[T::Hash[Symbol, String]])
|
|
49
114
|
@previous_version = previous_version
|
|
50
|
-
@previous_requirements =
|
|
51
|
-
previous_requirements&.map { |req| symbolize_keys(req) }
|
|
115
|
+
@previous_requirements = T.let(
|
|
116
|
+
previous_requirements&.map { |req| symbolize_keys(req) },
|
|
117
|
+
T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
|
|
118
|
+
)
|
|
52
119
|
@package_manager = package_manager
|
|
53
120
|
unless top_level? || subdependency_metadata == []
|
|
54
|
-
@subdependency_metadata =
|
|
55
|
-
|
|
121
|
+
@subdependency_metadata = T.let(
|
|
122
|
+
subdependency_metadata&.map { |h| symbolize_keys(h) },
|
|
123
|
+
T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
|
|
124
|
+
)
|
|
56
125
|
end
|
|
57
126
|
@removed = removed
|
|
58
|
-
@metadata = symbolize_keys(metadata || {})
|
|
127
|
+
@metadata = T.let(symbolize_keys(metadata || {}), T::Hash[Symbol, T.untyped])
|
|
59
128
|
|
|
60
129
|
check_values
|
|
61
130
|
end
|
|
62
131
|
|
|
132
|
+
sig { returns(T::Boolean) }
|
|
63
133
|
def top_level?
|
|
64
134
|
requirements.any?
|
|
65
135
|
end
|
|
66
136
|
|
|
137
|
+
sig { returns(T::Boolean) }
|
|
67
138
|
def removed?
|
|
68
139
|
@removed
|
|
69
140
|
end
|
|
70
141
|
|
|
142
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
|
71
143
|
def numeric_version
|
|
72
|
-
|
|
144
|
+
return unless version && version_class.correct?(version)
|
|
145
|
+
|
|
146
|
+
@numeric_version ||= T.let(version_class.new(version), T.nilable(Dependabot::Version))
|
|
73
147
|
end
|
|
74
148
|
|
|
149
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
|
75
150
|
def to_h
|
|
76
151
|
{
|
|
77
152
|
"name" => name,
|
|
@@ -85,10 +160,12 @@ module Dependabot
|
|
|
85
160
|
}.compact
|
|
86
161
|
end
|
|
87
162
|
|
|
163
|
+
sig { returns(T::Boolean) }
|
|
88
164
|
def appears_in_lockfile?
|
|
89
|
-
previous_version || (version && previous_requirements.nil?)
|
|
165
|
+
!!(previous_version || (version && previous_requirements.nil?))
|
|
90
166
|
end
|
|
91
167
|
|
|
168
|
+
sig { returns(T::Boolean) }
|
|
92
169
|
def production?
|
|
93
170
|
return subdependency_production_check unless top_level?
|
|
94
171
|
|
|
@@ -99,10 +176,12 @@ module Dependabot
|
|
|
99
176
|
.call(groups)
|
|
100
177
|
end
|
|
101
178
|
|
|
179
|
+
sig { returns(T::Boolean) }
|
|
102
180
|
def subdependency_production_check
|
|
103
181
|
!subdependency_metadata&.all? { |h| h[:production] == false }
|
|
104
182
|
end
|
|
105
183
|
|
|
184
|
+
sig { returns(String) }
|
|
106
185
|
def display_name
|
|
107
186
|
display_name_builder =
|
|
108
187
|
self.class.display_name_builder_for_package_manager(package_manager)
|
|
@@ -111,6 +190,7 @@ module Dependabot
|
|
|
111
190
|
display_name_builder.call(name)
|
|
112
191
|
end
|
|
113
192
|
|
|
193
|
+
sig { returns(T.nilable(String)) }
|
|
114
194
|
def humanized_previous_version
|
|
115
195
|
# If we don't have a previous version, we *may* still be able to figure
|
|
116
196
|
# one out if a ref was provided and has been changed (in which case the
|
|
@@ -119,48 +199,52 @@ module Dependabot
|
|
|
119
199
|
return ref_changed? ? previous_ref : nil
|
|
120
200
|
end
|
|
121
201
|
|
|
122
|
-
if previous_version.match?(/^[0-9a-f]{40}/)
|
|
202
|
+
if T.must(previous_version).match?(/^[0-9a-f]{40}/)
|
|
123
203
|
return previous_ref if ref_changed? && previous_ref
|
|
124
204
|
|
|
125
|
-
"`#{previous_version[0..6]}`"
|
|
205
|
+
"`#{T.must(previous_version)[0..6]}`"
|
|
126
206
|
elsif version == previous_version &&
|
|
127
207
|
package_manager == "docker"
|
|
128
|
-
digest = docker_digest_from_reqs(previous_requirements)
|
|
129
|
-
"`#{digest.split(':').last[0..6]}`"
|
|
208
|
+
digest = docker_digest_from_reqs(T.must(previous_requirements))
|
|
209
|
+
"`#{T.must(T.must(digest).split(':').last)[0..6]}`"
|
|
130
210
|
else
|
|
131
211
|
previous_version
|
|
132
212
|
end
|
|
133
213
|
end
|
|
134
214
|
|
|
215
|
+
sig { returns(T.nilable(String)) }
|
|
135
216
|
def humanized_version
|
|
136
217
|
return if removed?
|
|
137
218
|
|
|
138
|
-
if version.match?(/^[0-9a-f]{40}/)
|
|
219
|
+
if T.must(version).match?(/^[0-9a-f]{40}/)
|
|
139
220
|
return new_ref if ref_changed? && new_ref
|
|
140
221
|
|
|
141
|
-
"`#{version[0..6]}`"
|
|
222
|
+
"`#{T.must(version)[0..6]}`"
|
|
142
223
|
elsif version == previous_version &&
|
|
143
224
|
package_manager == "docker"
|
|
144
225
|
digest = docker_digest_from_reqs(requirements)
|
|
145
|
-
"`#{digest.split(':').last[0..6]}`"
|
|
226
|
+
"`#{T.must(T.must(digest).split(':').last)[0..6]}`"
|
|
146
227
|
else
|
|
147
228
|
version
|
|
148
229
|
end
|
|
149
230
|
end
|
|
150
231
|
|
|
232
|
+
sig { params(requirements: T::Array[T::Hash[Symbol, T.untyped]]).returns(T.nilable(String)) }
|
|
151
233
|
def docker_digest_from_reqs(requirements)
|
|
152
234
|
requirements
|
|
153
235
|
.filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }
|
|
154
236
|
.first
|
|
155
237
|
end
|
|
156
238
|
|
|
239
|
+
sig { returns(T.nilable(String)) }
|
|
157
240
|
def previous_ref
|
|
158
|
-
previous_refs = previous_requirements.filter_map do |r|
|
|
241
|
+
previous_refs = T.must(previous_requirements).filter_map do |r|
|
|
159
242
|
r.dig(:source, "ref") || r.dig(:source, :ref)
|
|
160
243
|
end.uniq
|
|
161
244
|
previous_refs.first if previous_refs.count == 1
|
|
162
245
|
end
|
|
163
246
|
|
|
247
|
+
sig { returns(T.nilable(String)) }
|
|
164
248
|
def new_ref
|
|
165
249
|
new_refs = requirements.filter_map do |r|
|
|
166
250
|
r.dig(:source, "ref") || r.dig(:source, :ref)
|
|
@@ -168,12 +252,14 @@ module Dependabot
|
|
|
168
252
|
new_refs.first if new_refs.count == 1
|
|
169
253
|
end
|
|
170
254
|
|
|
255
|
+
sig { returns(T::Boolean) }
|
|
171
256
|
def ref_changed?
|
|
172
257
|
previous_ref != new_ref
|
|
173
258
|
end
|
|
174
259
|
|
|
175
260
|
# Returns all detected versions of the dependency. Only ecosystems that
|
|
176
261
|
# support this feature will return more than the current version.
|
|
262
|
+
sig { returns(T::Array[T.nilable(String)]) }
|
|
177
263
|
def all_versions
|
|
178
264
|
all_versions = metadata[:all_versions]
|
|
179
265
|
return [version].compact unless all_versions
|
|
@@ -184,34 +270,52 @@ module Dependabot
|
|
|
184
270
|
# This dependency is being indirectly updated by an update to another
|
|
185
271
|
# dependency. We don't need to try and update it ourselves but want to
|
|
186
272
|
# surface it to the user in the PR.
|
|
273
|
+
sig { returns(T.nilable(T::Boolean)) }
|
|
187
274
|
def informational_only?
|
|
188
275
|
metadata[:information_only]
|
|
189
276
|
end
|
|
190
277
|
|
|
278
|
+
sig { params(other: T.anything).returns(T::Boolean) }
|
|
191
279
|
def ==(other)
|
|
192
|
-
|
|
280
|
+
case other
|
|
281
|
+
when Dependency
|
|
282
|
+
to_h == other.to_h
|
|
283
|
+
else
|
|
284
|
+
false
|
|
285
|
+
end
|
|
193
286
|
end
|
|
194
287
|
|
|
288
|
+
sig { returns(Integer) }
|
|
195
289
|
def hash
|
|
196
290
|
to_h.hash
|
|
197
291
|
end
|
|
198
292
|
|
|
293
|
+
sig { params(other: T.anything).returns(T::Boolean) }
|
|
199
294
|
def eql?(other)
|
|
200
295
|
self == other
|
|
201
296
|
end
|
|
202
297
|
|
|
298
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
203
299
|
def specific_requirements
|
|
204
300
|
requirements.select { |r| requirement_class.new(r[:requirement]).specific? }
|
|
205
301
|
end
|
|
206
302
|
|
|
303
|
+
sig { returns(T.class_of(Gem::Requirement)) }
|
|
207
304
|
def requirement_class
|
|
208
305
|
Utils.requirement_class_for_package_manager(package_manager)
|
|
209
306
|
end
|
|
210
307
|
|
|
308
|
+
sig { returns(T.class_of(Dependabot::Version)) }
|
|
211
309
|
def version_class
|
|
212
310
|
Utils.version_class_for_package_manager(package_manager)
|
|
213
311
|
end
|
|
214
312
|
|
|
313
|
+
sig do
|
|
314
|
+
params(
|
|
315
|
+
allowed_types: T.nilable(T::Array[String])
|
|
316
|
+
)
|
|
317
|
+
.returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped]))
|
|
318
|
+
end
|
|
215
319
|
def source_details(allowed_types: nil)
|
|
216
320
|
sources = all_sources.uniq.compact
|
|
217
321
|
sources.select! { |source| allowed_types.include?(source[:type].to_s) } if allowed_types
|
|
@@ -225,6 +329,7 @@ module Dependabot
|
|
|
225
329
|
sources.first
|
|
226
330
|
end
|
|
227
331
|
|
|
332
|
+
sig { returns(T.nilable(String)) }
|
|
228
333
|
def source_type
|
|
229
334
|
details = source_details
|
|
230
335
|
return "default" if details.nil?
|
|
@@ -232,11 +337,12 @@ module Dependabot
|
|
|
232
337
|
details[:type] || details.fetch("type")
|
|
233
338
|
end
|
|
234
339
|
|
|
340
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
235
341
|
def all_sources
|
|
236
342
|
if top_level?
|
|
237
343
|
requirements.map { |requirement| requirement.fetch(:source) }
|
|
238
344
|
elsif subdependency_metadata
|
|
239
|
-
subdependency_metadata.filter_map { |data| data[:source] }
|
|
345
|
+
T.must(subdependency_metadata).filter_map { |data| data[:source] }
|
|
240
346
|
else
|
|
241
347
|
[]
|
|
242
348
|
end
|
|
@@ -244,6 +350,7 @@ module Dependabot
|
|
|
244
350
|
|
|
245
351
|
private
|
|
246
352
|
|
|
353
|
+
sig { void }
|
|
247
354
|
def check_values
|
|
248
355
|
raise ArgumentError, "blank strings must not be provided as versions" if [version, previous_version].any?("")
|
|
249
356
|
|
|
@@ -251,6 +358,7 @@ module Dependabot
|
|
|
251
358
|
check_subdependency_metadata
|
|
252
359
|
end
|
|
253
360
|
|
|
361
|
+
sig { void }
|
|
254
362
|
def check_requirement_fields
|
|
255
363
|
requirement_fields = [requirements, previous_requirements].compact
|
|
256
364
|
unless requirement_fields.all?(Array) &&
|
|
@@ -273,15 +381,17 @@ module Dependabot
|
|
|
273
381
|
raise ArgumentError, "blank strings must not be provided as requirements"
|
|
274
382
|
end
|
|
275
383
|
|
|
384
|
+
sig { void }
|
|
276
385
|
def check_subdependency_metadata
|
|
277
386
|
return unless subdependency_metadata
|
|
278
387
|
|
|
279
388
|
unless subdependency_metadata.is_a?(Array) &&
|
|
280
|
-
subdependency_metadata.all?(Hash)
|
|
389
|
+
T.must(subdependency_metadata).all?(Hash)
|
|
281
390
|
raise ArgumentError, "subdependency_metadata must be an array of hashes"
|
|
282
391
|
end
|
|
283
392
|
end
|
|
284
393
|
|
|
394
|
+
sig { params(hash: T::Hash[String, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
285
395
|
def symbolize_keys(hash)
|
|
286
396
|
hash.keys.to_h { |k| [k.to_sym, hash[k]] }
|
|
287
397
|
end
|
data/lib/dependabot/errors.rb
CHANGED
|
@@ -5,6 +5,215 @@ require "sorbet-runtime"
|
|
|
5
5
|
require "dependabot/utils"
|
|
6
6
|
|
|
7
7
|
module Dependabot
|
|
8
|
+
# rubocop:disable Metrics/MethodLength
|
|
9
|
+
def self.fetcher_error_details(error)
|
|
10
|
+
case error
|
|
11
|
+
when Dependabot::ToolVersionNotSupported
|
|
12
|
+
{
|
|
13
|
+
"error-type": "tool_version_not_supported",
|
|
14
|
+
"error-detail": {
|
|
15
|
+
"tool-name": error.tool_name,
|
|
16
|
+
"detected-version": error.detected_version,
|
|
17
|
+
"supported-versions": error.supported_versions
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
when Dependabot::BranchNotFound
|
|
21
|
+
{
|
|
22
|
+
"error-type": "branch_not_found",
|
|
23
|
+
"error-detail": { "branch-name": error.branch_name }
|
|
24
|
+
}
|
|
25
|
+
when Dependabot::DirectoryNotFound
|
|
26
|
+
{
|
|
27
|
+
"error-type": "directory_not_found",
|
|
28
|
+
"error-detail": { "directory-name": error.directory_name }
|
|
29
|
+
}
|
|
30
|
+
when Dependabot::RepoNotFound
|
|
31
|
+
# This happens if the repo gets removed after a job gets kicked off.
|
|
32
|
+
# This also happens when a configured personal access token is not authz'd to fetch files from the job repo.
|
|
33
|
+
{
|
|
34
|
+
"error-type": "job_repo_not_found",
|
|
35
|
+
"error-detail": { message: error.message }
|
|
36
|
+
}
|
|
37
|
+
when Dependabot::DependencyFileNotParseable
|
|
38
|
+
{
|
|
39
|
+
"error-type": "dependency_file_not_parseable",
|
|
40
|
+
"error-detail": {
|
|
41
|
+
message: error.message,
|
|
42
|
+
"file-path": error.file_path
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
when Dependabot::DependencyFileNotFound
|
|
46
|
+
{
|
|
47
|
+
"error-type": "dependency_file_not_found",
|
|
48
|
+
"error-detail": { "file-path": error.file_path }
|
|
49
|
+
}
|
|
50
|
+
when Dependabot::OutOfDisk
|
|
51
|
+
{
|
|
52
|
+
"error-type": "out_of_disk",
|
|
53
|
+
"error-detail": {}
|
|
54
|
+
}
|
|
55
|
+
when Dependabot::PathDependenciesNotReachable
|
|
56
|
+
{
|
|
57
|
+
"error-type": "path_dependencies_not_reachable",
|
|
58
|
+
"error-detail": { dependencies: error.dependencies }
|
|
59
|
+
}
|
|
60
|
+
when Octokit::Unauthorized
|
|
61
|
+
{ "error-type": "octokit_unauthorized" }
|
|
62
|
+
when Octokit::ServerError
|
|
63
|
+
# If we get a 500 from GitHub there's very little we can do about it,
|
|
64
|
+
# and responsibility for fixing it is on them, not us. As a result we
|
|
65
|
+
# quietly log these as errors
|
|
66
|
+
{ "error-type": "server_error" }
|
|
67
|
+
when *Octokit::RATE_LIMITED_ERRORS
|
|
68
|
+
# If we get a rate-limited error we let dependabot-api handle the
|
|
69
|
+
# retry by re-enqueing the update job after the reset
|
|
70
|
+
{
|
|
71
|
+
"error-type": "octokit_rate_limited",
|
|
72
|
+
"error-detail": {
|
|
73
|
+
"rate-limit-reset": error.response_headers["X-RateLimit-Reset"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.parser_error_details(error)
|
|
80
|
+
case error
|
|
81
|
+
when Dependabot::DependencyFileNotEvaluatable
|
|
82
|
+
{
|
|
83
|
+
"error-type": "dependency_file_not_evaluatable",
|
|
84
|
+
"error-detail": { message: error.message }
|
|
85
|
+
}
|
|
86
|
+
when Dependabot::DependencyFileNotResolvable
|
|
87
|
+
{
|
|
88
|
+
"error-type": "dependency_file_not_resolvable",
|
|
89
|
+
"error-detail": { message: error.message }
|
|
90
|
+
}
|
|
91
|
+
when Dependabot::BranchNotFound
|
|
92
|
+
{
|
|
93
|
+
"error-type": "branch_not_found",
|
|
94
|
+
"error-detail": { "branch-name": error.branch_name }
|
|
95
|
+
}
|
|
96
|
+
when Dependabot::DependencyFileNotParseable
|
|
97
|
+
{
|
|
98
|
+
"error-type": "dependency_file_not_parseable",
|
|
99
|
+
"error-detail": {
|
|
100
|
+
message: error.message,
|
|
101
|
+
"file-path": error.file_path
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
when Dependabot::DependencyFileNotFound
|
|
105
|
+
{
|
|
106
|
+
"error-type": "dependency_file_not_found",
|
|
107
|
+
"error-detail": { "file-path": error.file_path }
|
|
108
|
+
}
|
|
109
|
+
when Dependabot::PathDependenciesNotReachable
|
|
110
|
+
{
|
|
111
|
+
"error-type": "path_dependencies_not_reachable",
|
|
112
|
+
"error-detail": { dependencies: error.dependencies }
|
|
113
|
+
}
|
|
114
|
+
when Dependabot::PrivateSourceAuthenticationFailure
|
|
115
|
+
{
|
|
116
|
+
"error-type": "private_source_authentication_failure",
|
|
117
|
+
"error-detail": { source: error.source }
|
|
118
|
+
}
|
|
119
|
+
when Dependabot::GitDependenciesNotReachable
|
|
120
|
+
{
|
|
121
|
+
"error-type": "git_dependencies_not_reachable",
|
|
122
|
+
"error-detail": { "dependency-urls": error.dependency_urls }
|
|
123
|
+
}
|
|
124
|
+
when Dependabot::NotImplemented
|
|
125
|
+
{
|
|
126
|
+
"error-type": "not_implemented",
|
|
127
|
+
"error-detail": {
|
|
128
|
+
message: error.message
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
when Octokit::ServerError
|
|
132
|
+
# If we get a 500 from GitHub there's very little we can do about it,
|
|
133
|
+
# and responsibility for fixing it is on them, not us. As a result we
|
|
134
|
+
# quietly log these as errors
|
|
135
|
+
{ "error-type": "server_error" }
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.updater_error_details(error)
|
|
140
|
+
case error
|
|
141
|
+
when Dependabot::DependencyFileNotResolvable
|
|
142
|
+
{
|
|
143
|
+
"error-type": "dependency_file_not_resolvable",
|
|
144
|
+
"error-detail": { message: error.message }
|
|
145
|
+
}
|
|
146
|
+
when Dependabot::DependencyFileNotEvaluatable
|
|
147
|
+
{
|
|
148
|
+
"error-type": "dependency_file_not_evaluatable",
|
|
149
|
+
"error-detail": { message: error.message }
|
|
150
|
+
}
|
|
151
|
+
when Dependabot::GitDependenciesNotReachable
|
|
152
|
+
{
|
|
153
|
+
"error-type": "git_dependencies_not_reachable",
|
|
154
|
+
"error-detail": { "dependency-urls": error.dependency_urls }
|
|
155
|
+
}
|
|
156
|
+
when Dependabot::MisconfiguredTooling
|
|
157
|
+
{
|
|
158
|
+
"error-type": "misconfigured_tooling",
|
|
159
|
+
"error-detail": { "tool-name": error.tool_name, message: error.tool_message }
|
|
160
|
+
}
|
|
161
|
+
when Dependabot::GitDependencyReferenceNotFound
|
|
162
|
+
{
|
|
163
|
+
"error-type": "git_dependency_reference_not_found",
|
|
164
|
+
"error-detail": { dependency: error.dependency }
|
|
165
|
+
}
|
|
166
|
+
when Dependabot::PrivateSourceAuthenticationFailure
|
|
167
|
+
{
|
|
168
|
+
"error-type": "private_source_authentication_failure",
|
|
169
|
+
"error-detail": { source: error.source }
|
|
170
|
+
}
|
|
171
|
+
when Dependabot::PrivateSourceTimedOut
|
|
172
|
+
{
|
|
173
|
+
"error-type": "private_source_timed_out",
|
|
174
|
+
"error-detail": { source: error.source }
|
|
175
|
+
}
|
|
176
|
+
when Dependabot::PrivateSourceCertificateFailure
|
|
177
|
+
{
|
|
178
|
+
"error-type": "private_source_certificate_failure",
|
|
179
|
+
"error-detail": { source: error.source }
|
|
180
|
+
}
|
|
181
|
+
when Dependabot::MissingEnvironmentVariable
|
|
182
|
+
{
|
|
183
|
+
"error-type": "missing_environment_variable",
|
|
184
|
+
"error-detail": {
|
|
185
|
+
"environment-variable": error.environment_variable
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
when Dependabot::GoModulePathMismatch
|
|
189
|
+
{
|
|
190
|
+
"error-type": "go_module_path_mismatch",
|
|
191
|
+
"error-detail": {
|
|
192
|
+
"declared-path": error.declared_path,
|
|
193
|
+
"discovered-path": error.discovered_path,
|
|
194
|
+
"go-mod": error.go_mod
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
when Dependabot::NotImplemented
|
|
198
|
+
{
|
|
199
|
+
"error-type": "not_implemented",
|
|
200
|
+
"error-detail": {
|
|
201
|
+
message: error.message
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
when *Octokit::RATE_LIMITED_ERRORS
|
|
205
|
+
# If we get a rate-limited error we let dependabot-api handle the
|
|
206
|
+
# retry by re-enqueing the update job after the reset
|
|
207
|
+
{
|
|
208
|
+
"error-type": "octokit_rate_limited",
|
|
209
|
+
"error-detail": {
|
|
210
|
+
"rate-limit-reset": error.response_headers["X-RateLimit-Reset"]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
# rubocop:enable Metrics/MethodLength
|
|
216
|
+
|
|
8
217
|
class DependabotError < StandardError
|
|
9
218
|
extend T::Sig
|
|
10
219
|
|
|
@@ -109,6 +318,31 @@ module Dependabot
|
|
|
109
318
|
# File level errors #
|
|
110
319
|
#####################
|
|
111
320
|
|
|
321
|
+
class MisconfiguredTooling < DependabotError
|
|
322
|
+
extend T::Sig
|
|
323
|
+
|
|
324
|
+
sig { returns(String) }
|
|
325
|
+
attr_reader :tool_name
|
|
326
|
+
|
|
327
|
+
sig { returns(String) }
|
|
328
|
+
attr_reader :tool_message
|
|
329
|
+
|
|
330
|
+
sig do
|
|
331
|
+
params(
|
|
332
|
+
tool_name: String,
|
|
333
|
+
tool_message: String
|
|
334
|
+
).void
|
|
335
|
+
end
|
|
336
|
+
def initialize(tool_name, tool_message)
|
|
337
|
+
@tool_name = tool_name
|
|
338
|
+
@tool_message = tool_message
|
|
339
|
+
|
|
340
|
+
msg = "Dependabot detected that #{tool_name} is misconfigured in this repository. " \
|
|
341
|
+
"Running `#{tool_name.downcase}` results in the following error: #{tool_message}"
|
|
342
|
+
super(msg)
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
112
346
|
class ToolVersionNotSupported < DependabotError
|
|
113
347
|
extend T::Sig
|
|
114
348
|
|
|
@@ -73,7 +73,7 @@ module Dependabot
|
|
|
73
73
|
|
|
74
74
|
sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
|
|
75
75
|
def requirement_changed?(file, dependency)
|
|
76
|
-
changed_requirements = dependency.requirements - dependency.previous_requirements
|
|
76
|
+
changed_requirements = dependency.requirements - T.must(dependency.previous_requirements)
|
|
77
77
|
|
|
78
78
|
changed_requirements.any? { |f| f[:file] == file.name }
|
|
79
79
|
end
|
|
@@ -153,6 +153,12 @@ module Dependabot
|
|
|
153
153
|
@local_tag_for_pinned_sha = most_specific_version_tag_for_sha(ref) if pinned_ref_looks_like_commit_sha?
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
def version_for_pinned_sha
|
|
157
|
+
return unless local_tag_for_pinned_sha && version_class.correct?(local_tag_for_pinned_sha)
|
|
158
|
+
|
|
159
|
+
version_class.new(local_tag_for_pinned_sha)
|
|
160
|
+
end
|
|
161
|
+
|
|
156
162
|
def git_repo_reachable?
|
|
157
163
|
local_upload_pack
|
|
158
164
|
true
|
|
@@ -1,49 +1,73 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "excon"
|
|
5
5
|
require "open3"
|
|
6
|
+
require "sorbet-runtime"
|
|
7
|
+
|
|
6
8
|
require "dependabot/errors"
|
|
9
|
+
require "dependabot/git_ref"
|
|
7
10
|
|
|
8
11
|
module Dependabot
|
|
9
12
|
class GitMetadataFetcher
|
|
13
|
+
extend T::Sig
|
|
14
|
+
|
|
10
15
|
KNOWN_HOSTS = /github\.com|bitbucket\.org|gitlab.com/i
|
|
11
16
|
|
|
17
|
+
sig do
|
|
18
|
+
params(
|
|
19
|
+
url: String,
|
|
20
|
+
credentials: T::Array[T::Hash[String, String]]
|
|
21
|
+
)
|
|
22
|
+
.void
|
|
23
|
+
end
|
|
12
24
|
def initialize(url:, credentials:)
|
|
13
25
|
@url = url
|
|
14
26
|
@credentials = credentials
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
sig { returns(T.nilable(String)) }
|
|
17
30
|
def upload_pack
|
|
18
|
-
@upload_pack ||= fetch_upload_pack_for(url)
|
|
31
|
+
@upload_pack ||= T.let(fetch_upload_pack_for(url), T.nilable(String))
|
|
19
32
|
rescue Octokit::ClientError
|
|
20
33
|
raise Dependabot::GitDependenciesNotReachable, [url]
|
|
21
34
|
end
|
|
22
35
|
|
|
36
|
+
sig { returns(T::Array[GitRef]) }
|
|
23
37
|
def tags
|
|
24
38
|
return [] unless upload_pack
|
|
25
39
|
|
|
26
|
-
@tags ||=
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
@tags ||= T.let(
|
|
41
|
+
tags_for_upload_pack.map do |ref|
|
|
42
|
+
GitRef.new(
|
|
43
|
+
name: ref.name,
|
|
44
|
+
tag_sha: ref.ref_sha,
|
|
45
|
+
commit_sha: ref.commit_sha
|
|
46
|
+
)
|
|
47
|
+
end,
|
|
48
|
+
T.nilable(T::Array[GitRef])
|
|
49
|
+
)
|
|
33
50
|
end
|
|
34
51
|
|
|
52
|
+
sig { returns(T::Array[GitRef]) }
|
|
35
53
|
def tags_for_upload_pack
|
|
36
|
-
@tags_for_upload_pack ||=
|
|
54
|
+
@tags_for_upload_pack ||= T.let(
|
|
55
|
+
refs_for_upload_pack.select { |ref| ref.ref_type == RefType::Tag },
|
|
56
|
+
T.nilable(T::Array[GitRef])
|
|
57
|
+
)
|
|
37
58
|
end
|
|
38
59
|
|
|
60
|
+
sig { returns(T::Array[GitRef]) }
|
|
39
61
|
def refs_for_upload_pack
|
|
40
|
-
@refs_for_upload_pack ||= parse_refs_for_upload_pack
|
|
62
|
+
@refs_for_upload_pack ||= T.let(parse_refs_for_upload_pack, T.nilable(T::Array[GitRef]))
|
|
41
63
|
end
|
|
42
64
|
|
|
65
|
+
sig { returns(T::Array[String]) }
|
|
43
66
|
def ref_names
|
|
44
67
|
refs_for_upload_pack.map(&:name)
|
|
45
68
|
end
|
|
46
69
|
|
|
70
|
+
sig { params(ref: String).returns(T.nilable(String)) }
|
|
47
71
|
def head_commit_for_ref(ref)
|
|
48
72
|
if ref == "HEAD"
|
|
49
73
|
# Remove the opening clause of the upload pack as this isn't always
|
|
@@ -51,8 +75,8 @@ module Dependabot
|
|
|
51
75
|
# causes problems for our `sha_for_update_pack_line` logic. The format
|
|
52
76
|
# of this opening clause is documented at
|
|
53
77
|
# https://git-scm.com/docs/http-protocol#_smart_server_response
|
|
54
|
-
line = upload_pack.gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
|
|
55
|
-
|
|
78
|
+
line = T.must(upload_pack).gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
|
|
79
|
+
.lines.find { |l| l.include?(" HEAD") }
|
|
56
80
|
return sha_for_update_pack_line(line) if line
|
|
57
81
|
end
|
|
58
82
|
|
|
@@ -61,6 +85,7 @@ module Dependabot
|
|
|
61
85
|
&.commit_sha
|
|
62
86
|
end
|
|
63
87
|
|
|
88
|
+
sig { params(ref: String).returns(T.nilable(String)) }
|
|
64
89
|
def head_commit_for_ref_sha(ref)
|
|
65
90
|
refs_for_upload_pack
|
|
66
91
|
.find { |r| r.ref_sha == ref }
|
|
@@ -69,8 +94,13 @@ module Dependabot
|
|
|
69
94
|
|
|
70
95
|
private
|
|
71
96
|
|
|
72
|
-
|
|
97
|
+
sig { returns(String) }
|
|
98
|
+
attr_reader :url
|
|
99
|
+
|
|
100
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
|
101
|
+
attr_reader :credentials
|
|
73
102
|
|
|
103
|
+
sig { params(uri: String).returns(String) }
|
|
74
104
|
def fetch_upload_pack_for(uri)
|
|
75
105
|
response = fetch_raw_upload_pack_for(uri)
|
|
76
106
|
return response.body if response.status == 200
|
|
@@ -97,6 +127,7 @@ module Dependabot
|
|
|
97
127
|
raise Dependabot::GitDependenciesNotReachable, [uri]
|
|
98
128
|
end
|
|
99
129
|
|
|
130
|
+
sig { params(uri: String).returns(Excon::Response) }
|
|
100
131
|
def fetch_raw_upload_pack_for(uri)
|
|
101
132
|
url = service_pack_uri(uri)
|
|
102
133
|
url = url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
|
|
@@ -107,6 +138,7 @@ module Dependabot
|
|
|
107
138
|
)
|
|
108
139
|
end
|
|
109
140
|
|
|
141
|
+
sig { params(uri: String).returns(T.untyped) }
|
|
110
142
|
def fetch_raw_upload_pack_with_git_for(uri)
|
|
111
143
|
service_pack_uri = uri
|
|
112
144
|
service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
|
|
@@ -129,11 +161,12 @@ module Dependabot
|
|
|
129
161
|
end
|
|
130
162
|
end
|
|
131
163
|
|
|
164
|
+
sig { returns(T::Array[GitRef]) }
|
|
132
165
|
def parse_refs_for_upload_pack
|
|
133
166
|
peeled_lines = []
|
|
134
167
|
|
|
135
|
-
result = upload_pack.lines.each_with_object({}) do |line, res|
|
|
136
|
-
full_ref_name = line.split.last
|
|
168
|
+
result = T.must(upload_pack).lines.each_with_object({}) do |line, res|
|
|
169
|
+
full_ref_name = T.must(line.split.last)
|
|
137
170
|
next unless full_ref_name.start_with?("refs/tags", "refs/heads")
|
|
138
171
|
|
|
139
172
|
(peeled_lines << line) && next if line.strip.end_with?("^{}")
|
|
@@ -141,10 +174,10 @@ module Dependabot
|
|
|
141
174
|
ref_name = full_ref_name.sub(%r{^refs/(tags|heads)/}, "").strip
|
|
142
175
|
sha = sha_for_update_pack_line(line)
|
|
143
176
|
|
|
144
|
-
res[ref_name] =
|
|
177
|
+
res[ref_name] = GitRef.new(
|
|
145
178
|
name: ref_name,
|
|
146
179
|
ref_sha: sha,
|
|
147
|
-
ref_type: full_ref_name.start_with?("refs/tags") ?
|
|
180
|
+
ref_type: full_ref_name.start_with?("refs/tags") ? RefType::Tag : RefType::Head,
|
|
148
181
|
commit_sha: sha
|
|
149
182
|
)
|
|
150
183
|
end
|
|
@@ -162,6 +195,7 @@ module Dependabot
|
|
|
162
195
|
result.values
|
|
163
196
|
end
|
|
164
197
|
|
|
198
|
+
sig { params(uri: String).returns(String) }
|
|
165
199
|
def service_pack_uri(uri)
|
|
166
200
|
service_pack_uri = uri_with_auth(uri)
|
|
167
201
|
service_pack_uri = service_pack_uri.gsub(%r{/$}, "")
|
|
@@ -169,6 +203,7 @@ module Dependabot
|
|
|
169
203
|
service_pack_uri + "/info/refs?service=git-upload-pack"
|
|
170
204
|
end
|
|
171
205
|
|
|
206
|
+
sig { params(uri: String).returns(T::Boolean) }
|
|
172
207
|
def skip_git_suffix(uri)
|
|
173
208
|
# TODO: Unlike the other providers (GitHub, GitLab, BitBucket), as of 2023-01-18 Azure DevOps does not support the
|
|
174
209
|
# ".git" suffix. It will return a 404.
|
|
@@ -188,6 +223,7 @@ module Dependabot
|
|
|
188
223
|
|
|
189
224
|
# Add in username and password if present in credentials.
|
|
190
225
|
# Credentials are never present for production Dependabot.
|
|
226
|
+
sig { params(uri: String).returns(String) }
|
|
191
227
|
def uri_with_auth(uri)
|
|
192
228
|
uri = SharedHelpers.scp_to_standard(uri)
|
|
193
229
|
uri = URI(uri)
|
|
@@ -196,7 +232,7 @@ module Dependabot
|
|
|
196
232
|
|
|
197
233
|
uri.scheme = "https" if uri.scheme != "http"
|
|
198
234
|
|
|
199
|
-
if !uri.password && cred
|
|
235
|
+
if !uri.password && cred && cred.fetch("username", nil) && cred.fetch("password", nil)
|
|
200
236
|
# URI doesn't have authentication details, but we have credentials
|
|
201
237
|
uri.user = URI.encode_www_form_component(cred["username"])
|
|
202
238
|
uri.password = URI.encode_www_form_component(cred["password"])
|
|
@@ -205,10 +241,12 @@ module Dependabot
|
|
|
205
241
|
uri.to_s
|
|
206
242
|
end
|
|
207
243
|
|
|
244
|
+
sig { params(line: String).returns(String) }
|
|
208
245
|
def sha_for_update_pack_line(line)
|
|
209
|
-
line.split.first.chars.last(40).join
|
|
246
|
+
T.must(line.split.first).chars.last(40).join
|
|
210
247
|
end
|
|
211
248
|
|
|
249
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
212
250
|
def excon_defaults
|
|
213
251
|
# Some git hosts are slow when returning a large number of tags
|
|
214
252
|
SharedHelpers.excon_defaults(read_timeout: 20)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
class GitRef
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { returns(String) }
|
|
11
|
+
attr_accessor :name
|
|
12
|
+
|
|
13
|
+
sig { returns(String) }
|
|
14
|
+
attr_accessor :commit_sha
|
|
15
|
+
|
|
16
|
+
sig { returns(T.nilable(String)) }
|
|
17
|
+
attr_reader :tag_sha
|
|
18
|
+
|
|
19
|
+
sig { returns(T.nilable(String)) }
|
|
20
|
+
attr_reader :ref_sha
|
|
21
|
+
|
|
22
|
+
sig { returns(T.nilable(RefType)) }
|
|
23
|
+
attr_reader :ref_type
|
|
24
|
+
|
|
25
|
+
sig do
|
|
26
|
+
params(
|
|
27
|
+
name: String,
|
|
28
|
+
commit_sha: String,
|
|
29
|
+
tag_sha: T.nilable(String),
|
|
30
|
+
ref_sha: T.nilable(String),
|
|
31
|
+
ref_type: T.nilable(RefType)
|
|
32
|
+
)
|
|
33
|
+
.void
|
|
34
|
+
end
|
|
35
|
+
def initialize(name:, commit_sha:, tag_sha: nil, ref_sha: nil, ref_type: nil)
|
|
36
|
+
@name = name
|
|
37
|
+
@commit_sha = commit_sha
|
|
38
|
+
@ref_sha = ref_sha
|
|
39
|
+
@tag_sha = tag_sha
|
|
40
|
+
@ref_type = ref_type
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
sig { params(other: BasicObject).returns(T::Boolean) }
|
|
44
|
+
def ==(other)
|
|
45
|
+
case other
|
|
46
|
+
when GitRef
|
|
47
|
+
to_h == other.to_h
|
|
48
|
+
else
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
sig { returns(T::Hash[Symbol, T.nilable(String)]) }
|
|
54
|
+
def to_h
|
|
55
|
+
{
|
|
56
|
+
name: name,
|
|
57
|
+
commit_sha: commit_sha,
|
|
58
|
+
tag_sha: tag_sha,
|
|
59
|
+
ref_sha: ref_sha,
|
|
60
|
+
ref_type: ref_type
|
|
61
|
+
}.compact
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class RefType < T::Enum
|
|
66
|
+
enums do
|
|
67
|
+
Tag = new
|
|
68
|
+
Head = new
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -85,7 +85,7 @@ module Dependabot
|
|
|
85
85
|
msg = (msg[0..trunc_length] + tr_msg)
|
|
86
86
|
end
|
|
87
87
|
# if we used a custom encoding for calculating length, then we need to force back to UTF-8
|
|
88
|
-
msg.
|
|
88
|
+
msg = msg.encode("utf-8", "binary", invalid: :replace, undef: :replace) unless pr_message_encoding.nil?
|
|
89
89
|
msg
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -162,7 +162,13 @@ module Dependabot
|
|
|
162
162
|
|
|
163
163
|
def group_pr_name
|
|
164
164
|
updates = dependencies.map(&:name).uniq.count
|
|
165
|
-
|
|
165
|
+
|
|
166
|
+
if source&.directories
|
|
167
|
+
"bump the #{dependency_group.name} across #{source.directories.count} directories " \
|
|
168
|
+
"with #{updates} update#{'s' if updates > 1}"
|
|
169
|
+
else
|
|
170
|
+
"bump the #{dependency_group.name} group#{pr_name_directory} with #{updates} update#{'s' if updates > 1}"
|
|
171
|
+
end
|
|
166
172
|
end
|
|
167
173
|
|
|
168
174
|
def pr_name_prefix
|
|
@@ -260,6 +266,8 @@ module Dependabot
|
|
|
260
266
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
261
267
|
# rubocop:disable Metrics/AbcSize
|
|
262
268
|
def version_commit_message_intro
|
|
269
|
+
return multi_directory_group_intro if dependency_group && source&.directories
|
|
270
|
+
|
|
263
271
|
return group_intro if dependency_group
|
|
264
272
|
|
|
265
273
|
return multidependency_property_intro if dependencies.count > 1 && updating_a_property?
|
|
@@ -346,6 +354,42 @@ module Dependabot
|
|
|
346
354
|
msg
|
|
347
355
|
end
|
|
348
356
|
|
|
357
|
+
def multi_directory_group_intro
|
|
358
|
+
msg = ""
|
|
359
|
+
|
|
360
|
+
source.directories.each do |directory|
|
|
361
|
+
dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
|
|
362
|
+
next unless dependencies_in_directory.any?
|
|
363
|
+
|
|
364
|
+
update_count = dependencies_in_directory.map(&:name).uniq.count
|
|
365
|
+
|
|
366
|
+
msg += "Bumps the #{dependency_group.name} " \
|
|
367
|
+
"with #{update_count} update#{update_count > 1 ? 's' : ''} in the #{directory} directory:"
|
|
368
|
+
|
|
369
|
+
msg += if update_count >= 5
|
|
370
|
+
header = %w(Package From To)
|
|
371
|
+
rows = dependencies_in_directory.map do |dep|
|
|
372
|
+
[
|
|
373
|
+
dependency_link(dep),
|
|
374
|
+
"`#{dep.humanized_previous_version}`",
|
|
375
|
+
"`#{dep.humanized_version}`"
|
|
376
|
+
]
|
|
377
|
+
end
|
|
378
|
+
"\n\n#{table([header] + rows)}"
|
|
379
|
+
elsif update_count > 1
|
|
380
|
+
dependency_links_in_directory = dependency_links_for_directory(directory)
|
|
381
|
+
" #{dependency_links_in_directory[0..-2].join(', ')} and #{dependency_links_in_directory[-1]}."
|
|
382
|
+
else
|
|
383
|
+
dependency_links_in_directory = dependency_links_for_directory(directory)
|
|
384
|
+
" #{dependency_links_in_directory.first}."
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
msg += "\n"
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
msg
|
|
391
|
+
end
|
|
392
|
+
|
|
349
393
|
def group_intro
|
|
350
394
|
update_count = dependencies.map(&:name).uniq.count
|
|
351
395
|
|
|
@@ -427,6 +471,12 @@ module Dependabot
|
|
|
427
471
|
@dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
|
|
428
472
|
end
|
|
429
473
|
|
|
474
|
+
def dependency_links_for_directory(directory)
|
|
475
|
+
dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
|
|
476
|
+
uniq_deps = dependencies_in_directory.each_with_object({}) { |dep, memo| memo[dep.name] ||= dep }.values
|
|
477
|
+
@dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
|
|
478
|
+
end
|
|
479
|
+
|
|
430
480
|
def dependency_link(dependency)
|
|
431
481
|
if source_url(dependency)
|
|
432
482
|
"[#{dependency.display_name}](#{source_url(dependency)})"
|
|
@@ -483,8 +533,8 @@ module Dependabot
|
|
|
483
533
|
"| #{row.join(' | ')} |"
|
|
484
534
|
end
|
|
485
535
|
|
|
486
|
-
def metadata_cascades
|
|
487
|
-
return metadata_cascades_for_dep(dependencies.first) if dependencies.one?
|
|
536
|
+
def metadata_cascades # rubocop:disable Metrics/PerceivedComplexity
|
|
537
|
+
return metadata_cascades_for_dep(dependencies.first) if dependencies.one? && !dependency_group
|
|
488
538
|
|
|
489
539
|
dependencies.map do |dep|
|
|
490
540
|
msg = if dep.removed?
|
data/lib/dependabot.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-common
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.238.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-12-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-codecommit
|
|
@@ -154,6 +154,20 @@ dependencies:
|
|
|
154
154
|
- - '='
|
|
155
155
|
- !ruby/object:Gem::Version
|
|
156
156
|
version: 4.19.0
|
|
157
|
+
- !ruby/object:Gem::Dependency
|
|
158
|
+
name: json
|
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
|
160
|
+
requirements:
|
|
161
|
+
- - "<"
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
version: '2.7'
|
|
164
|
+
type: :runtime
|
|
165
|
+
prerelease: false
|
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
167
|
+
requirements:
|
|
168
|
+
- - "<"
|
|
169
|
+
- !ruby/object:Gem::Version
|
|
170
|
+
version: '2.7'
|
|
157
171
|
- !ruby/object:Gem::Dependency
|
|
158
172
|
name: nokogiri
|
|
159
173
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -491,6 +505,7 @@ files:
|
|
|
491
505
|
- lib/dependabot/file_updaters/vendor_updater.rb
|
|
492
506
|
- lib/dependabot/git_commit_checker.rb
|
|
493
507
|
- lib/dependabot/git_metadata_fetcher.rb
|
|
508
|
+
- lib/dependabot/git_ref.rb
|
|
494
509
|
- lib/dependabot/logger.rb
|
|
495
510
|
- lib/dependabot/metadata_finders.rb
|
|
496
511
|
- lib/dependabot/metadata_finders/README.md
|
|
@@ -542,7 +557,7 @@ licenses:
|
|
|
542
557
|
- Nonstandard
|
|
543
558
|
metadata:
|
|
544
559
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
545
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
560
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.238.0
|
|
546
561
|
post_install_message:
|
|
547
562
|
rdoc_options: []
|
|
548
563
|
require_paths:
|