dependabot-common 0.245.0 → 0.247.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/bitbucket.rb +113 -5
  3. data/lib/dependabot/clients/codecommit.rb +107 -12
  4. data/lib/dependabot/clients/github_with_retries.rb +61 -19
  5. data/lib/dependabot/clients/gitlab_with_retries.rb +60 -7
  6. data/lib/dependabot/dependency.rb +1 -1
  7. data/lib/dependabot/errors.rb +20 -2
  8. data/lib/dependabot/file_fetchers/base.rb +8 -19
  9. data/lib/dependabot/file_updaters/base.rb +2 -0
  10. data/lib/dependabot/git_commit_checker.rb +3 -2
  11. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +1 -1
  12. data/lib/dependabot/metadata_finders/base/commits_finder.rb +1 -1
  13. data/lib/dependabot/metadata_finders/base/release_finder.rb +1 -1
  14. data/lib/dependabot/pull_request_creator/azure.rb +80 -9
  15. data/lib/dependabot/pull_request_creator/bitbucket.rb +73 -9
  16. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +1 -1
  17. data/lib/dependabot/pull_request_creator/codecommit.rb +96 -25
  18. data/lib/dependabot/pull_request_creator/github.rb +162 -49
  19. data/lib/dependabot/pull_request_creator/gitlab.rb +109 -21
  20. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +13 -4
  21. data/lib/dependabot/pull_request_creator/message_builder.rb +246 -89
  22. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +11 -9
  23. data/lib/dependabot/pull_request_creator.rb +32 -27
  24. data/lib/dependabot/pull_request_updater/azure.rb +75 -11
  25. data/lib/dependabot/pull_request_updater/github.rb +89 -28
  26. data/lib/dependabot/pull_request_updater/gitlab.rb +61 -12
  27. data/lib/dependabot/pull_request_updater.rb +1 -1
  28. data/lib/dependabot/registry_client.rb +2 -2
  29. data/lib/dependabot/requirements_update_strategy.rb +13 -0
  30. data/lib/dependabot/update_checkers/base.rb +123 -32
  31. data/lib/dependabot/update_checkers/version_filters.rb +15 -5
  32. data/lib/dependabot/version.rb +6 -43
  33. data/lib/dependabot.rb +1 -1
  34. metadata +18 -3
@@ -1,7 +1,9 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "pathname"
5
+ require "sorbet-runtime"
6
+
5
7
  require "dependabot/clients/github_with_retries"
6
8
  require "dependabot/clients/gitlab_with_retries"
7
9
  require "dependabot/dependency_group"
@@ -15,19 +17,72 @@ module Dependabot
15
17
  class PullRequestCreator
16
18
  # MessageBuilder builds PR message for a dependency update
17
19
  class MessageBuilder
20
+ extend T::Sig
21
+
18
22
  require_relative "message_builder/metadata_presenter"
19
23
  require_relative "message_builder/issue_linker"
20
24
  require_relative "message_builder/link_and_mention_sanitizer"
21
25
  require_relative "pr_name_prefixer"
22
26
 
23
- attr_reader :source, :dependencies, :files, :credentials,
24
- :pr_message_header, :pr_message_footer,
25
- :commit_message_options, :vulnerabilities_fixed,
26
- :github_redirection_service, :dependency_group, :pr_message_max_length,
27
- :pr_message_encoding, :ignore_conditions
27
+ sig { returns(Dependabot::Source) }
28
+ attr_reader :source
29
+
30
+ sig { returns(T::Array[Dependabot::Dependency]) }
31
+ attr_reader :dependencies
32
+
33
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
34
+ attr_reader :files
35
+
36
+ sig { returns(T::Array[Dependabot::Credential]) }
37
+ attr_reader :credentials
38
+
39
+ sig { returns(T.nilable(String)) }
40
+ attr_reader :pr_message_header
41
+
42
+ sig { returns(T.nilable(String)) }
43
+ attr_reader :pr_message_footer
44
+
45
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
46
+ attr_reader :commit_message_options
47
+
48
+ sig { returns(T::Hash[String, T.untyped]) }
49
+ attr_reader :vulnerabilities_fixed
50
+
51
+ sig { returns(T.nilable(String)) }
52
+ attr_reader :github_redirection_service
53
+
54
+ sig { returns(T.nilable(Dependabot::DependencyGroup)) }
55
+ attr_reader :dependency_group
56
+
57
+ sig { returns(T.nilable(Integer)) }
58
+ attr_reader :pr_message_max_length
59
+
60
+ sig { returns(T.nilable(Encoding)) }
61
+ attr_reader :pr_message_encoding
62
+
63
+ sig { returns(T::Array[T::Hash[String, String]]) }
64
+ attr_reader :ignore_conditions
28
65
 
29
66
  TRUNCATED_MSG = "...\n\n_Description has been truncated_"
