dependabot-common 0.392.0 → 0.394.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +42 -22
  3. data/lib/dependabot/clients/bitbucket.rb +52 -26
  4. data/lib/dependabot/clients/bitbucket_with_retries.rb +11 -12
  5. data/lib/dependabot/clients/codecommit.rb +73 -53
  6. data/lib/dependabot/clients/github_resource_parser.rb +70 -0
  7. data/lib/dependabot/clients/github_with_retries.rb +63 -23
  8. data/lib/dependabot/clients/gitlab_with_retries.rb +19 -17
  9. data/lib/dependabot/clients/json_response_parser.rb +103 -0
  10. data/lib/dependabot/command_helpers.rb +79 -11
  11. data/lib/dependabot/credential.rb +12 -3
  12. data/lib/dependabot/errors.rb +4 -1
  13. data/lib/dependabot/file_fetchers/base.rb +199 -151
  14. data/lib/dependabot/file_filtering.rb +0 -2
  15. data/lib/dependabot/file_updaters/vendor_updater.rb +8 -2
  16. data/lib/dependabot/git_tag_details.rb +8 -14
  17. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +211 -110
  18. data/lib/dependabot/metadata_finders/base/commit_response_parser.rb +68 -0
  19. data/lib/dependabot/metadata_finders/base/commits_finder.rb +44 -38
  20. data/lib/dependabot/package/npm_package_manager_config.rb +72 -0
  21. data/lib/dependabot/package/npm_registry_package.rb +186 -0
  22. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  23. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  24. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  25. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  26. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  27. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  28. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  29. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  30. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  31. data/lib/dependabot/pull_request_updater.rb +3 -3
  32. data/lib/dependabot/shared_helpers.rb +77 -32
  33. data/lib/dependabot/update_checkers/base.rb +9 -5
  34. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  35. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  36. data/lib/dependabot/workspace/git.rb +35 -4
  37. data/lib/dependabot.rb +1 -1
  38. metadata +10 -4
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -10,6 +10,7 @@ require "dependabot/clients/github_with_retries"
10
10
  require "dependabot/clients/gitlab_with_retries"
11
11
  require "dependabot/pull_request_creator"
12
12
  require "dependabot/pull_request_creator/commit_message_options"
13
+ require "dependabot/errors"
13
14
 
14
15
  module Dependabot
15
16
  class PullRequestCreator
@@ -27,6 +28,13 @@ module Dependabot
27
28
  truck twisted_rightwards_arrows whale wheelchair white_check_mark wrench zap
28
29
  ).freeze
29
30
 
31
+ class RecentCommit < T::ImmutableStruct
32
+ const :message, T.nilable(String)
33
+ const :author_email, T.nilable(String), default: nil
34
+ const :author_name, T.nilable(String), default: nil
35
+ const :author_type, T.nilable(String), default: nil
36
+ end
37
+
30
38
  sig do
31
39
  params(
32
40
  source: Dependabot::Source,
@@ -52,6 +60,11 @@ module Dependabot
52
60
  commit_message_options && CommitMessageOptions.from_hash(commit_message_options),
53
61
  T.nilable(CommitMessageOptions)
54
62
  )
63
+ @recent_github_commits = T.let(nil, T.nilable(T::Array[RecentCommit]))
64
+ @recent_gitlab_commits = T.let(nil, T.nilable(T::Array[RecentCommit]))
65
+ @recent_azure_commits = T.let(nil, T.nilable(T::Array[RecentCommit]))
66
+ @recent_bitbucket_commits = T.let(nil, T.nilable(T::Array[RecentCommit]))
67
+ @recent_codecommit_commits = T.let(nil, T.nilable(T::Array[RecentCommit]))
55
68
  end
56
69
 
57
70
  sig { returns(String) }
@@ -334,58 +347,46 @@ module Dependabot
334
347
  sig { returns(T::Array[String]) }
335
348
  def recent_github_commit_messages
