dependabot-common 0.239.0 → 0.241.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +1,29 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
5
+ require "sorbet-runtime"
5
6
  require "dependabot/pull_request_creator"
7
+
6
8
  module Dependabot
7
9
  class PullRequestCreator
10
+ # rubocop:disable Metrics/ClassLength
8
11
  class Labeler
12
+ extend T::Sig
13
+
9
14
  DEPENDENCIES_LABEL_REGEX = %r{^[^/]*dependenc[^/]+$}i
10
15
  DEFAULT_DEPENDENCIES_LABEL = "dependencies"
11
16
  DEFAULT_SECURITY_LABEL = "security"
12
17
 
13
- @package_manager_labels = {}
18
+ @package_manager_labels = T.let({}, T::Hash[String, T::Hash[Symbol, String]])
14
19
 
15
20
  class << self
21
+ extend T::Sig
22
+
23
+ sig { returns(T::Hash[String, T::Hash[Symbol, String]]) }
16
24
  attr_reader :package_manager_labels
17
25
 
26
+ sig { params(package_manager: String).returns(T::Hash[Symbol, String]) }
18
27
  def label_details_for_package_manager(package_manager)
19
28
  label_details = @package_manager_labels[package_manager]
20
29
  return label_details if label_details
@@ -22,11 +31,24 @@ module Dependabot
22
31
  raise "Unsupported package_manager #{package_manager}"
23
32
  end
24
33
 
34
+ sig { params(package_manager: String, label_details: T::Hash[Symbol, String]).void }
25
35
  def register_label_details(package_manager, label_details)
26
36
  @package_manager_labels[package_manager] = label_details
27
37
  end
28
38
  end
29
39
 
40
+ sig do
41
+ params(
42
+ source: Dependabot::Source,
43
+ custom_labels: T.nilable(T::Array[String]),
44
+ credentials: T::Array[T::Hash[String, String]],
45
+ dependencies: T::Array[Dependency],
46
+ includes_security_fixes: T::Boolean,
47
+ label_language: T::Boolean,
48
+ automerge_candidate: T::Boolean
49
+ )
50
+ .void
51
+ end
30
52
  def initialize(source:, custom_labels:, credentials:, dependencies:,
31
53
  includes_security_fixes:, label_language:,
32
54
  automerge_candidate:)
@@ -39,12 +61,14 @@ module Dependabot
39
61
  @automerge_candidate = automerge_candidate
40
62
  end
41
63
 
64
+ sig { void }
42
65
  def create_default_labels_if_required
43
66
  create_default_dependencies_label_if_required
44
67
  create_default_security_label_if_required
45
68
  create_default_language_label_if_required
46
69
  end
47
70
 
71
+ sig { returns(T::Array[String]) }
48
72
  def labels_for_pr
49
73
  [
50
74
  *default_labels_for_pr,
@@ -54,13 +78,14 @@ module Dependabot
54
78
  ].compact.uniq
55
79
  end
56
80
 
81
+ sig { params(pull_request_number: Integer).void }
57
82
  def label_pull_request(pull_request_number)
58
83
  create_default_labels_if_required
59
84
 
60
85
  return if labels_for_pr.none?
61
86
  raise "Only GitHub!" unless source.provider == "github"
62
87
 