30
67
 
68
+ sig do
69
+ params(
70
+ source: Dependabot::Source,
71
+ dependencies: T::Array[Dependabot::Dependency],
72
+ files: T::Array[Dependabot::DependencyFile],
73
+ credentials: T::Array[Dependabot::Credential],
74
+ pr_message_header: T.nilable(String),
75
+ pr_message_footer: T.nilable(String),
76
+ commit_message_options: T.nilable(T::Hash[Symbol, T.untyped]),
77
+ vulnerabilities_fixed: T::Hash[String, T.untyped],
78
+ github_redirection_service: T.nilable(String),
79
+ dependency_group: T.nilable(Dependabot::DependencyGroup),
80
+ pr_message_max_length: T.nilable(Integer),
81
+ pr_message_encoding: T.nilable(Encoding),
82
+ ignore_conditions: T::Array[T::Hash[String, String]]
83
+ )
84
+ .void
85
+ end
31
86
  def initialize(source:, dependencies:, files:, credentials:,
32
87
  pr_message_header: nil, pr_message_footer: nil,
33
88
  commit_message_options: {}, vulnerabilities_fixed: {},
@@ -48,16 +103,20 @@ module Dependabot
48
103
  @ignore_conditions = ignore_conditions
49
104
  end
50
105
 
106
+ sig { params(pr_message_max_length: Integer).returns(Integer) }
51
107
  attr_writer :pr_message_max_length
52
108
 
109
+ sig { params(pr_message_encoding: Encoding).returns(Encoding) }
53
110
  attr_writer :pr_message_encoding
54
111
 
112
+ sig { returns(String) }
55
113
  def pr_name
56
114
  name = dependency_group ? group_pr_name : solo_pr_name
57
- name[0] = name[0].capitalize if pr_name_prefixer.capitalize_first_word?
115
+ name[0] = T.must(name[0]).capitalize if pr_name_prefixer.capitalize_first_word?
58
116
  "#{pr_name_prefix}#{name}"
59
117
  end
60
118
 
119
+ sig { returns(String) }
61
120
  def pr_message
62
121
  msg = "#{suffixed_pr_message_header}" \
63
122
  "#{commit_message_intro}" \
@@ -73,35 +132,42 @@ module Dependabot
73
132
 
74
133
  # Truncate PR message as determined by the pr_message_max_length and pr_message_encoding instance variables
75
134
  # The encoding is used when calculating length, all messages are returned as ruby UTF_8 encoded string
135
+ sig { params(msg: String).returns(String) }
76
136
  def truncate_pr_message(msg)
77
137
  return msg if pr_message_max_length.nil?
78
138
 
79
139
  msg = msg.dup
80
- msg = msg.force_encoding(pr_message_encoding) unless pr_message_encoding.nil?
140
+ msg = msg.force_encoding(T.must(pr_message_encoding)) unless pr_message_encoding.nil?
81
141
 
82
- if msg.length > pr_message_max_length
83
- tr_msg = pr_message_encoding.nil? ? TRUNCATED_MSG : (+TRUNCATED_MSG).dup.force_encoding(pr_message_encoding)
84
- trunc_length = pr_message_max_length - tr_msg.length
85
- msg = (msg[0..trunc_length] + tr_msg)
142
+ if msg.length > T.must(pr_message_max_length)
143
+ tr_msg = if pr_message_encoding.nil?
144
+ TRUNCATED_MSG
145
+ else
146
+ (+TRUNCATED_MSG).dup.force_encoding(T.must(pr_message_encoding))
147
+ end
148
+ trunc_length = T.must(pr_message_max_length) - tr_msg.length
149
+ msg = (T.must(msg[0..trunc_length]) + tr_msg)
86
150
  end
87
151
  # if we used a custom encoding for calculating length, then we need to force back to UTF-8
88
152
  msg = msg.encode("utf-8", "binary", invalid: :replace, undef: :replace) unless pr_message_encoding.nil?
89
153
  msg
90
154
  end
91
155
 
156
+ sig { returns(String) }
92
157
  def commit_message
93
158
  message = commit_subject + "\n\n"
94
159
  message += commit_message_intro
95
160
  message += metadata_links
96
- message += "\n\n" + message_trailers if message_trailers
161
+ message += "\n\n" + T.must(message_trailers) if message_trailers
97
162
  message
98
163
  rescue StandardError => e
99
164
  Dependabot.logger.error("Error while generating commit message: #{e.message}")
100
165
  message = commit_subject
101
- message += "\n\n" + message_trailers if message_trailers
166
+ message += "\n\n" + T.must(message_trailers) if message_trailers
102
167
  message
103
168
  end
104
169
 
170
+ sig { returns(Dependabot::PullRequestCreator::Message) }
105
171
  def message
106
172
  Dependabot::PullRequestCreator::Message.new(
107
173
  pr_name: pr_name,
@@ -112,65 +178,81 @@ module Dependabot
112
178
 
113
179
  private
114
180
 
181
+ sig { returns(String) }
115
182
  def solo_pr_name
116
183
  name = library? ? library_pr_name : application_pr_name
117
184
  "#{name}#{pr_name_directory}"
118
185
  end
119
186
 
187
+ sig { returns(String) }
120
188
  def library_pr_name
121
189
  "update " +
122
190
  if dependencies.count == 1
123
- "#{dependencies.first.display_name} requirement " \
124
- "#{from_version_msg(old_library_requirement(dependencies.first))}" \
125
- "to #{new_library_requirement(dependencies.first)}"
191
+ "#{T.must(dependencies.first).display_name} requirement " \
192
+ "#{from_version_msg(old_library_requirement(T.must(dependencies.first)))}" \
193
+ "to #{new_library_requirement(T.must(dependencies.first))}"
126
194
  else
127
195
  names = dependencies.map(&:name).uniq
128
196
  if names.count == 1
129
197
  "requirements for #{names.first}"
130
198
  else
131
- "requirements for #{names[0..-2].join(', ')} and #{names[-1]}"
199
+ "requirements for #{T.must(names[0..-2]).join(', ')} and #{names[-1]}"
132
200
  end
133
201
  end
134
202
  end
135
203
 
204
+ # rubocop:disable Metrics/AbcSize
205
+ sig { returns(String) }
136
206
  def application_pr_name
137
207
  "bump " +
138
208
  if dependencies.count == 1
139
209
  dependency = dependencies.first
140
- "#{dependency.display_name} " \
141
- "#{from_version_msg(dependency.humanized_previous_version)}" \
142
- "to #{dependency.humanized_version}"
210
+ "#{T.must(dependency).display_name} " \
211
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
212
+ "to #{T.must(dependency).humanized_version}"
143
213
  elsif updating_a_property?
144
214
  dependency = dependencies.first
145
215
  "#{property_name} " \
146
- "#{from_version_msg(dependency.humanized_previous_version)}" \
147
- "to #{dependency.humanized_version}"
216
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
217
+ "to #{T.must(dependency).humanized_version}"
148
218
  elsif updating_a_dependency_set?
149
219
  dependency = dependencies.first
150
220
  "#{dependency_set.fetch(:group)} dependency set " \
151
- "#{from_version_msg(dependency.humanized_previous_version)}" \
152
- "to #{dependency.humanized_version}"
221
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
222
+ "to #{T.must(dependency).humanized_version}"
153
223
  else
154
224
  names = dependencies.map(&:name).uniq
155
225
  if names.count == 1
156
- names.first
226
+ T.must(names.first)
157
227
  else
158
- "#{names[0..-2].join(', ')} and #{names[-1]}"
228
+ "#{T.must(names[0..-2]).join(', ')} and #{names[-1]}"
159
229
  end
160
230
  end
161
231
  end
232
+ # rubocop:enable Metrics/AbcSize
162
233
 
234
+ sig { returns(String) }
163
235
  def group_pr_name
236
+ directories_from_dependencies = dependencies.to_set { |dep| dep.metadata[:directory] }
237
+
238
+ directories_with_updates = source.directories&.filter do |directory|
239
+ directories_from_dependencies.include?(directory)
240
+ end
241
+
164
242
  updates = dependencies.map(&:name).uniq.count
165
243
 
166
- if source&.directories
167
- "bump the #{dependency_group.name} across #{source.directories.count} directories " \
244
+ if source.directories
245
+ "bump the #{T.must(dependency_group).name} across #{T.must(directories_with_updates).count} " \
246
+ "#{T.must(directories_with_updates).count > 1 ? 'directories' : 'directory'} " \
168
247
  "with #{updates} update#{'s' if updates > 1}"
169
248
  else
170
- "bump the #{dependency_group.name} group#{pr_name_directory} with #{updates} update#{'s' if updates > 1}"
249
+ "bump the #{T.must(dependency_group).name} group#{pr_name_directory} with #{updates} update#{if updates > 1
250
+ 's'
251
+ end}"
171
252
  end
172
253
  end
173
254
 
255
+ sig { returns(String) }
174
256
  def pr_name_prefix
175
257
  pr_name_prefixer.pr_name_prefix
176
258
  rescue StandardError => e
@@ -178,12 +260,14 @@ module Dependabot
178
260
  ""
179
261
  end
180
262
 
263
+ sig { returns(String) }
181
264
  def pr_name_directory
182
- return "" if files.first.directory == "/"
265
+ return "" if T.must(files.first).directory == "/"
183
266
 
184
- " in #{files.first.directory}"
267
+ " in #{T.must(files.first).directory}"
185
268
  end
186
269
 
270
+ sig { returns(String) }
187
271
  def commit_subject
188
272
  subject = pr_name.gsub("⬆️", ":arrow_up:").gsub("🔒", ":lock:")
189
273
  return subject unless subject.length > 72
@@ -191,57 +275,65 @@ module Dependabot
191
275
  subject = subject.gsub(/ from [^\s]*? to [^\s]*/, "")
192
276
  return subject unless subject.length > 72
193
277
 
194
- subject.split(" in ").first
278
+ T.must(subject.split(" in ").first)
195
279
  end
196
280
 
281
+ sig { returns(String) }
197
282
  def commit_message_intro
198
283
  return requirement_commit_message_intro if library?
199
284
 
200
285
  version_commit_message_intro
201
286
  end
202
287
 
288
+ sig { returns(String) }
203
289
  def prefixed_pr_message_footer
204
290
  return "" unless pr_message_footer
205
291
 
206
292
  "\n\n#{pr_message_footer}"
207
293
  end
208
294
 
295
+ sig { returns(String) }
209
296
  def suffixed_pr_message_header
210
297
  return "" unless pr_message_header
211
298
 
212
299
  "#{pr_message_header}\n\n"
213
300
  end
214
301
 
302
+ sig { returns(T.nilable(String)) }
215
303
  def message_trailers
216
304
  return unless signoff_trailers || custom_trailers
217
305
 
218
306
  [signoff_trailers, custom_trailers].compact.join("\n")
219
307
  end
220
308
 
309
+ sig { returns(T.nilable(String)) }
221
310
  def custom_trailers
222
- trailers = commit_message_options[:trailers]
311
+ trailers = commit_message_options&.dig(:trailers)
223
312
  return if trailers.nil?
224
313
  raise("Commit trailers must be a Hash object") unless trailers.is_a?(Hash)
225
314
 
226
315
  trailers.compact.map { |k, v| "#{k}: #{v}" }.join("\n")
227
316
  end
228
317
 
318
+ sig { returns(T.nilable(String)) }
229
319
  def signoff_trailers
230
320
  return unless on_behalf_of_message || signoff_message
231
321
 
232
322
  [on_behalf_of_message, signoff_message].compact.join("\n")
233
323
  end
234
324
 
325
+ sig { returns(T.nilable(String)) }
235
326
  def signoff_message
236
- signoff_details = commit_message_options[:signoff_details]
327
+ signoff_details = commit_message_options&.dig(:signoff_details)
237
328
  return unless signoff_details.is_a?(Hash)
238
329
  return unless signoff_details[:name] && signoff_details[:email]
239
330
 
240
331
  "Signed-off-by: #{signoff_details[:name]} <#{signoff_details[:email]}>"
241
332
  end
242
333
 
334
+ sig { returns(T.nilable(String)) }
243
335
  def on_behalf_of_message
244
- signoff_details = commit_message_options[:signoff_details]
336
+ signoff_details = commit_message_options&.dig(:signoff_details)
245
337
  return unless signoff_details.is_a?(Hash)
246
338
  return unless signoff_details[:org_name] && signoff_details[:org_email]
247
339
 
@@ -249,6 +341,7 @@ module Dependabot
249
341
  "<#{signoff_details[:org_email]}>"
250
342
  end
251
343
 
344
+ sig { returns(String) }
252
345
  def requirement_commit_message_intro
253
346
  msg = "Updates the requirements on "
254
347
 
@@ -256,7 +349,7 @@ module Dependabot
256
349
  if dependencies.count == 1
257
350
  "#{dependency_links.first} "
258
351
  else
259
- "#{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]} "
352
+ "#{T.must(dependency_links[0..-2]).join(', ')} and #{dependency_links[-1]} "
260
353
  end
261
354
 
262
355
  msg + "to permit the latest version."
@@ -265,8 +358,9 @@ module Dependabot
265
358
  # rubocop:disable Metrics/CyclomaticComplexity
266
359
  # rubocop:disable Metrics/PerceivedComplexity
267
360
  # rubocop:disable Metrics/AbcSize
361
+ sig { returns(String) }
268
362
  def version_commit_message_intro
269
- return multi_directory_group_intro if dependency_group && source&.directories
363
+ return multi_directory_group_intro if dependency_group && source.directories
270
364
 
271
365
  return group_intro if dependency_group
272
366
 
@@ -283,14 +377,16 @@ module Dependabot
283
377
 
284
378
  dependency = dependencies.first
285
379
  msg = "Bumps #{dependency_links.first} " \
286
- "#{from_version_msg(dependency.humanized_previous_version)}" \
287
- "to #{dependency.humanized_version}."
380
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
381
+ "to #{T.must(dependency).humanized_version}."
288
382
 
289
- msg += " This release includes the previously tagged commit." if switching_from_ref_to_release?(dependency)
383
+ if switching_from_ref_to_release?(T.must(dependency))
384
+ msg += " This release includes the previously tagged commit."
385
+ end
290
386
 
291
- if vulnerabilities_fixed[dependency.name]&.one?
387
+ if vulnerabilities_fixed[T.must(dependency).name]&.one?
292
388
  msg += " **This update includes a security fix.**"
293
- elsif vulnerabilities_fixed[dependency.name]&.any?
389
+ elsif vulnerabilities_fixed[T.must(dependency).name]&.any?
294
390
  msg += " **This update includes security fixes.**"
295
391
  end
296
392
 
@@ -300,35 +396,39 @@ module Dependabot
300
396
  # rubocop:enable Metrics/PerceivedComplexity
301
397
  # rubocop:enable Metrics/AbcSize
302
398
 
399
+ sig { returns(String) }
303
400
  def multidependency_property_intro
304
401
  dependency = dependencies.first
305
402
 
306
403
  "Bumps `#{property_name}` " \
307
- "#{from_version_msg(dependency.humanized_previous_version)}" \
308
- "to #{dependency.humanized_version}."
404
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
405
+ "to #{T.must(dependency).humanized_version}."
309
406
  end
310
407
 
408
+ sig { returns(String) }
311
409
  def dependency_set_intro
312
410
  dependency = dependencies.first
313
411
 
314
412
  "Bumps `#{dependency_set.fetch(:group)}` " \
315
- "dependency set #{from_version_msg(dependency.humanized_previous_version)}" \
316
- "to #{dependency.humanized_version}."
413
+ "dependency set #{from_version_msg(T.must(dependency).humanized_previous_version)}" \
414
+ "to #{T.must(dependency).humanized_version}."
317
415
  end
318
416
 
417
+ sig { returns(String) }
319
418
  def multidependency_intro
320
- "Bumps #{dependency_links[0..-2].join(', ')} " \
419
+ "Bumps #{T.must(dependency_links[0..-2]).join(', ')} " \
321
420
  "and #{dependency_links[-1]}. These " \
322
421
  "dependencies needed to be updated together."
323
422
  end
324
423
 
424
+ sig { returns(String) }
325
425
  def transitive_multidependency_intro
326
426
  dependency = dependencies.first
327
427
 
328
- msg = "Bumps #{dependency_links[0]} to #{dependency.humanized_version}"
428
+ msg = "Bumps #{dependency_links[0]} to #{T.must(dependency).humanized_version}"
329
429
 
330
430
  msg += if dependencies.count > 2
331
- " and updates ancestor dependencies #{dependency_links[0..-2].join(', ')} " \
431
+ " and updates ancestor dependencies #{T.must(dependency_links[0..-2]).join(', ')} " \
332
432
  "and #{dependency_links[-1]}. "
333
433
  else
334
434
  " and updates ancestor dependency #{dependency_links[1]}. "
@@ -339,11 +439,12 @@ module Dependabot
339
439
  msg
340
440
  end
341
441
 
442
+ sig { returns(String) }
342
443
  def transitive_removed_dependency_intro
343
444
  msg = "Removes #{dependency_links[0]}. It's no longer used after updating"
344
445
 
345
446
  msg += if dependencies.count > 2
346
- " ancestor dependencies #{dependency_links[0..-2].join(', ')} " \
447
+ " ancestor dependencies #{T.must(dependency_links[0..-2]).join(', ')} " \
347
448
  "and #{dependency_links[-1]}. "
348
449
  else
349
450
  " ancestor dependency #{dependency_links[1]}. "
@@ -354,16 +455,18 @@ module Dependabot
354
455
  msg
355
456
  end
356
457
 
458
+ # rubocop:disable Metrics/AbcSize
459
+ sig { returns(String) }
357
460
  def multi_directory_group_intro
358
461
  msg = ""
359
462
 
360
- source.directories.each do |directory|
463
+ T.must(source.directories).each do |directory|
361
464
  dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
362
465
  next unless dependencies_in_directory.any?
363
466
 
364
467
  update_count = dependencies_in_directory.map(&:name).uniq.count
365
468
 
366
- msg += "Bumps the #{dependency_group.name} " \
469
+ msg += "Bumps the #{T.must(dependency_group).name} " \
367
470
  "with #{update_count} update#{update_count > 1 ? 's' : ''} in the #{directory} directory:"
368
471
 
369
472
  msg += if update_count >= 5
@@ -378,10 +481,11 @@ module Dependabot
378
481
  "\n\n#{table([header] + rows)}"
379
482
  elsif update_count > 1
380
483
  dependency_links_in_directory = dependency_links_for_directory(directory)
381
- " #{dependency_links_in_directory[0..-2].join(', ')} and #{dependency_links_in_directory[-1]}."
484
+ " #{T.must(T.must(dependency_links_in_directory)[0..-2]).join(', ')}" \
485
+ " and #{T.must(dependency_links_in_directory)[-1]}."
382
486
  else
383
487
  dependency_links_in_directory = dependency_links_for_directory(directory)
384
- " #{dependency_links_in_directory.first}."
488
+ " #{T.must(dependency_links_in_directory).first}."
385
489
  end
386
490
 
387
491
  msg += "\n"
@@ -389,11 +493,13 @@ module Dependabot
389
493
 
390
494
  msg
391
495
  end
496
+ # rubocop:enable Metrics/AbcSize
392
497
 
498
+ sig { returns(String) }
393
499
  def group_intro
394
500
  update_count = dependencies.map(&:name).uniq.count
395
501
 
396
- msg = "Bumps the #{dependency_group.name} group#{pr_name_directory} " \
502
+ msg = "Bumps the #{T.must(dependency_group).name} group#{pr_name_directory} " \
397
503
  "with #{update_count} update#{update_count > 1 ? 's' : ''}:"
398
504
 
399
505
  msg += if update_count >= 5
@@ -407,7 +513,7 @@ module Dependabot
407
513
  end
408
514
  "\n\n#{table([header] + rows)}"
409
515
  elsif update_count > 1
410
- " #{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]}."
516
+ " #{T.must(dependency_links[0..-2]).join(', ')} and #{dependency_links[-1]}."
411
517
  else
412
518
  " #{dependency_links.first}."
413
519
  end
@@ -417,66 +523,86 @@ module Dependabot
417
523
  msg
418
524
  end
419
525
 
526
+ sig { params(previous_version: T.nilable(String)).returns(String) }
420
527
  def from_version_msg(previous_version)
421
528
  return "" unless previous_version
422
529
 
423
530
  "from #{previous_version} "
424
531
  end
425
532
 
533
+ sig { returns(T::Boolean) }
426
534
  def updating_a_property?
427
- dependencies.first
428
- .requirements
429
- .any? { |r| r.dig(:metadata, :property_name) }
535
+ T.must(dependencies.first)
536
+ .requirements
537
+ .any? { |r| r.dig(:metadata, :property_name) }
430
538
  end
431
539
 
540
+ sig { returns(T::Boolean) }
432
541
  def updating_a_dependency_set?
433
- dependencies.first
434
- .requirements
435
- .any? { |r| r.dig(:metadata, :dependency_set) }
542
+ T.must(dependencies.first)
543
+ .requirements
544
+ .any? { |r| r.dig(:metadata, :dependency_set) }
436
545
  end
437
546
 
547
+ sig { returns(T::Boolean) }
438
548
  def removing_a_transitive_dependency?
439
549
  dependencies.any?(&:removed?)
440
550
  end
441
551
 
552
+ sig { returns(T::Boolean) }
442
553
  def updating_top_level_and_transitive_dependencies?
443
554
  dependencies.any?(&:top_level?) &&
444
555
  dependencies.any? { |dep| !dep.top_level? }
445
556
  end
446
557
 
558
+ sig { returns(String) }
447
559
  def property_name
448
- @property_name ||= dependencies.first.requirements
449
- .find { |r| r.dig(:metadata, :property_name) }
450
- &.dig(:metadata, :property_name)
560
+ @property_name ||=
561
+ T.let(
562
+ dependencies.first
563
+ &.requirements
564
+ &.find { |r| r.dig(:metadata, :property_name) }
565
+ &.dig(:metadata, :property_name),
566
+ T.nilable(String)
567
+ )
451
568
 
452
569
  raise "No property name!" unless @property_name
453
570
 
454
571
  @property_name
455
572
  end
456
573
 
574
+ sig { returns(T::Hash[Symbol, String]) }
457
575
  def dependency_set
458
- @dependency_set ||= dependencies.first.requirements
459
- .find { |r| r.dig(:metadata, :dependency_set) }
460
- &.dig(:metadata, :dependency_set)
576
+ @dependency_set ||=
577
+ T.let(
578
+ dependencies.first
579
+ &.requirements
580
+ &.find { |r| r.dig(:metadata, :dependency_set) }
581
+ &.dig(:metadata, :dependency_set),
582
+ T.nilable(T.nilable(T::Hash[Symbol, String]))
583
+ )
461
584
 
462
585
  raise "No dependency set!" unless @dependency_set
463
586
 
464
587
  @dependency_set
465
588
  end
466
589
 
590
+ sig { returns(T::Array[String]) }
467
591
  def dependency_links
468
- return @dependency_links if defined?(@dependency_links)
592
+ return T.must(@dependency_links) if defined?(@dependency_links)
469
593
 
470
594
  uniq_deps = dependencies.each_with_object({}) { |dep, memo| memo[dep.name] ||= dep }.values
471
595
  @dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
472
596
  end
473
597
 
598
+ sig { params(directory: String).returns(T.nilable(T::Array[String])) }
474
599
  def dependency_links_for_directory(directory)
475
600
  dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
476
601
  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) }