336
349
  recent_github_commits
337
- .reject { |c| c.author&.type == "Bot" }
338
- .reject { |c| c.commit&.message&.start_with?("Merge") }
339
- .map(&:commit)
350
+ .reject { |commit| commit.author_type == "Bot" }
351
+ .reject { |commit| commit.message&.start_with?("Merge") }
340
352
  .filter_map(&:message)
341
353
  .map(&:strip)
342
354
  end
343
355
 
344
356
  sig { returns(T::Array[String]) }
345
357
  def recent_gitlab_commit_messages
346
- @recent_gitlab_commit_messages ||=
347
- gitlab_client_for_source.commits(source.repo)
348
-
349
- @recent_gitlab_commit_messages
350
- .reject { |c| c.author_email == dependabot_email }
351
- .reject { |c| c.message&.start_with?("merge !") }
358
+ recent_gitlab_commits
359
+ .reject { |commit| commit.author_email == dependabot_email }
360
+ .reject { |commit| commit.message&.start_with?("merge !") }
352
361
  .filter_map(&:message)
353
362
  .map(&:strip)
354
363
  end
355
364
 
356
365
  sig { returns(T::Array[String]) }
357
366
  def recent_azure_commit_messages
358
- @recent_azure_commit_messages ||=
359
- azure_client_for_source.commits
360
-
361
- @recent_azure_commit_messages
362
- .reject { |c| azure_commit_author_email(c) == dependabot_email }
363
- .reject { |c| c.fetch("comment")&.start_with?("Merge") }
364
- .filter_map { |c| c.fetch("comment") }
367
+ recent_azure_commits
368
+ .reject { |commit| commit.author_email == dependabot_email }
369
+ .reject { |commit| commit.message&.start_with?("Merge") }
370
+ .filter_map(&:message)
365
371
  .map(&:strip)
366
372
  end
367
373
 
368
374
  sig { returns(T::Array[String]) }
369
375
  def recent_bitbucket_commit_messages
370
- @recent_bitbucket_commit_messages ||=
371
- bitbucket_client_for_source.commits(source.repo)
372
-
373
- @recent_bitbucket_commit_messages
374
- .reject { |c| bitbucket_commit_author_email(c) == dependabot_email }
375
- .filter_map { |c| c.fetch("message", nil) }
376
+ recent_bitbucket_commits
377
+ .reject { |commit| commit.author_email == dependabot_email }
378
+ .filter_map(&:message)
376
379
  .reject { |m| m.start_with?("Merge") }
377
380
  .map(&:strip)
378
381
  end
379
382
 
380
383
  sig { returns(T::Array[String]) }
381
384
  def recent_codecommit_commit_messages
382
- @recent_codecommit_commit_messages ||=
383
- T.unsafe(codecommit_client_for_source).commits
384
- @recent_codecommit_commit_messages.commits
385
- .reject { |c| c.author.email == dependabot_email }
386
- .reject { |c| c.message&.start_with?("Merge") }
387
- .filter_map(&:message)
388
- .map(&:strip)
385
+ recent_codecommit_commits
386
+ .reject { |commit| commit.author_email == dependabot_email }
387
+ .reject { |commit| commit.message&.start_with?("Merge") }
388
+ .filter_map(&:message)
389
+ .map(&:strip)
389
390
  end
390
391
 
391
392
  sig { returns(T.nilable(String)) }
@@ -417,89 +418,189 @@ module Dependabot
417
418
  sig { returns(T.nilable(String)) }
418
419
  def last_github_dependabot_commit_message
419
420
  recent_github_commits
420
- .reject { |c| c.commit&.message&.start_with?("Merge") }
421
- .find { |c| c.commit.author&.name&.include?("dependabot") }
422
- &.commit
421
+ .reject { |commit| commit.message&.start_with?("Merge") }
422
+ .find { |commit| commit.author_name&.include?("dependabot") }
423
423
  &.message
424
424
  &.strip
