dependabot-common 0.244.0 → 0.246.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/bitbucket.rb +113 -5
  3. data/lib/dependabot/clients/bitbucket_with_retries.rb +34 -10
  4. data/lib/dependabot/clients/codecommit.rb +107 -12
  5. data/lib/dependabot/clients/github_with_retries.rb +61 -19
  6. data/lib/dependabot/clients/gitlab_with_retries.rb +60 -7
  7. data/lib/dependabot/dependency.rb +1 -1
  8. data/lib/dependabot/errors.rb +8 -2
  9. data/lib/dependabot/git_commit_checker.rb +4 -3
  10. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +1 -1
  11. data/lib/dependabot/metadata_finders/base/commits_finder.rb +1 -1
  12. data/lib/dependabot/metadata_finders/base/release_finder.rb +1 -1
  13. data/lib/dependabot/pull_request_creator/azure.rb +80 -9
  14. data/lib/dependabot/pull_request_creator/bitbucket.rb +73 -9
  15. data/lib/dependabot/pull_request_creator/codecommit.rb +96 -25
  16. data/lib/dependabot/pull_request_creator/github.rb +162 -49
  17. data/lib/dependabot/pull_request_creator/gitlab.rb +109 -21
  18. data/lib/dependabot/pull_request_creator/message_builder.rb +239 -89
  19. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +11 -9
  20. data/lib/dependabot/pull_request_creator.rb +32 -27
  21. data/lib/dependabot/pull_request_updater/azure.rb +75 -11
  22. data/lib/dependabot/pull_request_updater/github.rb +89 -28
  23. data/lib/dependabot/pull_request_updater/gitlab.rb +61 -12
  24. data/lib/dependabot/pull_request_updater.rb +1 -1
  25. data/lib/dependabot/shared_helpers.rb +19 -1
  26. data/lib/dependabot/update_checkers/base.rb +121 -31
  27. data/lib/dependabot.rb +1 -1
  28. metadata +3 -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,74 @@ 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
164
236
  updates = dependencies.map(&:name).uniq.count
165
237
 
166
- if source&.directories
167
- "bump the #{dependency_group.name} across #{source.directories.count} directories " \
238
+ if source.directories
239
+ "bump the #{T.must(dependency_group).name} across #{T.must(source.directories).count} directories " \
168
240
  "with #{updates} update#{'s' if updates > 1}"
169
241
  else
170
- "bump the #{dependency_group.name} group#{pr_name_directory} with #{updates} update#{'s' if updates > 1}"
242
+ "bump the #{T.must(dependency_group).name} group#{pr_name_directory} with #{updates} update#{if updates > 1
243
+ 's'
244
+ end}"
171
245
  end
172
246
  end
173
247
 
248
+ sig { returns(String) }
174
249
  def pr_name_prefix
175
250
  pr_name_prefixer.pr_name_prefix
176
251
  rescue StandardError => e
@@ -178,12 +253,14 @@ module Dependabot
178
253
  ""
179
254
  end
180
255
 
256
+ sig { returns(String) }
181
257
  def pr_name_directory
182
- return "" if files.first.directory == "/"
258
+ return "" if T.must(files.first).directory == "/"
183
259
 
184
- " in #{files.first.directory}"
260
+ " in #{T.must(files.first).directory}"
185
261
  end
186
262
 
263
+ sig { returns(String) }
187
264
  def commit_subject
188
265
  subject = pr_name.gsub("⬆️", ":arrow_up:").gsub("🔒", ":lock:")
189
266
  return subject unless subject.length > 72
@@ -191,57 +268,65 @@ module Dependabot
191
268
  subject = subject.gsub(/ from [^\s]*? to [^\s]*/, "")
192
269
  return subject unless subject.length > 72
193
270
 
194
- subject.split(" in ").first
271
+ T.must(subject.split(" in ").first)
195
272
  end
196
273
 
274
+ sig { returns(String) }
197
275
  def commit_message_intro
198
276
  return requirement_commit_message_intro if library?
199
277
 
200
278
  version_commit_message_intro
201
279
  end
202
280
 
281
+ sig { returns(String) }
203
282
  def prefixed_pr_message_footer
204
283
  return "" unless pr_message_footer
205
284
 
206
285
  "\n\n#{pr_message_footer}"
207
286
  end
208
287
 
288
+ sig { returns(String) }
209
289
  def suffixed_pr_message_header
210
290
  return "" unless pr_message_header
211
291
 
212
292
  "#{pr_message_header}\n\n"
213
293
  end