602
+ @dependency_links = T.let(uniq_deps.map { |dep| dependency_link(dep) }, T.nilable(T::Array[String]))
478
603
  end
479
604
 
605
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
480
606
  def dependency_link(dependency)
481
607
  if source_url(dependency)
482
608
  "[#{dependency.display_name}](#{source_url(dependency)})"
@@ -487,12 +613,14 @@ module Dependabot
487
613
  end
488
614
  end
489
615
 
616
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
490
617
  def dependency_version_update(dependency)
491
618
  "#{dependency.humanized_previous_version} to #{dependency.humanized_version}"
492
619
  end
493
620
 
621
+ sig { returns(String) }
494
622
  def metadata_links
495
- return metadata_links_for_dep(dependencies.first) if dependencies.count == 1 && dependency_group.nil?
623
+ return metadata_links_for_dep(T.must(dependencies.first)) if dependencies.count == 1 && dependency_group.nil?
496
624
 
497
625
  dependencies.map do |dep|
498
626
  if dep.removed?
@@ -506,6 +634,7 @@ module Dependabot
506
634
  end.join
507
635
  end
508
636
 
637
+ sig { params(dep: Dependabot::Dependency).returns(String) }
509
638
  def metadata_links_for_dep(dep)
510
639
  msg = ""