425
425
  end
426
426
 
427
- sig { returns(T.untyped) }
427
+ sig { returns(T::Array[RecentCommit]) }
428
428
  def recent_github_commits
429
429
  @recent_github_commits ||=
430
- T.let(
431
- github_client_for_source.commits(source.repo, per_page: 100),
432
- T.untyped
433
- )
430
+ github_client_for_source
431
+ .commits(source.repo, per_page: 100)
432
+ .map { |commit| parse_github_commit(commit) }
434
433
  rescue Octokit::Conflict, Octokit::NotFound
435
434
  @recent_github_commits ||= []
436
435
  end
437
436
 
438
437
  sig { returns(T.nilable(String)) }
439
438
  def last_gitlab_dependabot_commit_message
440
- @recent_gitlab_commit_messages ||=
441
- T.let(
442
- gitlab_client_for_source.commits(source.repo),
443
- T.untyped
444
- )
445
-
446
- @recent_gitlab_commit_messages
447
- .find { |c| c.author_email == dependabot_email }
439
+ recent_gitlab_commits
440
+ .find { |commit| commit.author_email == dependabot_email }
448
441
  &.message
449
442
  &.strip
450
443
  end
451
444
 
452
445
  sig { returns(T.nilable(String)) }
453
446
  def last_azure_dependabot_commit_message
454
- @recent_azure_commit_messages ||=
455
- T.let(
456
- azure_client_for_source.commits,
457
- T.untyped
458
- )
459
-
460
- @recent_azure_commit_messages
461
- .find { |c| azure_commit_author_email(c) == dependabot_email }
447
+ recent_azure_commits
448
+ .find { |commit| commit.author_email == dependabot_email }
462
449
  &.message
463
450
  &.strip
464
451
  end
465
452
 
466
453
  sig { returns(T.nilable(String)) }
467
454
  def last_bitbucket_dependabot_commit_message
468
- @recent_bitbucket_commit_messages ||=
469
- T.let(
470
- bitbucket_client_for_source.commits(source.repo),
471
- T.untyped
472
- )
473
-
474
- @recent_bitbucket_commit_messages
475
- .find { |c| bitbucket_commit_author_email(c) == dependabot_email }
476
- &.fetch("message", nil)
455
+ recent_bitbucket_commits
456
+ .find { |commit| commit.author_email == dependabot_email }
457
+ &.message
477
458
  &.strip
478
459
  end
479
460
 
480
461
  sig { returns(T.nilable(String)) }
481
462
  def last_codecommit_dependabot_commit_message