214
294
 
295
+ sig { returns(T.nilable(String)) }
215
296
  def message_trailers
216
297
  return unless signoff_trailers || custom_trailers
217
298
 
218
299
  [signoff_trailers, custom_trailers].compact.join("\n")
219
300
  end
220
301
 
302
+ sig { returns(T.nilable(String)) }
221
303
  def custom_trailers
222
- trailers = commit_message_options[:trailers]
304
+ trailers = commit_message_options&.dig(:trailers)
223
305
  return if trailers.nil?
224
306
  raise("Commit trailers must be a Hash object") unless trailers.is_a?(Hash)
225
307
 
226
308
  trailers.compact.map { |k, v| "#{k}: #{v}" }.join("\n")
227
309
  end
228
310
 
311
+ sig { returns(T.nilable(String)) }
229
312
  def signoff_trailers
230
313
  return unless on_behalf_of_message || signoff_message
231
314
 
232
315
  [on_behalf_of_message, signoff_message].compact.join("\n")
233
316
  end
234
317
 
318
+ sig { returns(T.nilable(String)) }
235
319
  def signoff_message
236
- signoff_details = commit_message_options[:signoff_details]
320
+ signoff_details = commit_message_options&.dig(:signoff_details)
237
321
  return unless signoff_details.is_a?(Hash)
238
322
  return unless signoff_details[:name] && signoff_details[:email]
239
323
 
240
324
  "Signed-off-by: #{signoff_details[:name]} <#{signoff_details[:email]}>"
241
325
  end
242
326
 
327
+ sig { returns(T.nilable(String)) }
243
328
  def on_behalf_of_message
244
- signoff_details = commit_message_options[:signoff_details]
329
+ signoff_details = commit_message_options&.dig(:signoff_details)
245
330
  return unless signoff_details.is_a?(Hash)
246
331
  return unless signoff_details[:org_name] && signoff_details[:org_email]
247
332
 
@@ -249,6 +334,7 @@ module Dependabot
249
334
  "<#{signoff_details[:org_email]}>"
250
335
  end
251
336
 
337
+ sig { returns(String) }
252
338
  def requirement_commit_message_intro
253
339
  msg = "Updates the requirements on "
254
340
 
@@ -256,7 +342,7 @@ module Dependabot
256
342
  if dependencies.count == 1
257
343
  "#{dependency_links.first} "
258
344
  else
259
- "#{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]} "
345
+ "#{T.must(dependency_links[0..-2]).join(', ')} and #{dependency_links[-1]} "
260
346
  end
261
347
 
262
348
  msg + "to permit the latest version."
@@ -265,8 +351,9 @@ module Dependabot
265
351
  # rubocop:disable Metrics/CyclomaticComplexity
266
352
  # rubocop:disable Metrics/PerceivedComplexity
267
353
  # rubocop:disable Metrics/AbcSize
354
+ sig { returns(String) }
268
355
  def version_commit_message_intro
269
- return multi_directory_group_intro if dependency_group && source&.directories
356
+ return multi_directory_group_intro if dependency_group && source.directories
270
357
 
271
358
  return group_intro if dependency_group
272
359
 
@@ -283,14 +370,16 @@ module Dependabot
283
370
 
284
371
  dependency = dependencies.first
285
372
  msg = "Bumps #{dependency_links.first} " \
286
- "#{from_version_msg(dependency.humanized_previous_version)}" \
287
- "to #{dependency.humanized_version}."
373
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
374
+ "to #{T.must(dependency).humanized_version}."
288
375
 
289
- msg += " This release includes the previously tagged commit." if switching_from_ref_to_release?(dependency)
376
+ if switching_from_ref_to_release?(T.must(dependency))
377
+ msg += " This release includes the previously tagged commit."
378
+ end
290
379
 
291
- if vulnerabilities_fixed[dependency.name]&.one?
380
+ if vulnerabilities_fixed[T.must(dependency).name]&.one?
292
381
  msg += " **This update includes a security fix.**"
293
- elsif vulnerabilities_fixed[dependency.name]&.any?
382
+ elsif vulnerabilities_fixed[T.must(dependency).name]&.any?
294
383
  msg += " **This update includes security fixes.**"
295
384
  end
296
385
 
@@ -300,35 +389,39 @@ module Dependabot
300
389
  # rubocop:enable Metrics/PerceivedComplexity
301
390
  # rubocop:enable Metrics/AbcSize
302
391
 
392
+ sig { returns(String) }
303
393
  def multidependency_property_intro