511
640
  msg += "\n- [Release notes](#{releases_url(dep)})" if releases_url(dep)
@@ -515,13 +644,15 @@ module Dependabot
515
644
  msg
516
645
  end
517
646
 
647
+ sig { params(rows: T::Array[T::Array[String]]).returns(String) }
518
648
  def table(rows)
519
649
  [
520
- table_header(rows[0]),
521
- rows[1..].map { |r| table_row(r) }
650
+ table_header(T.must(rows[0])),
651
+ T.must(rows[1..]).map { |r| table_row(r) }
522
652
  ].join("\n")
523
653
  end
524
654
 
655
+ sig { params(row: T::Array[String]).returns(String) }
525
656
  def table_header(row)
526
657
  [
527
658
  table_row(row),
@@ -529,12 +660,14 @@ module Dependabot
529
660
  ].join("\n")
530
661
  end
531
662
 
663
+ sig { params(row: T::Array[String]).returns(String) }
532
664
  def table_row(row)
533
665
  "| #{row.join(' | ')} |"
534
666
  end
535
667
 
668
+ sig { returns(String) }
536
669
  def metadata_cascades # rubocop:disable Metrics/PerceivedComplexity
537
- return metadata_cascades_for_dep(dependencies.first) if dependencies.one? && !dependency_group
670
+ return metadata_cascades_for_dep(T.must(dependencies.first)) if dependencies.one? && !dependency_group
538
671
 
