dependabot-common 0.236.0 → 0.238.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +3 -3
  3. data/lib/dependabot/clients/codecommit.rb +1 -0
  4. data/lib/dependabot/config/file.rb +17 -6
  5. data/lib/dependabot/config/update_config.rb +23 -5
  6. data/lib/dependabot/dependency.rb +137 -27
  7. data/lib/dependabot/dependency_file.rb +84 -14
  8. data/lib/dependabot/dependency_group.rb +29 -5
  9. data/lib/dependabot/errors.rb +335 -13
  10. data/lib/dependabot/file_fetchers/base.rb +227 -93
  11. data/lib/dependabot/file_updaters/base.rb +1 -1
  12. data/lib/dependabot/git_commit_checker.rb +6 -0
  13. data/lib/dependabot/git_metadata_fetcher.rb +58 -20
  14. data/lib/dependabot/git_ref.rb +71 -0
  15. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +13 -6
  16. data/lib/dependabot/pull_request_creator/github.rb +11 -8
  17. data/lib/dependabot/pull_request_creator/message.rb +21 -2
  18. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +37 -16
  19. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +4 -2
  20. data/lib/dependabot/pull_request_creator/message_builder.rb +54 -4
  21. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +10 -4
  22. data/lib/dependabot/shared_helpers.rb +117 -33
  23. data/lib/dependabot/simple_instrumentor.rb +22 -3
  24. data/lib/dependabot/source.rb +65 -17
  25. data/lib/dependabot/update_checkers/version_filters.rb +12 -1
  26. data/lib/dependabot/utils.rb +21 -2
  27. data/lib/dependabot/workspace/base.rb +42 -7
  28. data/lib/dependabot/workspace/change_attempt.rb +31 -3
  29. data/lib/dependabot/workspace/git.rb +34 -4
  30. data/lib/dependabot/workspace.rb +16 -2
  31. data/lib/dependabot.rb +1 -1
  32. metadata +38 -9
@@ -1,20 +1,234 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "dependabot/utils"
5
6
 
6
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
+
7
217
  class DependabotError < StandardError