304
394
  dependency = dependencies.first
305
395
 
306
396
  "Bumps `#{property_name}` " \
307
- "#{from_version_msg(dependency.humanized_previous_version)}" \
308
- "to #{dependency.humanized_version}."
397
+ "#{from_version_msg(T.must(dependency).humanized_previous_version)}" \
398
+ "to #{T.must(dependency).humanized_version}."
309
399
  end
310
400
 
401
+ sig { returns(String) }
311
402
  def dependency_set_intro
312
403
  dependency = dependencies.first
313
404
 
314
405
  "Bumps `#{dependency_set.fetch(:group)}` " \
315
- "dependency set #{from_version_msg(dependency.humanized_previous_version)}" \
316
- "to #{dependency.humanized_version}."
406
+ "dependency set #{from_version_msg(T.must(dependency).humanized_previous_version)}" \
407
+ "to #{T.must(dependency).humanized_version}."
317
408
  end
318
409
 
410
+ sig { returns(String) }
319
411
  def multidependency_intro
320
- "Bumps #{dependency_links[0..-2].join(', ')} " \
412
+ "Bumps #{T.must(dependency_links[0..-2]).join(', ')} " \
321
413
  "and #{dependency_links[-1]}. These " \
322
414
  "dependencies needed to be updated together."
323
415
  end
324
416
 
417
+ sig { returns(String) }
325
418
  def transitive_multidependency_intro
326
419
  dependency = dependencies.first
327
420
 
328
- msg = "Bumps #{dependency_links[0]} to #{dependency.humanized_version}"
421
+ msg = "Bumps #{dependency_links[0]} to #{T.must(dependency).humanized_version}"
329
422
 
330
423
  msg += if dependencies.count > 2
331
- " and updates ancestor dependencies #{dependency_links[0..-2].join(', ')} " \
424
+ " and updates ancestor dependencies #{T.must(dependency_links[0..-2]).join(', ')} " \
332
425
  "and #{dependency_links[-1]}. "
333
426
  else
334
427
  " and updates ancestor dependency #{dependency_links[1]}. "
@@ -339,11 +432,12 @@ module Dependabot
339
432
  msg
340
433
  end
341
434
 
435
+ sig { returns(String) }
342
436
  def transitive_removed_dependency_intro
343
437
  msg = "Removes #{dependency_links[0]}. It's no longer used after updating"
344
438
 
345
439
  msg += if dependencies.count > 2
346
- " ancestor dependencies #{dependency_links[0..-2].join(', ')} " \
440
+ " ancestor dependencies #{T.must(dependency_links[0..-2]).join(', ')} " \
347
441
  "and #{dependency_links[-1]}. "
348
442
  else
349
443
  " ancestor dependency #{dependency_links[1]}. "
@@ -354,16 +448,18 @@ module Dependabot
354
448
  msg
355
449
  end
356
450
 
451
+ # rubocop:disable Metrics/AbcSize
452
+ sig { returns(String) }
357
453
  def multi_directory_group_intro
358
454
  msg = ""
359
455
 
360
- source.directories.each do |directory|
456
+ T.must(source.directories).each do |directory|
361
457
  dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
362
458
  next unless dependencies_in_directory.any?
363
459
 
364
460
  update_count = dependencies_in_directory.map(&:name).uniq.count
365
461
 
366
- msg += "Bumps the #{dependency_group.name} " \
462
+ msg += "Bumps the #{T.must(dependency_group).name} " \
367
463
  "with #{update_count} update#{update_count > 1 ? 's' : ''} in the #{directory} directory:"
368
464
 
369
465
  msg += if update_count >= 5
@@ -378,10 +474,11 @@ module Dependabot
378
474
  "\n\n#{table([header] + rows)}"
379
475
  elsif update_count > 1
380
476
  dependency_links_in_directory = dependency_links_for_directory(directory)
381
- " #{dependency_links_in_directory[0..-2].join(', ')} and #{dependency_links_in_directory[-1]}."
477
+ " #{T.must(T.must(dependency_links_in_directory)[0..-2]).join(', ')}" \
478
+ " and #{T.must(dependency_links_in_directory)[-1]}."
382
479
  else
383
480
  dependency_links_in_directory = dependency_links_for_directory(directory)
384
- " #{dependency_links_in_directory.first}."
481
+ " #{T.must(dependency_links_in_directory).first}."
385
482
  end
386
483
 
387
484
  msg += "\n"
@@ -389,11 +486,13 @@ module Dependabot
389
486
 
390
487
  msg