539
672
  dependencies.map do |dep|
540
673
  msg = if dep.removed?
@@ -555,6 +688,7 @@ module Dependabot
555
688
  end.join
556
689
  end
557
690
 
691
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
558
692
  def metadata_cascades_for_dep(dependency)
559
693
  return "" if dependency.removed?
560
694
 
@@ -567,6 +701,7 @@ module Dependabot
567
701
  ).to_s
568
702
  end
569
703
 
704
+ sig { returns(String) }
570
705
  def ignore_conditions_table
571
706
  # Return an empty string if ignore_conditions is empty
572
707
  return "" if @ignore_conditions.empty?
@@ -580,7 +715,9 @@ module Dependabot
580
715
  return "" if valid_ignore_conditions.empty?
581
716
 
582
717
  # Sort them by updated_at (or created_at if updated_at is nil), taking the latest 20
583
- sorted_ignore_conditions = valid_ignore_conditions.sort_by { |ic| ic["updated-at"] }.last(20)
718
+ sorted_ignore_conditions = valid_ignore_conditions.sort_by do |ic|
719
+ ic["updated_at"].nil? ? T.must(ic["created_at"]) : T.must(ic["updated_at"])
720
+ end.last(20)
584
721
 
585
722
  # Map each condition to a row string
586
723
  table_rows = sorted_ignore_conditions.map do |ic|