482
- @recent_codecommit_commit_messages ||=
483
- T.let(
484
- codecommit_client_for_source.commits(source.repo),
485
- T.untyped
463
+ recent_codecommit_commits
464
+ .find { |commit| commit.author_email == dependabot_email }
465
+ &.message
466
+ &.strip
467
+ end
468
+
469
+ sig { returns(T::Array[RecentCommit]) }
470
+ def recent_gitlab_commits
471
+ @recent_gitlab_commits ||= gitlab_client_for_source
472
+ .commits(source.repo)
473
+ .map { |commit| parse_gitlab_commit(commit) }
474
+ end
475
+
476
+ sig { returns(T::Array[RecentCommit]) }
477
+ def recent_azure_commits
478
+ @recent_azure_commits ||= azure_client_for_source.commits.map { |commit| parse_azure_commit(commit) }
479
+ end
480
+
481
+ sig { returns(T::Array[RecentCommit]) }
482
+ def recent_bitbucket_commits
483
+ @recent_bitbucket_commits ||= bitbucket_client_for_source
484
+ .commits(source.repo)
485
+ .map { |commit| parse_bitbucket_commit(commit) }
486
+ end
487
+
488
+ sig { returns(T::Array[RecentCommit]) }
489
+ def recent_codecommit_commits
490
+ return @recent_codecommit_commits if @recent_codecommit_commits
491
+
492
+ response = codecommit_client_for_source.commits(source.repo)
493
+ output = T.cast(response.data, Aws::CodeCommit::Types::BatchGetCommitsOutput)
494
+ @recent_codecommit_commits = (output.commits || []).map do |commit|
495
+ RecentCommit.new(
496
+ message: commit.message,
497
+ author_email: commit.author&.email
486
498
  )
499
+ end
500
+ end
501
+
502
+ sig { params(commit: Sawyer::Resource).returns(RecentCommit) }
503
+ def parse_github_commit(commit)
504
+ details = sawyer_resource(commit, :commit, "commit details")
505
+ author = sawyer_optional_resource(commit, :author, "author")
506
+ commit_author = sawyer_optional_resource(details, :author, "commit author")
507
+ RecentCommit.new(
508
+ message: sawyer_optional_string(details, :message, "commit message"),
509
+ author_email: commit_author && sawyer_optional_string(commit_author, :email, "author email"),
510
+ author_name: commit_author && sawyer_optional_string(commit_author, :name, "author name"),
511
+ author_type: author && sawyer_optional_string(author, :type, "author type")
512
+ )
513
+ end
514
+
515
+ sig { params(commit: ::Gitlab::ObjectifiedHash).returns(RecentCommit) }
516
+ def parse_gitlab_commit(commit)
517
+ RecentCommit.new(
518
+ message: gitlab_optional_string(commit, "message", "commit message"),
519
+ author_email: gitlab_optional_string(commit, "author_email", "author email")
520
+ )
521
+ end
522
+
523
+ sig { params(commit: Dependabot::Clients::Azure::JsonObject).returns(RecentCommit) }
524
+ def parse_azure_commit(commit)
525
+ author = object_hash(commit, "author", "Azure author")
526
+ RecentCommit.new(
527
+ message: object_optional_string(commit, "comment", "Azure commit message"),
528
+ author_email: object_optional_string(author, "email", "Azure author email")
529
+ )
530
+ end
531
+
532
+ sig { params(commit: Dependabot::Clients::Bitbucket::JsonObject).returns(RecentCommit) }
533
+ def parse_bitbucket_commit(commit)
534
+ author = object_hash(commit, "author", "Bitbucket author")
535
+ raw_author = object_optional_string(author, "raw", "Bitbucket author")
536
+ matches = raw_author&.match(/<(.*)>/)
537
+ RecentCommit.new(
538
+ message: object_optional_string(commit, "message", "Bitbucket commit message"),
539
+ author_email: matches ? T.must(matches[1]) : nil
540
+ )
541
+ end
542
+
543
+ sig { params(resource: Sawyer::Resource, key: Symbol, context: String).returns(Sawyer::Resource) }
544
+ def sawyer_resource(resource, key, context)
545
+ value = T.cast(resource[key], Object)
546
+ return value if value.is_a?(Sawyer::Resource)
487
547
 
488
- @recent_codecommit_commit_messages.commits
489
- .find { |c| c.author.email == dependabot_email }
490
- &.message
491
- &.strip
548
+ raise_bad_commit_response("GitHub", "#{context} must be an object")
492
549
  end
493
550
 
494
- sig { params(commit: T::Hash[String, T::Hash[String, String]]).returns(String) }
495
- def azure_commit_author_email(commit)
496
- commit.fetch("author").fetch("email", "")
551
+ sig { params(resource: Sawyer::Resource, key: Symbol, context: String).returns(T.nilable(Sawyer::Resource)) }
552
+ def sawyer_optional_resource(resource, key, context)
553
+ value = T.cast(resource[key], Object)
554
+ return if value.nil?
555
+ return value if value.is_a?(Sawyer::Resource)
556
+
557
+ raise_bad_commit_response("GitHub", "#{context} must be an object or nil")
558
+ end
559
+
560
+ sig { params(resource: Sawyer::Resource, key: Symbol, context: String).returns(T.nilable(String)) }
561
+ def sawyer_optional_string(resource, key, context)
562
+ value = T.cast(resource[key], Object)
563
+ return if value.nil?
564
+ return value if value.is_a?(String)
565
+
566
+ raise_bad_commit_response("GitHub", "#{context} must be a string or nil")
567
+ end
568
+
569
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(T.nilable(String)) }
570
+ def gitlab_optional_string(resource, key, context)
571
+ value = T.cast(resource[key], Object)
572
+ return if value.nil?
573
+ return value if value.is_a?(String)
574
+
575
+ raise_bad_commit_response("GitLab", "#{context} must be a string or nil")
576
+ end
577
+
578
+ sig do
579
+ params(object: T::Hash[String, Object], key: String, context: String)
580
+ .returns(T::Hash[String, Object])
581
+ end
582
+ def object_hash(object, key, context)
583
+ value = object[key]
584
+ return T.let(value, T::Hash[String, Object]) if value.is_a?(Hash)
585
+
586
+ raise_bad_commit_response(source.provider, "#{context} must be an object")
497
587
  end