63
- github_client_for_source.add_labels_to_an_issue(
88
+ T.unsafe(github_client_for_source).add_labels_to_an_issue(
64
89
  source.repo,
65
90
  pull_request_number,
66
91
  labels_for_pr
@@ -76,20 +101,34 @@ module Dependabot
76
101
 
77
102
  private
78
103
 
79
- attr_reader :source, :custom_labels, :credentials, :dependencies
104
+ sig { returns(Dependabot::Source) }
105
+ attr_reader :source
106
+
107
+ sig { returns(T.nilable(T::Array[String])) }
108
+ attr_reader :custom_labels
80
109
 
110
+ sig { returns(T::Array[T::Hash[String, String]]) }
111
+ attr_reader :credentials
112
+
113
+ sig { returns(T::Array[Dependency]) }
114
+ attr_reader :dependencies
115
+
116
+ sig { returns(T::Boolean) }
81
117
  def label_language?
82
118
  @label_language
83
119
  end
84
120
 
121
+ sig { returns(T::Boolean) }
85
122
  def includes_security_fixes?
86
123
  @includes_security_fixes
87
124
  end
88
125
 
126
+ sig { returns(T::Boolean) }
89
127
  def automerge_candidate?
90
128
  @automerge_candidate
91
129
  end
92
130
 
131
+ sig { returns(T.nilable(String)) }
93
132
  def update_type
94
133
  return unless dependencies.any?(&:previous_version)
95
134
 
@@ -101,9 +140,10 @@ module Dependabot
101
140
  end
102
141
  end
103
142
 
143
+ sig { returns(Integer) }
104
144
  def precision
105
- dependencies.map do |dep|
106
- new_version_parts = version(dep).split(/[.+]/)
145
+ T.must(dependencies.map do |dep|
146
+ new_version_parts = T.must(version(dep)).split(/[.+]/)
107
147
  old_version_parts = previous_version(dep)&.split(/[.+]/) || []
108
148
  all_parts = new_version_parts.first(3) + old_version_parts.first(3)
109
149
  # rubocop:disable Performance/RedundantEqualityComparisonBlock
@@ -113,10 +153,11 @@ module Dependabot
113
153
  next 2 if new_version_parts[1] != old_version_parts[1]
114
154
 
115
155
  3
116
- end.min
156
+ end.min)
117
157
  end
118
158
 
119
159
  # rubocop:disable Metrics/PerceivedComplexity
160
+ sig { params(dep: Dependabot::Dependency).returns(T.nilable(String)) }
120
161
  def version(dep)
121
162
  return dep.version if version_class.correct?(dep.version)
122
163
 
@@ -134,12 +175,13 @@ module Dependabot
134
175
  # rubocop:enable Metrics/PerceivedComplexity
135
176
 
136
177
  # rubocop:disable Metrics/PerceivedComplexity
178
+ sig { params(dep: Dependabot::Dependency).returns(T.nilable(String)) }
137
179
  def previous_version(dep)
138
180
  version_str = dep.previous_version
139
181
  return version_str if version_class.correct?(version_str)
140
182
 
141
- source = dep.previous_requirements
142
- .find { |r| r.fetch(:source) }&.fetch(:source)
183
+ source = T.must(dep.previous_requirements)
184
+ .find { |r| r.fetch(:source) }&.fetch(:source)
143
185
  type = source&.fetch("type", nil) || source&.fetch(:type)
144
186
  return version_str unless type == "git"
145
187
 
@@ -152,6 +194,7 @@ module Dependabot
152
194
  end
153
195
  # rubocop:enable Metrics/PerceivedComplexity
154
196
 
197
+ sig { returns(T.nilable(T::Array[String])) }
155
198
  def create_default_dependencies_label_if_required
156
199
  return if custom_labels
157
200
  return if dependencies_label_exists?
@@ -159,6 +202,7 @@ module Dependabot
159
202
  create_dependencies_label
160
203
  end
161
204
 
205
+ sig { returns(T.nilable(T::Array[String])) }
162
206
  def create_default_security_label_if_required
163
207
  return unless includes_security_fixes?
164
208
  return if security_label_exists?
@@ -166,6 +210,7 @@ module Dependabot
166
210
  create_security_label
167
211
  end
168
212
 
213
+ sig { returns(T.nilable(T::Array[String])) }
169
214
  def create_default_language_label_if_required
170
215
  return unless label_language?
171
216
  return if custom_labels
@@ -174,12 +219,13 @@ module Dependabot
174
219
  create_language_label
175
220
  end
176
221
 
222
+ sig { returns(T::Array[String]) }
177
223
  def default_labels_for_pr
178
224
  if custom_labels
179
225
  # Azure does not have centralised labels
180
- return custom_labels if source.provider == "azure"
226
+ return T.must(custom_labels) if source.provider == "azure"
181
227
 
182
- custom_labels & labels
228
+ T.must(custom_labels) & labels
183
229
  else
184
230
  [
185
231
  default_dependencies_label,
@@ -188,26 +234,31 @@ module Dependabot
188
234
  end
189
235
  end
190
236
 
191
- # Find the exact match first and then fallback to *dependenc* label
237
+ # Find the exact match first and then fallback to *dependency* label
238
+ sig { returns(T.nilable(String)) }
192
239
  def default_dependencies_label
193
240
  labels.find { |l| l == DEFAULT_DEPENDENCIES_LABEL } ||
194
241
  labels.find { |l| l.match?(DEPENDENCIES_LABEL_REGEX) }
195
242
  end
196
243
 
244
+ sig { returns(T::Boolean) }
197
245
  def dependencies_label_exists?
198
246
  labels.any? { |l| l.match?(DEPENDENCIES_LABEL_REGEX) }
199
247
  end
200
248
 
249
+ sig { returns(T::Boolean) }
201
250
  def security_label_exists?
202
251
  !security_label.nil?
203
252
  end
204
253
 
205
254
  # Find the exact match first and then fallback to * security* label
255
+ sig { returns(T.nilable(String)) }
206
256
  def security_label
207
257
  labels.find { |l| l == DEFAULT_SECURITY_LABEL } ||
208
258
  labels.find { |l| l.match?(/security/i) }
209
259
  end
210
260
 
261
+ sig { returns(T::Boolean) }
211
262
  def label_update_type?
212
263
  # If a `skip-release` label exists then this repo is likely to be using
213
264
  # an auto-releasing service (like auto). We don't want to hijack that
@@ -218,46 +269,54 @@ module Dependabot
218
269
  (%w(major minor patch) - labels.map(&:downcase)).empty?
219
270
  end
220
271
 
272
+ sig { returns(T.nilable(String)) }
221
273
  def semver_label
222
274
  return unless update_type
223
275
 
224
276
  labels.find { |l| l.downcase == update_type.to_s }
225
277
  end
226
278
 
279
+ sig { returns(T.nilable(String)) }
227
280
  def automerge_label
228
- labels.find { |l| l.casecmp("automerge").zero? }
281
+ labels.find { |l| l.casecmp("automerge")&.zero? }
229
282
  end
230
283
 
284
+ sig { returns(T::Boolean) }
231
285
  def language_label_exists?
232
286
  !language_label.nil?
233
287
  end
234
288
 
289
+ sig { returns(T.nilable(String)) }
235
290
  def language_label
236
291
  label_name =
237
292
  self.class.label_details_for_package_manager(package_manager)
238
293
  .fetch(:name)
239
- labels.find { |l| l.casecmp(label_name).zero? }
294
+ labels.find { |l| l.casecmp(label_name)&.zero? }
240
295
  end
241
296
 
297
+ sig { returns(T::Array[String]) }
242
298
  def labels
243
- @labels ||=
299
+ @labels ||= T.let(
244
300
  case source.provider
245
301
  when "github" then fetch_github_labels
246
302
  when "gitlab" then fetch_gitlab_labels
247
303
  when "azure" then fetch_azure_labels
248
304
  else raise "Unsupported provider #{source.provider}"
249
- end
305
+ end,
306
+ T.nilable(T::Array[String])
307
+ )
250
308
  end
251
309
 
310
+ sig { returns(T::Array[String]) }
252
311
  def fetch_github_labels
253
312
  client = github_client_for_source
254
313
 
255
314
  labels =
256
- client
257
- .labels(source.repo, per_page: 100)
258
- .map(&:name)
315
+ T.unsafe(client)
316
+ .labels(source.repo, per_page: 100)
317
+ .map(&:name)
259
318
 
260
- next_link = client.last_response.rels[:next]
319
+ next_link = T.unsafe(client).last_response.rels[:next]
261
320
 
262
321
  while next_link
263
322
  next_page = next_link.get
@@ -268,13 +327,15 @@ module Dependabot
268
327
  labels
269
328
  end
270
329
 
330
+ sig { returns(T::Array[String]) }
271
331
  def fetch_gitlab_labels
272
- gitlab_client_for_source
273
- .labels(source.repo, per_page: 100)
274
- .auto_paginate
275
- .map(&:name)
332
+ T.unsafe(gitlab_client_for_source)
333
+ .labels(source.repo, per_page: 100)
334
+ .auto_paginate
335
+ .map(&:name)
276
336
  end
277
337
 
338
+ sig { returns(T::Array[String]) }
278
339
  def fetch_azure_labels
279
340
  language_name =
280
341
  self.class.label_details_for_package_manager(package_manager)
@@ -288,6 +349,7 @@ module Dependabot
288
349
  ].uniq
289
350
  end
290
351
 
352
+ sig { returns(T.nilable(T::Array[String])) }
291
353
  def create_dependencies_label
292
354
  case source.provider
293
355
  when "github" then create_github_dependencies_label
@@ -297,6 +359,7 @@ module Dependabot
297
359
  end
298
360
  end
299
361
 
362
+ sig { returns(T.nilable(T::Array[String])) }
300
363
  def create_security_label
301
364
  case source.provider
302
365
  when "github" then create_github_security_label
@@ -306,6 +369,7 @@ module Dependabot
306
369
  end
307
370
  end
308
371
 
372
+ sig { returns(T.nilable(T::Array[String])) }
309
373
  def create_language_label
310
374
  case source.provider
311
375
  when "github" then create_github_language_label
@@ -315,8 +379,9 @@ module Dependabot
315
379
  end
316
380
  end
317
381
 
382
+ sig { returns(T::Array[String]) }
318
383
  def create_github_dependencies_label
319
- github_client_for_source.add_label(
384
+ T.unsafe(github_client_for_source).add_label(
320
385
  source.repo, DEFAULT_DEPENDENCIES_LABEL, "0366d6",
321
386
  description: "Pull requests that update a dependency file",
322
387
  accept: "application/vnd.github.symmetra-preview+json"
@@ -328,16 +393,18 @@ module Dependabot
328
393
  @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq
329
394
  end
330
395
 
396
+ sig { returns(T::Array[String]) }
331
397
  def create_gitlab_dependencies_label
332
- gitlab_client_for_source.create_label(
398
+ T.unsafe(gitlab_client_for_source).create_label(
333
399
  source.repo, DEFAULT_DEPENDENCIES_LABEL, "#0366d6",
334
400
  description: "Pull requests that update a dependency file"
335
401
  )
336
402
  @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq
337
403
  end
338
404
 
405
+ sig { returns(T::Array[String]) }
339
406
  def create_github_security_label
340
- github_client_for_source.add_label(
407
+ T.unsafe(github_client_for_source).add_label(
341
408
  source.repo, DEFAULT_SECURITY_LABEL, "ee0701",
342
409
  description: "Pull requests that address a security vulnerability",
343
410
  accept: "application/vnd.github.symmetra-preview+json"
@@ -349,18 +416,20 @@ module Dependabot
349
416
  @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq
350
417
  end
351
418
 
419
+ sig { returns(T.nilable(T::Array[String])) }
352
420
  def create_gitlab_security_label
353
- gitlab_client_for_source.create_label(
421
+ T.unsafe(gitlab_client_for_source).create_label(
354
422
  source.repo, DEFAULT_SECURITY_LABEL, "#ee0701",
355
423
  description: "Pull requests that address a security vulnerability"
356
424
  )
357
425
  @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq
358
426
  end
359
427
 
428
+ sig { returns(T::Array[String]) }
360
429
  def create_github_language_label
361
430
  label = self.class.label_details_for_package_manager(package_manager)
362
431
  language_name = label.fetch(:name)
363
- github_client_for_source.add_label(
432
+ T.unsafe(github_client_for_source).add_label(
364
433
  source.repo,
365
434
  language_name,
366
435
  label.fetch(:colour),
@@ -371,18 +440,20 @@ module Dependabot
371
440
  rescue Octokit::UnprocessableEntity => e
372
441
  raise unless e.errors.first.fetch(:code) == "already_exists"
373
442
 
374
- @labels = [*@labels, language_name].uniq
443
+ @labels = [*@labels, language_name].uniq.compact
375
444
  end
376
445
 
446
+ sig { params(language: String).returns(String) }
377
447
  def default_description_for(language)
378
448
  "Pull requests that update #{language.capitalize} code"
379
449
  end
380
450
 
451
+ sig { returns(T::Array[String]) }
381
452
  def create_gitlab_language_label
382
453
  language_name =
383
454
  self.class.label_details_for_package_manager(package_manager)
384
455
  .fetch(:name)
385
- gitlab_client_for_source.create_label(
456
+ T.unsafe(gitlab_client_for_source).create_label(
386
457
  source.repo,
387
458
  language_name,
388
459
  "#" + self.class.label_details_for_package_manager(package_manager)
@@ -391,29 +462,38 @@ module Dependabot
391
462
  @labels = [*@labels, language_name].uniq
392
463
  end
393
464
 
465
+ sig { returns(Dependabot::Clients::GithubWithRetries) }
394
466
  def github_client_for_source
395
- @github_client_for_source ||=
467
+ @github_client_for_source ||= T.let(
396
468
  Dependabot::Clients::GithubWithRetries.for_source(
397
469
  source: source,
398
470
  credentials: credentials
399
- )
471
+ ),
472
+ T.nilable(Dependabot::Clients::GithubWithRetries)
473
+ )
400
474
  end
401
475
 
476
+ sig { returns(Dependabot::Clients::GitlabWithRetries) }
402
477
  def gitlab_client_for_source
403
- @gitlab_client_for_source ||=
478
+ @gitlab_client_for_source ||= T.let(
404
479
  Dependabot::Clients::GitlabWithRetries.for_source(
405
480
  source: source,
406
481
  credentials: credentials
407
- )
482
+ ),
483
+ T.nilable(Dependabot::Clients::GitlabWithRetries)
484
+ )
408
485
  end
409
486
 
487
+ sig { returns(String) }
410
488
  def package_manager
411
- @package_manager ||= dependencies.first.package_manager
489
+ @package_manager ||= T.let(T.must(dependencies.first).package_manager, T.nilable(String))
412
490
  end
413
491
 
492
+ sig { returns(T.class_of(Dependabot::Version)) }
414
493
  def version_class
415
494
  Utils.version_class_for_package_manager(package_manager)
416
495
  end
417
496
  end
497
+ # rubocop:enable Metrics/ClassLength
418
498
  end
419
499
  end