dependabot-common 0.237.0 → 0.238.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|