498
588
 
499
- sig { params(commit: T::Hash[String, T::Hash[String, String]]).returns(String) }
500
- def bitbucket_commit_author_email(commit)
501
- matches = commit.fetch("author").fetch("raw").match(/<(.*)>/)
502
- matches ? T.must(matches[1]) : ""
589
+ sig { params(object: T::Hash[String, Object], key: String, context: String).returns(T.nilable(String)) }
590
+ def object_optional_string(object, key, context)
591
+ value = object[key]
592
+ return if value.nil?
593
+ return value if value.is_a?(String)
594
+
595
+ raise_bad_commit_response(source.provider, "#{context} must be a string or nil")
596
+ end
597
+
598
+ sig { params(provider: String, message: String).returns(T.noreturn) }
599
+ def raise_bad_commit_response(provider, message)
600
+ raise Dependabot::PrivateSourceBadResponse.new(
601
+ source.url,
602
+ "Malformed #{provider} commit response: #{message}"
603
+ )
503
604
  end
504
605
 
505
606
  sig { returns(Dependabot::Clients::GithubWithRetries) }
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "securerandom"
@@ -114,22 +114,24 @@ module Dependabot
114
114
  # 3) Delete temp branch
115
115
  update_branch(temp_branch_name, new_commit, OBJECT_ID_FOR_BRANCH_DELETE)
116
116
 
117
- raise PullRequestUpdateFailed, response.fetch("customMessage", nil) unless response.fetch("success", false)
117
+ return if object_boolean(response, "success", "updated ref")
118
+
119
+ raise PullRequestUpdateFailed, object_optional_string(response, "customMessage", "updated ref")
118
120
  end
119
121
 
120
- sig { returns(T.nilable(T::Hash[String, T.untyped])) }
122
+ sig { returns(Dependabot::Clients::Azure::JsonObject) }
121
123
  def pull_request
122
124
  @pull_request ||=
123
125
  T.let(
124
126
  azure_client_for_source.pull_request(pull_request_number.to_s),
125
- T.nilable(T::Hash[String, T.untyped])
127
+ T.nilable(Dependabot::Clients::Azure::JsonObject)
126
128
  )
127
129
  end
128
130
 
129
131
  sig { returns(String) }
130
132
  def source_branch_name
131
133
  @source_branch_name ||= T.let(
132
- pull_request&.fetch("sourceRefName")&.gsub("refs/heads/", ""),
134
+ object_string(pull_request, "sourceRefName", "pull request").gsub("refs/heads/", ""),
133
135
  T.nilable(String)
134
136
  )
135
137
  end
@@ -147,7 +149,12 @@ module Dependabot
147
149
  author
148
150
  )
149
151
 