218
+ extend T::Sig
219
+
8
220
  BASIC_AUTH_REGEX = %r{://(?<auth>[^:@]*:[^@%\s/]+(@|%40))}
9
221
  # Remove any path segment from fury.io sources
10
222
  FURY_IO_PATH_REGEX = %r{fury\.io/(?<path>.+)}
11
223
 
224
+ sig { params(message: T.any(T.nilable(String), MatchData)).void }
12
225
  def initialize(message = nil)
13
226
  super(sanitize_message(message))
14
227
  end
15
228
 
16
229
  private
17
230
 
231
+ sig { params(message: T.any(T.nilable(String), MatchData)).returns(T.any(T.nilable(String), MatchData)) }
18
232
  def sanitize_message(message)
19
233
  return message unless message.is_a?(String)
20
234
 
@@ -26,18 +240,25 @@ module Dependabot
26
240
  filter_sensitive_data(message)
27
241
  end
28
242
 
243
+ sig { params(message: String).returns(String) }
29
244
  def filter_sensitive_data(message)
30
245
  replace_capture_groups(message, BASIC_AUTH_REGEX, "")
31
246
  end
32
247
 
248
+ sig { params(source: String).returns(String) }
33
249
  def sanitize_source(source)
34
250
  source = filter_sensitive_data(source)
35
251
  replace_capture_groups(source, FURY_IO_PATH_REGEX, "<redacted>")
36
252
  end
37
253
 
254
+ sig do
255
+ params(
256
+ string: String,
257
+ regex: Regexp,
258
+ replacement: String
259
+ ).returns(String)
260
+ end
38
261
  def replace_capture_groups(string, regex, replacement)
39
- return string unless string.is_a?(String)
40
-
41
262
  string.scan(regex).flatten.compact.reduce(string) do |original_msg, match|
42
263
  original_msg.gsub(match, replacement)
43
264
  end
@@ -55,8 +276,12 @@ module Dependabot
55
276
  #####################
56
277
 
57
278
  class DirectoryNotFound < DependabotError
279
+ extend T::Sig
280
+
281
+ sig { returns(String) }
58
282
  attr_reader :directory_name
59
283
 
284
+ sig { params(directory_name: String, msg: T.nilable(String)).void }
60
285
  def initialize(directory_name, msg = nil)
61
286
  @directory_name = directory_name
62
287
  super(msg)
@@ -64,8 +289,12 @@ module Dependabot
64
289
  end
65
290
 
66
291
  class BranchNotFound < DependabotError
292
+ extend T::Sig
293
+
294
+ sig { returns(T.nilable(String)) }
67
295
  attr_reader :branch_name
68
296
 
297
+ sig { params(branch_name: T.nilable(String), msg: T.nilable(String)).void }
69
298
  def initialize(branch_name, msg = nil)
70
299
  @branch_name = branch_name
71
300
  super(msg)
@@ -73,8 +302,12 @@ module Dependabot
73
302
  end
74
303
 
75
304
  class RepoNotFound < DependabotError
305
+ extend T::Sig
306
+
307
+ sig { returns(T.any(Dependabot::Source, String)) }
76
308
  attr_reader :source
77
309
 
310
+ sig { params(source: T.any(Dependabot::Source, String), msg: T.nilable(String)).void }
78
311
  def initialize(source, msg = nil)
79
312
  @source = source
80
313
  super(msg)
@@ -85,9 +318,50 @@ module Dependabot
85
318
  # File level errors #
86
319
  #####################
87
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
+
88
346
  class ToolVersionNotSupported < DependabotError
89
- attr_reader :tool_name, :detected_version, :supported_versions
347
+ extend T::Sig
348
+
349
+ sig { returns(String) }
350
+ attr_reader :tool_name
90
351
 
352
+ sig { returns(String) }
353
+ attr_reader :detected_version
354
+
355
+ sig { returns(String) }
356
+ attr_reader :supported_versions
357
+
358
+ sig do
359
+ params(
360
+ tool_name: String,
361
+ detected_version: String,
362
+ supported_versions: String
363
+ ).void
364
+ end
91
365
  def initialize(tool_name, detected_version, supported_versions)
92
366
  @tool_name = tool_name
93
367
  @detected_version = detected_version
@@ -100,6 +374,9 @@ module Dependabot
100
374
  end
101
375
 
102
376
  class DependencyFileNotFound < DependabotError
377
+ extend T::Sig
378
+
379
+ sig { returns(String) }
103
380
  attr_reader :file_path
104
381
 
105
382
  def initialize(file_path, msg = nil)
@@ -107,31 +384,39 @@ module Dependabot
107
384
  super(msg || "#{file_path} not found")
108
385
  end
109
386
 
387
+ sig { returns(String) }
110
388
  def file_name
111
- file_path.split("/").last
389
+ T.must(file_path.split("/").last)
112
390
  end
113
391
 
392
+ sig { returns(String) }
114
393
  def directory
115
394
  # Directory should always start with a `/`
116
- file_path.split("/")[0..-2].join("/").sub(%r{^/*}, "/")
395
+ T.must(file_path.split("/")[0..-2]).join("/").sub(%r{^/*}, "/")
117
396
  end
118
397
  end
119
398
 
120
399
  class DependencyFileNotParseable < DependabotError
400
+ extend T::Sig
401
+
402
+ sig { returns(String) }
121
403
  attr_reader :file_path
122
404
 
405
+ sig { params(file_path: String, msg: T.nilable(String)).void }
123
406
  def initialize(file_path, msg = nil)
124
407
  @file_path = file_path
125
408
  super(msg || "#{file_path} not parseable")
126
409
  end
127
410
 
411
+ sig { returns(String) }
128
412
  def file_name
129
- file_path.split("/").last
413
+ T.must(file_path.split("/").last)
130
414
  end
131
415
 
416
+ sig { returns(String) }
132
417
  def directory
133
418
  # Directory should always start with a `/`
134
- file_path.split("/")[0..-2].join("/").sub(%r{^/*}, "/")
419
+ T.must(file_path.split("/")[0..-2]).join("/").sub(%r{^/*}, "/")
135
420
  end
136
421
  end
137
422
 
@@ -144,10 +429,13 @@ module Dependabot
144
429
  #######################
145
430
 
146
431
  class PrivateSourceAuthenticationFailure < DependabotError
432
+ extend T::Sig
433
+
434
+ sig { returns(String) }
147
435
  attr_reader :source
148
436
 
149
437
  def initialize(source)
150
- @source = sanitize_source(source)
438
+ @source = T.let(sanitize_source(source), String)
151
439
  msg = "The following source could not be reached as it requires " \
152
440
  "authentication (and any provided details were invalid or lacked " \
153
441
  "the required permissions): #{@source}"
@@ -156,26 +444,38 @@ module Dependabot
156
444
  end
157
445
 
158
446
  class PrivateSourceTimedOut < DependabotError
447
+ extend T::Sig
448
+
449
+ sig { returns(String) }
159
450
  attr_reader :source
160
451
 
452
+ sig { params(source: String).void }
161
453
  def initialize(source)
162
- @source = sanitize_source(source)
454
+ @source = T.let(sanitize_source(source), String)
163
455
  super("The following source timed out: #{@source}")
164
456
  end
165
457
  end
166
458
 
167
459
  class PrivateSourceCertificateFailure < DependabotError
460
+ extend T::Sig
461
+
462
+ sig { returns(String) }
168
463
  attr_reader :source
169
464
 
465
+ sig { params(source: String).void }
170
466
  def initialize(source)
171
- @source = sanitize_source(source)
467
+ @source = T.let(sanitize_source(source), String)
172
468
  super("Could not verify the SSL certificate for #{@source}")
173
469
  end
174
470
  end
175
471
 
176
472
  class MissingEnvironmentVariable < DependabotError
473
+ extend T::Sig
474
+
475
+ sig { returns(String) }
177
476
  attr_reader :environment_variable
178
477
 
478
+ sig { params(environment_variable: String).void }
179
479
  def initialize(environment_variable)
180
480
  @environment_variable = environment_variable
181
481
  super("Missing environment variable #{@environment_variable}")
@@ -191,11 +491,15 @@ module Dependabot
191
491
  ###########################
192
492
 
193
493
  class GitDependenciesNotReachable < DependabotError
494
+ extend T::Sig
495
+
496
+ sig { returns(T::Array[String]) }
194
497
  attr_reader :dependency_urls
195
498
 
499
+ sig { params(dependency_urls: T.any(String, T::Array[String])).void }
196
500
  def initialize(*dependency_urls)
197
501
  @dependency_urls =
198
- dependency_urls.flatten.map { |uri| filter_sensitive_data(uri) }
502
+ T.let(dependency_urls.flatten.map { |uri| filter_sensitive_data(uri) }, T::Array[String])
199
503
 
200
504
  msg = "The following git URLs could not be retrieved: " \
201
505
  "#{@dependency_urls.join(', ')}"
@@ -204,8 +508,12 @@ module Dependabot
204
508
  end
205
509
 
206
510
  class GitDependencyReferenceNotFound < DependabotError
511
+ extend T::Sig
512
+
513
+ sig { returns(String) }
207
514
  attr_reader :dependency
208
515
 
516
+ sig { params(dependency: String).void }
209
517
  def initialize(dependency)
210
518
  @dependency = dependency
211
519
 
@@ -216,10 +524,14 @@ module Dependabot
216
524
  end
217
525
 
218
526
  class PathDependenciesNotReachable < DependabotError
527
+ extend T::Sig
528
+
529
+ sig { returns(T::Array[String]) }
219
530
  attr_reader :dependencies
220
531
 
532
+ sig { params(dependencies: T.any(String, T::Array[String])).void }
221
533
  def initialize(*dependencies)
222
- @dependencies = dependencies.flatten
534
+ @dependencies = T.let(dependencies.flatten, T::Array[String])
223
535
  msg = "The following path based dependencies could not be retrieved: " \
224
536
  "#{@dependencies.join(', ')}"
225
537
  super(msg)
@@ -227,8 +539,18 @@ module Dependabot
227
539
  end
228
540
 
229
541
  class GoModulePathMismatch < DependabotError
230
- attr_reader :go_mod, :declared_path, :discovered_path
542
+ extend T::Sig
543
+
544
+ sig { returns(String) }
545
+ attr_reader :go_mod
546
+
547
+ sig { returns(String) }
548
+ attr_reader :declared_path
549
+
550
+ sig { returns(String) }
551
+ attr_reader :discovered_path
231
552
 
553
+ sig { params(go_mod: String, declared_path: String, discovered_path: String).void }
232
554
  def initialize(go_mod, declared_path, discovered_path)
233
555
  @go_mod = go_mod
234
556
  @declared_path = declared_path