@@ -591,6 +728,7 @@ module Dependabot
591
728
  build_table(summary, table_rows)
592
729
  end
593
730
 
731
+ sig { params(summary: String, rows: T::Array[String]).returns(String) }
594
732
  def build_table(summary, rows)
595
733
  table_header = "| Dependency Name | Ignore Conditions |"
596
734
  table_divider = "| --- | --- |"
@@ -607,74 +745,87 @@ module Dependabot
607
745
  end
608
746
  end
609
747
 
748
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
610
749
  def changelog_url(dependency)
611
750
  metadata_finder(dependency).changelog_url
612
751
  end
613
752
 
753
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
614
754
  def commits_url(dependency)
615
755
  metadata_finder(dependency).commits_url
616
756
  end
617
757
 
758
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
618
759
  def homepage_url(dependency)
619
760
  metadata_finder(dependency).homepage_url
620
761
  end
621
762
 
763
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
622
764
  def releases_url(dependency)
623
765
  metadata_finder(dependency).releases_url
624
766
  end
625
767
 
768
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
626
769
  def source_url(dependency)
627
770
  metadata_finder(dependency).source_url
628
771
  end
629
772
 
773
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
630
774
  def upgrade_url(dependency)
631
775
  metadata_finder(dependency).upgrade_guide_url