150
- JSON.parse(response.body).fetch("refUpdates").first.fetch("newObjectId")
152
+ root = T.cast(JSON.parse(response.body), Dependabot::Clients::Azure::JsonObject)
153
+ ref_updates = object_array(root, "refUpdates", "created commit")
154
+ ref_update = ref_updates.first
155
+ raise PullRequestUpdateFailed, "created commit refUpdates must contain at least one object" unless ref_update
156
+
157
+ object_string(ref_update, "newObjectId", "created commit")
151
158
  end
152
159
 
153
160
  sig { returns(String) }
@@ -159,7 +166,10 @@ module Dependabot
159
166
  )
160
167
  end
161
168
 
162
- sig { params(branch_name: String, old_commit: String, new_commit: String).returns(T::Hash[String, T.untyped]) }
169
+ sig do
170
+ params(branch_name: String, old_commit: String, new_commit: String)
171
+ .returns(Dependabot::Clients::Azure::JsonObject)
172
+ end
163
173
  def update_branch(branch_name, old_commit, new_commit)
164
174
  azure_client_for_source.update_ref(
165
175
  branch_name,
@@ -169,23 +179,65 @@ module Dependabot
169
179
  end
170
180
 
171
181
  # For updating source branch, we require the latest commit for the source branch.
172
- sig { returns(T::Hash[String, T.untyped]) }
182
+ sig { returns(Dependabot::Clients::Azure::JsonObject) }
173
183
  def commit_being_updated
174
184
  @commit_being_updated ||=
175
185
  T.let(
176
186
  T.must(azure_client_for_source.commits(source_branch_name).first),
177
- T.nilable(T::Hash[String, T.untyped])
187
+ T.nilable(Dependabot::Clients::Azure::JsonObject)
178
188
  )
179
189
  end
180
190
 
181
191
  sig { returns(String) }
182
192
  def old_source_branch_commit
183
- commit_being_updated.fetch("commitId")
193
+ object_string(commit_being_updated, "commitId", "commit")
184
194
  end
185
195
 
186
196
  sig { returns(String) }
187
197
  def commit_message
188
- commit_being_updated.fetch("comment")
198
+ object_string(commit_being_updated, "comment", "commit")
199
+ end
200
+
201
+ sig { params(object: T::Hash[String, Object], key: String, context: String).returns(String) }
202
+ def object_string(object, key, context)
203
+ value = object[key]
204
+ return value if value.is_a?(String)
205
+
206
+ raise PullRequestUpdateFailed, "#{context} #{key} must be a string"
207
+ end
208
+
209
+ sig { params(object: T::Hash[String, Object], key: String, context: String).returns(T.nilable(String)) }
210
+ def object_optional_string(object, key, context)
211
+ value = object[key]
212
+ return if value.nil?
213
+ return value if value.is_a?(String)
214
+
215
+ raise PullRequestUpdateFailed, "#{context} #{key} must be a string or nil"
216
+ end
217
+
218
+ sig { params(object: T::Hash[String, Object], key: String, context: String).returns(T::Boolean) }
219
+ def object_boolean(object, key, context)
220
+ value = object[key]
221
+ return value if value == true || value == false
222
+ return false if value.nil?
223
+
224
+ raise PullRequestUpdateFailed, "#{context} #{key} must be a boolean or nil"
225
+ end
226
+
227
+ sig do
228
+ params(object: T::Hash[String, Object], key: String, context: String)
229
+ .returns(T::Array[T::Hash[String, Object]])
230
+ end
231
+ def object_array(object, key, context)
232
+ value = object[key]
233
+ raise PullRequestUpdateFailed, "#{context} #{key} must be an array" unless value.is_a?(Array)
234
+
235
+ value.map do |entry|
236
+ parsed_entry = T.cast(entry, Object)
237
+ raise PullRequestUpdateFailed, "#{context} entry must be an object" unless parsed_entry.is_a?(Hash)
238
+
239
+ T.let(parsed_entry, T::Hash[String, Object])
240
+ end
189
241
  end
190
242
  end
191
243
  end