391
488
  end
489
+ # rubocop:enable Metrics/AbcSize
392
490
 
491
+ sig { returns(String) }
393
492
  def group_intro
394
493
  update_count = dependencies.map(&:name).uniq.count
395
494
 
396
- msg = "Bumps the #{dependency_group.name} group#{pr_name_directory} " \
495
+ msg = "Bumps the #{T.must(dependency_group).name} group#{pr_name_directory} " \
397
496
  "with #{update_count} update#{update_count > 1 ? 's' : ''}:"
398
497
 
399
498
  msg += if update_count >= 5
@@ -407,7 +506,7 @@ module Dependabot
407
506
  end
408
507
  "\n\n#{table([header] + rows)}"
409
508
  elsif update_count > 1
410
- " #{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]}."
509
+ " #{T.must(dependency_links[0..-2]).join(', ')} and #{dependency_links[-1]}."
411
510
  else
412
511
  " #{dependency_links.first}."
413
512
  end
@@ -417,66 +516,86 @@ module Dependabot
417
516
  msg
418
517
  end
419
518
 
519
+ sig { params(previous_version: T.nilable(String)).returns(String) }
420
520
  def from_version_msg(previous_version)
421
521
  return "" unless previous_version
422
522
 
423
523
  "from #{previous_version} "
424
524
  end
425
525
 
526
+ sig { returns(T::Boolean) }
426
527
  def updating_a_property?
427
- dependencies.first
428
- .requirements
429
- .any? { |r| r.dig(:metadata, :property_name) }
528
+ T.must(dependencies.first)
529
+ .requirements
530
+ .any? { |r| r.dig(:metadata, :property_name) }
430
531
  end
431
532
 
533
+ sig { returns(T::Boolean) }
432
534
  def updating_a_dependency_set?
433
- dependencies.first
434
- .requirements
435
- .any? { |r| r.dig(:metadata, :dependency_set) }
535
+ T.must(dependencies.first)
536
+ .requirements
537
+ .any? { |r| r.dig(:metadata, :dependency_set) }
436
538
  end
437
539
 
540
+ sig { returns(T::Boolean) }
438
541
  def removing_a_transitive_dependency?
439
542
  dependencies.any?(&:removed?)
440
543
  end
441
544
 
545
+ sig { returns(T::Boolean) }
442
546
  def updating_top_level_and_transitive_dependencies?
443
547
  dependencies.any?(&:top_level?) &&
444
548
  dependencies.any? { |dep| !dep.top_level? }
445
549
  end
446
550
 
551
+ sig { returns(String) }
447
552
  def property_name
448
- @property_name ||= dependencies.first.requirements
449
- .find { |r| r.dig(:metadata, :property_name) }
450
- &.dig(:metadata, :property_name)
553
+ @property_name ||=
554
+ T.let(
555
+ dependencies.first
556
+ &.requirements
557
+ &.find { |r| r.dig(:metadata, :property_name) }
558
+ &.dig(:metadata, :property_name),
559
+ T.nilable(String)
560
+ )
451
561
 
452
562
  raise "No property name!" unless @property_name
453
563
 
454
564
  @property_name
455
565
  end
456
566
 
567
+ sig { returns(T::Hash[Symbol, String]) }
457
568
  def dependency_set
458
- @dependency_set ||= dependencies.first.requirements
459
- .find { |r| r.dig(:metadata, :dependency_set) }
460
- &.dig(:metadata, :dependency_set)
569
+ @dependency_set ||=
570
+ T.let(
571
+ dependencies.first
572
+ &.requirements
573
+ &.find { |r| r.dig(:metadata, :dependency_set) }
574
+ &.dig(:metadata, :dependency_set),
575
+ T.nilable(T.nilable(T::Hash[Symbol, String]))
576
+ )
461
577
 
462
578
  raise "No dependency set!" unless @dependency_set
463
579
 
464
580
  @dependency_set
465
581
  end
466
582
 
583
+ sig { returns(T::Array[String]) }
467
584
  def dependency_links
468
- return @dependency_links if defined?(@dependency_links)
585
+ return T.must(@dependency_links) if defined?(@dependency_links)
469
586
 
470
587
  uniq_deps = dependencies.each_with_object({}) { |dep, memo| memo[dep.name] ||= dep }.values
471
588
  @dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
472
589
  end
473
590
 
591
+ sig { params(directory: String).returns(T.nilable(T::Array[String])) }
474
592
  def dependency_links_for_directory(directory)
475
593
  dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
476
594
  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) }