632
776
  end
633
777
 
778
+ sig { params(dependency: Dependabot::Dependency).returns(Dependabot::MetadataFinders::Base) }
634
779
  def metadata_finder(dependency)
635
- @metadata_finder ||= {}
780
+ @metadata_finder ||= T.let({}, T.nilable(T::Hash[String, Dependabot::MetadataFinders::Base]))
636
781
  @metadata_finder[dependency.name] ||=
637
782
  MetadataFinders
638
783
  .for_package_manager(dependency.package_manager)
639
784
  .new(dependency: dependency, credentials: credentials)
640
785
  end
641
786
 
787
+ sig { returns(Dependabot::PullRequestCreator::PrNamePrefixer) }
642
788
  def pr_name_prefixer
643
789
  @pr_name_prefixer ||=
644
- PrNamePrefixer.new(
645
- source: source,
646
- dependencies: dependencies,
647
- credentials: credentials,
648
- commit_message_options: commit_message_options,
649
- security_fix: vulnerabilities_fixed.values.flatten.any?
790
+ T.let(
791
+ PrNamePrefixer.new(
792
+ source: source,
793
+ dependencies: dependencies,
794
+ credentials: credentials,
795
+ commit_message_options: commit_message_options,
796
+ security_fix: vulnerabilities_fixed.values.flatten.any?
797
+ ),
798
+ T.nilable(Dependabot::PullRequestCreator::PrNamePrefixer)
650
799
  )
651
800
  end
652
801
 
802
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
653
803
  def old_library_requirement(dependency)
654
804
  old_reqs =
655
- dependency.previous_requirements - dependency.requirements
805
+ T.must(dependency.previous_requirements) - dependency.requirements
656
806
 
657
807
  gemspec =
658
808
  old_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
659
809
  return gemspec.fetch(:requirement) if gemspec
660
810
 
661
- req = old_reqs.first.fetch(:requirement)
811
+ req = T.must(old_reqs.first).fetch(:requirement)
662
812
  return req if req
663
813
 
664
814
  dependency.previous_ref if dependency.ref_changed?
665
815
  end
666
816
 
817
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
667
818
  def new_library_requirement(dependency)
668
819
  updated_reqs =
669
- dependency.requirements - dependency.previous_requirements
820
+ dependency.requirements - T.must(dependency.previous_requirements)
670
821
 
671
822
  gemspec =
672
823
  updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
673
824
  return gemspec.fetch(:requirement) if gemspec
674
825
 
675
- req = updated_reqs.first.fetch(:requirement)
826
+ req = T.must(updated_reqs.first).fetch(:requirement)
676
827
  return req if req
677
- return dependency.new_ref if dependency.ref_changed? && dependency.new_ref
828
+ return T.must(dependency.new_ref) if dependency.ref_changed? && dependency.new_ref
678
829
 
679
830
  raise "No new requirement!"
680
831
  end
@@ -684,6 +835,7 @@ module Dependabot
684
835
  # `requirements_update_strategy`.
685
836
  #
686
837
  # TODO reuse in BranchNamer
838
+ sig { returns(T::Boolean) }
687
839
  def library?
688
840
  # Reject any nested child gemspecs/vendored git dependencies
689
841
  root_files = files.map(&:name)
@@ -693,6 +845,7 @@ module Dependabot
693
845
  dependencies.any? { |d| d.humanized_previous_version.nil? }
694
846
  end
695
847
 
848
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
696
849
  def switching_from_ref_to_release?(dependency)
697
850
  unless dependency.previous_version&.match?(/^[0-9a-f]{40}$/) ||
698
851
  (dependency.previous_version.nil? && dependency.previous_ref)
@@ -702,8 +855,12 @@ module Dependabot
702
855
  Gem::Version.correct?(dependency.version)
703
856
  end
704
857
 
858
+ sig { returns(String) }
705
859
  def package_manager
706
- @package_manager ||= dependencies.first.package_manager
860
+ @package_manager ||= T.let(
861
+ T.must(dependencies.first).package_manager,
862
+ T.nilable(String)
863
+ )
707
864
  end
708
865
  end
709
866
  end