595
+ @dependency_links = T.let(uniq_deps.map { |dep| dependency_link(dep) }, T.nilable(T::Array[String]))
478
596
  end
479
597
 
598
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
480
599
  def dependency_link(dependency)
481
600
  if source_url(dependency)
482
601
  "[#{dependency.display_name}](#{source_url(dependency)})"
@@ -487,12 +606,14 @@ module Dependabot
487
606
  end
488
607
  end
489
608
 
609
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
490
610
  def dependency_version_update(dependency)
491
611
  "#{dependency.humanized_previous_version} to #{dependency.humanized_version}"
492
612
  end
493
613
 
614
+ sig { returns(String) }
494
615
  def metadata_links
495
- return metadata_links_for_dep(dependencies.first) if dependencies.count == 1 && dependency_group.nil?
616
+ return metadata_links_for_dep(T.must(dependencies.first)) if dependencies.count == 1 && dependency_group.nil?
496
617
 
497
618
  dependencies.map do |dep|
498
619
  if dep.removed?
@@ -506,6 +627,7 @@ module Dependabot
506
627
  end.join
507
628
  end
508
629
 
630
+ sig { params(dep: Dependabot::Dependency).returns(String) }
509
631
  def metadata_links_for_dep(dep)
510
632
  msg = ""
511
633
  msg += "\n- [Release notes](#{releases_url(dep)})" if releases_url(dep)
@@ -515,13 +637,15 @@ module Dependabot
515
637
  msg
516
638
  end
517
639
 
640
+ sig { params(rows: T::Array[T::Array[String]]).returns(String) }
518
641
  def table(rows)
519
642
  [
520
- table_header(rows[0]),
521
- rows[1..].map { |r| table_row(r) }
643
+ table_header(T.must(rows[0])),
644
+ T.must(rows[1..]).map { |r| table_row(r) }
522
645
  ].join("\n")
523
646
  end
524
647
 
648
+ sig { params(row: T::Array[String]).returns(String) }
525
649
  def table_header(row)
526
650
  [
527
651
  table_row(row),
@@ -529,12 +653,14 @@ module Dependabot
529
653
  ].join("\n")
530
654
  end
531
655
 
656
+ sig { params(row: T::Array[String]).returns(String) }
532
657
  def table_row(row)
533
658
  "| #{row.join(' | ')} |"
534
659
  end
535
660
 
661
+ sig { returns(String) }
536
662
  def metadata_cascades # rubocop:disable Metrics/PerceivedComplexity
537
- return metadata_cascades_for_dep(dependencies.first) if dependencies.one? && !dependency_group
663
+ return metadata_cascades_for_dep(T.must(dependencies.first)) if dependencies.one? && !dependency_group
538
664
 
539
665
  dependencies.map do |dep|
540
666
  msg = if dep.removed?
@@ -555,6 +681,7 @@ module Dependabot
555
681
  end.join
556
682
  end
557
683
 
684
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
558
685
  def metadata_cascades_for_dep(dependency)
559
686
  return "" if dependency.removed?
560
687
 
@@ -567,6 +694,7 @@ module Dependabot
567
694
  ).to_s
568
695
  end
569
696
 
697
+ sig { returns(String) }
570
698
  def ignore_conditions_table
571
699
  # Return an empty string if ignore_conditions is empty
572
700
  return "" if @ignore_conditions.empty?
@@ -580,7 +708,9 @@ module Dependabot
580
708
  return "" if valid_ignore_conditions.empty?
581
709
 
582
710
  # 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)
711
+ sorted_ignore_conditions = valid_ignore_conditions.sort_by do |ic|
712
+ ic["updated_at"].nil? ? T.must(ic["created_at"]) : T.must(ic["updated_at"])
713
+ end.last(20)
584
714
 
585
715
  # Map each condition to a row string
586
716
  table_rows = sorted_ignore_conditions.map do |ic|
@@ -591,6 +721,7 @@ module Dependabot
591
721
  build_table(summary, table_rows)
592
722
  end
593
723
 
724
+ sig { params(summary: String, rows: T::Array[String]).returns(String) }
594
725
  def build_table(summary, rows)
595
726
  table_header = "| Dependency Name | Ignore Conditions |"
596
727
  table_divider = "| --- | --- |"
@@ -607,74 +738,87 @@ module Dependabot
607
738
  end
608
739
  end
609
740
 
741
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
610
742
  def changelog_url(dependency)
611
743
  metadata_finder(dependency).changelog_url
612
744
  end
613
745
 
746
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
614
747
  def commits_url(dependency)
615
748
  metadata_finder(dependency).commits_url
616
749
  end
617
750
 
751
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
618
752
  def homepage_url(dependency)
619
753
  metadata_finder(dependency).homepage_url
620
754
  end
621
755
 
756
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
622
757
  def releases_url(dependency)
623
758
  metadata_finder(dependency).releases_url
624
759
  end
625
760
 
761
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
626
762
  def source_url(dependency)
627
763
  metadata_finder(dependency).source_url
628
764
  end
629
765
 
766
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
630
767
  def upgrade_url(dependency)
631
768
  metadata_finder(dependency).upgrade_guide_url
632
769
  end
633
770
 
771
+ sig { params(dependency: Dependabot::Dependency).returns(Dependabot::MetadataFinders::Base) }
634
772
  def metadata_finder(dependency)
635
- @metadata_finder ||= {}
773
+ @metadata_finder ||= T.let({}, T.nilable(T::Hash[String, Dependabot::MetadataFinders::Base]))
636
774
  @metadata_finder[dependency.name] ||=
637
775
  MetadataFinders
638
776
  .for_package_manager(dependency.package_manager)
639
777
  .new(dependency: dependency, credentials: credentials)
640
778
  end
641
779
 
780
+ sig { returns(Dependabot::PullRequestCreator::PrNamePrefixer) }
642
781
  def pr_name_prefixer
643
782
  @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?
783
+ T.let(
784
+ PrNamePrefixer.new(
785
+ source: source,
786
+ dependencies: dependencies,
787
+ credentials: credentials,
788
+ commit_message_options: commit_message_options,
789
+ security_fix: vulnerabilities_fixed.values.flatten.any?
790
+ ),
791
+ T.nilable(Dependabot::PullRequestCreator::PrNamePrefixer)
650
792
  )
651
793
  end
652
794
 
795
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
653
796
  def old_library_requirement(dependency)
654
797
  old_reqs =
655
- dependency.previous_requirements - dependency.requirements
798
+ T.must(dependency.previous_requirements) - dependency.requirements
656
799
 
657
800
  gemspec =
658
801
  old_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
659
802
  return gemspec.fetch(:requirement) if gemspec
660
803
 
661
- req = old_reqs.first.fetch(:requirement)
804
+ req = T.must(old_reqs.first).fetch(:requirement)
662
805
  return req if req
663
806
 
664
807
  dependency.previous_ref if dependency.ref_changed?
665
808
  end
666
809
 
810
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
667
811
  def new_library_requirement(dependency)
668
812
  updated_reqs =
669
- dependency.requirements - dependency.previous_requirements
813
+ dependency.requirements - T.must(dependency.previous_requirements)
670
814
 
671
815
  gemspec =
672
816
  updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
673
817
  return gemspec.fetch(:requirement) if gemspec
674
818
 
675
- req = updated_reqs.first.fetch(:requirement)
819
+ req = T.must(updated_reqs.first).fetch(:requirement)
676
820
  return req if req
677
- return dependency.new_ref if dependency.ref_changed? && dependency.new_ref
821
+ return T.must(dependency.new_ref) if dependency.ref_changed? && dependency.new_ref
678
822
 
679
823
  raise "No new requirement!"
680
824
  end
@@ -684,6 +828,7 @@ module Dependabot
684
828
  # `requirements_update_strategy`.
685
829
  #
686
830
  # TODO reuse in BranchNamer
831
+ sig { returns(T::Boolean) }
687
832
  def library?
688
833
  # Reject any nested child gemspecs/vendored git dependencies
689
834
  root_files = files.map(&:name)
@@ -693,6 +838,7 @@ module Dependabot
693
838
  dependencies.any? { |d| d.humanized_previous_version.nil? }
694
839
  end
695
840
 
841
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
696
842
  def switching_from_ref_to_release?(dependency)
697
843
  unless dependency.previous_version&.match?(/^[0-9a-f]{40}$/) ||
698
844
  (dependency.previous_version.nil? && dependency.previous_ref)
@@ -702,8 +848,12 @@ module Dependabot
702
848
  Gem::Version.correct?(dependency.version)
703
849
  end
704
850
 
851
+ sig { returns(String) }
705
852
  def package_manager
706
- @package_manager ||= dependencies.first.package_manager
853
+ @package_manager ||= T.let(
854
+ T.must(dependencies.first).package_manager,
855
+ T.nilable(String)
856
+ )
707
857
  end
708
858
  end
709
859
  end