dependabot-common 0.392.0 → 0.393.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 (34) 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/file_fetchers/base.rb +196 -140
  13. data/lib/dependabot/file_updaters/vendor_updater.rb +8 -2
  14. data/lib/dependabot/git_tag_details.rb +8 -14
  15. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +211 -110
  16. data/lib/dependabot/metadata_finders/base/commit_response_parser.rb +68 -0
  17. data/lib/dependabot/metadata_finders/base/commits_finder.rb +44 -38
  18. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  19. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  20. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  21. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  22. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  23. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  24. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  25. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  26. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  27. data/lib/dependabot/pull_request_updater.rb +3 -3
  28. data/lib/dependabot/shared_helpers.rb +77 -32
  29. data/lib/dependabot/update_checkers/base.rb +9 -5
  30. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  31. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  32. data/lib/dependabot/workspace/git.rb +35 -4
  33. data/lib/dependabot.rb +1 -1
  34. metadata +6 -2
@@ -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,8 @@ require "dependabot/git_metadata_fetcher"
10
10
  require "dependabot/git_commit_checker"
11
11
  require "dependabot/metadata_finders/base"
12
12
  require "dependabot/credential"
13
+ require "dependabot/errors"
14
+ require_relative "commit_response_parser"
13
15
 
14
16
  module Dependabot
15
17
  module MetadataFinders
@@ -283,24 +285,21 @@ module Dependabot
283
285
 
284
286
  args = { sha: previous_tag, path: path }.compact
285
287
  previous_commit_shas =
286
- T.unsafe(github_client.commits(repo, **args)).map(&:sha)
288
+ github_client.commits(repo, args).map { |commit| commit_response_parser.github_commit_sha(commit) }
287
289
 
288
290
  # NOTE: We reverse this so it's consistent with the array we get
289
291
  # from `github_client.compare(...)`
290
292
  args = { sha: new_tag, path: path }.compact
291
- T.unsafe(
292
- github_client
293
- .commits(repo, **args)
294
- )
295
- .reject { |c| previous_commit_shas.include?(c.sha) }.reverse
293
+ github_client
294
+ .commits(repo, args)
295
+ .reject { |commit| previous_commit_shas.include?(commit_response_parser.github_commit_sha(commit)) }
296
+ .reverse
296
297
  end
297
- return [] unless commits
298
-
299
298
  commits.map do |commit|
300
299
  {
301
- message: commit.commit.message,
302
- sha: commit.sha,
303
- html_url: commit.html_url
300
+ message: commit_response_parser.github_commit_message(commit),
301
+ sha: commit_response_parser.github_commit_sha(commit),
302
+ html_url: commit_response_parser.sawyer_string(commit, :html_url, "commit")
304
303
  }
305
304
  end
306
305
  rescue Octokit::NotFound
@@ -312,10 +311,13 @@ module Dependabot
312
311
  bitbucket_client
313
312
  .compare(T.must(source).repo, T.must(previous_tag), T.must(new_tag))
314
313
  .map do |commit|
314
+ summary = T.cast(commit.fetch("summary"), Dependabot::Clients::Bitbucket::JsonObject)
315
+ links = T.cast(commit.fetch("links"), Dependabot::Clients::Bitbucket::JsonObject)
316
+ html = T.cast(links.fetch("html"), Dependabot::Clients::Bitbucket::JsonObject)
315
317
  {
316
- message: commit.dig("summary", "raw"),
317
- sha: commit["hash"],
318
- html_url: commit.dig("links", "html", "href")
318
+ message: T.cast(summary.fetch("raw"), String),
319
+ sha: T.cast(commit.fetch("hash"), String),
320
+ html_url: T.cast(html.fetch("href"), String)
319
321
  }
320
322
  end
321
323
  rescue Dependabot::Clients::Bitbucket::NotFound,
@@ -329,16 +331,20 @@ module Dependabot
329
331
 
330
332
  sig { returns(T::Array[T::Hash[Symbol, String]]) }
331
333
  def fetch_gitlab_commits
332
- T.unsafe(
333
- gitlab_client
334
- .compare(T.must(source).repo, T.must(previous_tag), T.must(new_tag))
335
- )
336
- .commits
337
- .map do |commit|
334
+ comparison = gitlab_client.compare(T.must(source).repo, T.must(previous_tag), T.must(new_tag))
335
+ commits = T.cast(comparison["commits"], Object)
336
+ commit_response_parser.raise_bad_response("GitLab", "commits must be an array") unless commits.is_a?(Array)
337
+
338
+ commits.map do |raw_commit|
339
+ commit = T.cast(raw_commit, Object)
340
+ commit_response_parser.raise_bad_response("GitLab", "commit must be an object") unless
341
+ commit.is_a?(Gitlab::ObjectifiedHash)
342
+
343
+ id = commit_response_parser.gitlab_string(commit, "id", "commit")
338
344
  {
339
- message: commit["message"],
340
- sha: commit["id"],
341
- html_url: "#{T.must(source).url}/commit/#{commit['id']}"
345
+ message: commit_response_parser.gitlab_string(commit, "message", "commit"),
346
+ sha: id,
347
+ html_url: "#{T.must(source).url}/commit/#{id}"
342
348
  }
343
349
  end
344
350
  rescue Gitlab::Error::NotFound
@@ -352,9 +358,9 @@ module Dependabot
352
358
  .compare(previous_tag, new_tag, type)
353
359
  .map do |commit|
354
360
  {
355
- message: commit["comment"],
356
- sha: commit["commitId"],
357
- html_url: commit["remoteUrl"]
361
+ message: commit_response_parser.object_string(commit, "comment", "commit"),
362
+ sha: commit_response_parser.object_string(commit, "commitId", "commit"),
363
+ html_url: commit_response_parser.object_string(commit, "remoteUrl", "commit")
358
364
  }
359
365
  end
360
366
  rescue Dependabot::Clients::Azure::NotFound,
@@ -366,6 +372,14 @@ module Dependabot
366
372
  []
367
373
  end
368
374
 
375
+ sig { returns(CommitResponseParser) }
376
+ def commit_response_parser
377
+ @commit_response_parser ||= T.let(
378
+ CommitResponseParser.new(source_url: T.must(source).url),
379
+ T.nilable(CommitResponseParser)
380
+ )
381
+ end
382
+
369
383
  sig { returns(Dependabot::Clients::GitlabWithRetries) }
370
384
  def gitlab_client
371
385
  @gitlab_client ||=
@@ -410,21 +424,13 @@ module Dependabot
410
424
  end
411
425
 
412
426
  sig { returns(T.class_of(Dependabot::Version)) }
413
- def version_class
414
- dependency.version_class
415
- end
427
+ def version_class = dependency.version_class
416
428
 
417
429
  sig { returns(T.class_of(Dependabot::Requirement)) }
418
- def requirement_class
419
- dependency.requirement_class
420
- end
430
+ def requirement_class = dependency.requirement_class
421
431
 
422
432
  sig { params(version: T.nilable(String)).returns(T::Boolean) }
423
- def git_sha?(version)
424
- return false unless version
425
-
426
- version.match?(/^[0-9a-f]{40}$/)
427
- end
433
+ def git_sha?(version) = version&.match?(/^[0-9a-f]{40}$/) || false
428
434
 
429
435
  sig { returns(T::Boolean) }
430
436
  def reliable_source_directory?
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -110,7 +110,7 @@ module Dependabot
110
110
  ) == base_commit
111
111
  end
112
112
 
113
- sig { returns(T.nilable(Aws::CodeCommit::Types::CreatePullRequestOutput)) }
113
+ sig { returns(T.nilable(Seahorse::Client::Response)) }
114
114
  def create_pull_request
115
115
  branch = create_or_get_branch(base_commit)
116
116
  return unless branch
@@ -124,7 +124,6 @@ module Dependabot
124
124
  pr_description
125
125
  # codecommit doesn't support PR labels
126
126
  )
127
- return unless pull_request
128
127
 
129
128
  pull_request
130
129
  end
@@ -167,7 +166,7 @@ module Dependabot
167
166
  def branch_exists?(branch_name)
168
167
  @branch_ref ||= T.let(
169
168
  codecommit_client_for_source.branch(branch_name),
170
- T.nilable(String)
169
+ T.nilable(Seahorse::Client::Response)
171
170
  )
172
171
  !@branch_ref.nil?
173
172
  rescue Aws::CodeCommit::Errors::BranchDoesNotExistException
@@ -178,15 +177,14 @@ module Dependabot
178
177
  def unmerged_pull_request_exists?
179
178
  unmerged_prs = []
180
179
  pull_requests_for_branch.each do |pr|
181
- unless T.unsafe(pr).pull_request
182
- .pull_request_targets[0].merge_metadata.is_merged
183
- unmerged_prs << pr
184
- end
180
+ output = T.cast(pr.data, Aws::CodeCommit::Types::GetPullRequestOutput)
181
+ target = T.must(output.pull_request.pull_request_targets[0])
182
+ unmerged_prs << pr unless target.merge_metadata&.is_merged
185
183
  end
186
184
  unmerged_prs.any?
187
185
  end
188
186
 
189
- sig { returns(T::Array[Aws::CodeCommit::Types::PullRequest]) }
187
+ sig { returns(T::Array[Seahorse::Client::Response]) }
190
188
  def pull_requests_for_branch
191
189
  @pull_requests_for_branch ||=
192
190
  T.let(
@@ -194,17 +192,17 @@ module Dependabot
194
192
  open_prs = codecommit_client_for_source.pull_requests(
195
193
  source.repo,
196
194
  "open",
197
- source.branch || default_branch
195
+ branch_name
198
196
  )
199
197
  closed_prs = codecommit_client_for_source.pull_requests(
200
198
  source.repo,
201
199
  "closed",
202
- source.branch || default_branch
200
+ branch_name
203
201
  )
204
202
 
205
203
  [*open_prs, *closed_prs]
206
204
  end,
207
- T.nilable(T::Array[Aws::CodeCommit::Types::PullRequest])
205
+ T.nilable(T::Array[Seahorse::Client::Response])
208
206
  )
209
207
  end
210
208
 
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
@@ -6,6 +6,7 @@ require "securerandom"
6
6
  require "sorbet-runtime"
7
7
 
8
8
  require "dependabot/clients/github_with_retries"
9
+ require "dependabot/clients/github_resource_parser"
9
10
  require "dependabot/pull_request_creator"
10
11
  require "dependabot/pull_request_creator/commit_signer"
11
12
 
@@ -14,6 +15,7 @@ module Dependabot
14
15
  # rubocop:disable Metrics/ClassLength
15
16
  class Github
16
17
  extend T::Sig
18
+ include Dependabot::Clients::GithubResourceParser
17
19
 
18
20
  # GitHub limits PR descriptions to a max of 65,536 characters:
19
21
  # https://github.com/orgs/community/discussions/27190#discussioncomment-3726017
@@ -121,7 +123,7 @@ module Dependabot
121
123
  @require_up_to_date_base = require_up_to_date_base
122
124
  end
123
125
 
124
- sig { returns(T.untyped) }
126
+ sig { returns(Sawyer::Resource) }
125
127
  def create
126
128
  Dependabot.logger.info(
127
129
  "Initiating Github pull request."
@@ -135,7 +137,8 @@ module Dependabot
135
137
  end
136
138
 
137
139
  if branch_exists?(branch_name) && open_pull_request_exists?
138
- raise UnmergedPRExists, "PR ##{open_pull_requests.first.number} already exists"
140
+ number = github_integer(T.must(open_pull_requests.first), :number, "pull request")
141
+ raise UnmergedPRExists, "PR ##{number} already exists"
139
142
  end
140
143
  if require_up_to_date_base? && !base_commit_is_up_to_date?
141
144
  raise BaseCommitNotUpToDate, "HEAD #{head_commit} does not match base #{base_commit}"
@@ -186,12 +189,22 @@ module Dependabot
186
189
  open_pull_requests.any?
187
190
  end
188
191
 
189
- sig { returns(T::Array[T.untyped]) }
192
+ sig { returns(T::Array[Sawyer::Resource]) }
190
193
  def open_pull_requests
191
- pull_requests_for_branch.reject(&:closed).reject(&:merged)
194
+ pull_requests_for_branch
195
+ .reject { |pull_request| github_pull_request_merged?(pull_request) }
192
196
  end
193
197
 
194
- sig { returns(T::Array[T.untyped]) }
198
+ sig { params(pull_request: Sawyer::Resource).returns(T::Boolean) }
199
+ def github_pull_request_merged?(pull_request)
200
+ merged_at = T.cast(pull_request[:merged_at], Object)
201
+ return false if merged_at.nil?
202
+ return true if merged_at.is_a?(String) || merged_at.is_a?(Time)
203
+
204
+ raise_bad_github_response("pull request merged_at must be a time, string, or nil")
205
+ end
206
+
207
+ sig { returns(T::Array[Sawyer::Resource]) }
195
208
  def pull_requests_for_branch
196
209
  @pull_requests_for_branch ||=
197
210
  T.let(
@@ -217,7 +230,7 @@ module Dependabot
217
230
  )
218
231
  [*open_prs, *closed_prs]
219
232
  end,
220
- T.nilable(T::Array[T.untyped])
233
+ T.nilable(T::Array[Sawyer::Resource])
221
234
  )
222
235
  end
223
236
 
@@ -234,7 +247,7 @@ module Dependabot
234
247
  )
235
248
  end
236
249
 
237
- sig { returns(T.untyped) }
250
+ sig { returns(Sawyer::Resource) }
238
251
  def create_annotated_pull_request
239
252
  commit = create_commit
240
253
  branch = create_or_update_branch(commit)
@@ -260,7 +273,7 @@ module Dependabot
260
273
  false
261
274
  end
262
275
 
263
- sig { returns(T.untyped) }
276
+ sig { returns(Sawyer::Resource) }
264
277
  def create_commit
265
278
  tree = create_tree
266
279
 
@@ -268,7 +281,7 @@ module Dependabot
268
281
  github_client_for_source.create_commit(
269
282
  source.repo,
270
283
  commit_message,
271
- tree.sha,
284
+ github_string(tree, :sha, "tree"),
272
285
  base_commit,
273
286
  commit_options(tree)
274
287
  )
@@ -291,19 +304,21 @@ module Dependabot
291
304
  retry
292
305
  end
293
306
 
294
- sig { params(tree: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
307
+ sig { params(tree: Sawyer::Resource).returns(T::Hash[Symbol, Object]) }
295
308
  def commit_options(tree)
296
- options = author_details&.any? ? { author: author_details } : {}
309
+ options = T.let({}, T::Hash[Symbol, Object])
310
+ author = author_details&.dup
311
+ options[:author] = author if author&.any?
297
312
 
298
- if options[:author]&.any? && signature_key
299
- options[:author][:date] = Time.now.utc.iso8601
300
- options[:signature] = commit_signature(tree, options[:author])
313
+ if author&.any? && signature_key
314
+ author[:date] = Time.now.utc.iso8601
315
+ options[:signature] = commit_signature(tree, author)
301
316
  end
302
317
 
303
318
  options
304
319
  end
305
320
 
306
- sig { returns(T.untyped) }
321
+ sig { returns(Sawyer::Resource) }
307
322
  def create_tree
308
323
  file_trees = files.map do |file|
309
324
  if file.type == "submodule"
@@ -340,7 +355,7 @@ module Dependabot
340
355
  )
341
356
  end
342
357
 
343
- sig { params(commit: T.untyped).returns(T.untyped) }
358
+ sig { params(commit: Sawyer::Resource).returns(T.nilable(Sawyer::Resource)) }
344
359
  def create_or_update_branch(commit)
345
360
  if branch_exists?(branch_name)
346
361
  update_branch(commit)
@@ -359,13 +374,13 @@ module Dependabot
359
374
  retry
360
375
  end
361
376
 
362
- sig { params(commit: T.untyped).returns(T.untyped) }
377
+ sig { params(commit: Sawyer::Resource).returns(Sawyer::Resource) }
363
378
  def create_branch(commit)
364
379
  ref = "refs/heads/#{branch_name}"
365
380
 
366
381
  begin
367
382
  branch =
368
- github_client_for_source.create_ref(source.repo, ref, commit.sha)
383
+ github_client_for_source.create_ref(source.repo, ref, github_string(commit, :sha, "commit"))
369
384
  @branch_name = ref.gsub(%r{^refs/heads/}, "")
370
385
  branch
371
386
  rescue Octokit::UnprocessableEntity => e
@@ -383,32 +398,32 @@ module Dependabot
383
398
  end
384
399
  end
385
400
 
386
- sig { params(commit: T.untyped).void }
401
+ sig { params(commit: Sawyer::Resource).returns(Sawyer::Resource) }
387
402
  def update_branch(commit)
388
403
  github_client_for_source.update_ref(
389
404
  source.repo,
390
405
  "heads/#{branch_name}",
391
- commit.sha,
406
+ github_string(commit, :sha, "commit"),
392
407
  true
393
408
  )
394
409
  end
395
410
 
396
- sig { params(pull_request: T.untyped).void }
411
+ sig { params(pull_request: Sawyer::Resource).void }
397
412
  def annotate_pull_request(pull_request)
398
- labeler.label_pull_request(pull_request.number)
413
+ labeler.label_pull_request(github_integer(pull_request, :number, "pull request"))
399
414
  add_reviewers_to_pull_request(pull_request) if reviewers&.any?
400
415
  add_assignees_to_pull_request(pull_request) if assignees&.any?
401
416
  add_milestone_to_pull_request(pull_request) if milestone
402
417
  end
403
418
 
404
- sig { params(pull_request: T.untyped).void }
419
+ sig { params(pull_request: Sawyer::Resource).void }
405
420
  def add_reviewers_to_pull_request(pull_request)
406
421
  reviewers_hash =
407
422
  T.must(reviewers).keys.to_h { |k| [k.to_sym, T.must(reviewers)[k]] }
408
423
 
409
424
  github_client_for_source.request_pull_request_review(
410
425
  source.repo,
411
- pull_request.number,
426
+ github_integer(pull_request, :number, "pull request"),
412
427
  reviewers: reviewers_hash[:reviewers] || [],
413
428
  team_reviewers: reviewers_hash[:team_reviewers] || []
414
429
  )
@@ -434,7 +449,7 @@ module Dependabot
434
449
  false
435
450
  end
436
451
 
437
- sig { params(pull_request: T.untyped, message: String).void }
452
+ sig { params(pull_request: Sawyer::Resource, message: String).void }
438
453
  def comment_with_invalid_reviewer(pull_request, message)
439
454
  reviewers_hash =
440
455
  T.must(reviewers).keys.to_h { |k| [k.to_sym, T.must(reviewers)[k]] }
@@ -460,16 +475,16 @@ module Dependabot
460
475
 
461
476
  github_client_for_source.add_comment(
462
477
  source.repo,
463
- pull_request.number,
478
+ github_integer(pull_request, :number, "pull request"),
464
479
  msg
465
480
  )
466
481
  end
467
482
 
468
- sig { params(pull_request: T.untyped).void }
483
+ sig { params(pull_request: Sawyer::Resource).void }
469
484
  def add_assignees_to_pull_request(pull_request)
470
485
  github_client_for_source.add_assignees(
471
486
  source.repo,
472
- pull_request.number,
487
+ github_integer(pull_request, :number, "pull request"),
473
488
  T.must(assignees)
474
489
  )
475
490
  rescue Octokit::NotFound
@@ -480,18 +495,18 @@ module Dependabot
480
495
  raise unless e.message.include?("Could not add assignees")
481
496
  end
482
497
 
483
- sig { params(pull_request: T.untyped).void }
498
+ sig { params(pull_request: Sawyer::Resource).void }
484
499
  def add_milestone_to_pull_request(pull_request)
485
500
  github_client_for_source.update_issue(
486
501
  source.repo,
487
- pull_request.number,
502
+ github_integer(pull_request, :number, "pull request"),
488
503
  milestone: milestone
489
504
  )
490
505
  rescue Octokit::UnprocessableEntity => e
491
506
  raise unless e.message.include?("code: invalid")
492
507
  end
493
508
 
494
- sig { returns(T.untyped) }
509
+ sig { returns(T.nilable(Sawyer::Resource)) }
495
510
  def create_pull_request
496
511
  github_client_for_source.create_pull_request(
497
512
  source.repo,
@@ -521,7 +536,7 @@ module Dependabot
521
536
  def default_branch
522
537
  @default_branch ||=
523
538
  T.let(
524
- T.unsafe(github_client_for_source.repo(source.repo)).default_branch,
539
+ github_client_for_source.fetch_default_branch(source.repo),
525
540
  T.nilable(String)
526
541
  )
527
542
  end
@@ -539,13 +554,13 @@ module Dependabot
539
554
  end
540
555
 
541
556
  sig do
542
- params(tree: T.untyped, author_details_with_date: T::Hash[Symbol, String]).returns(String)
557
+ params(tree: Sawyer::Resource, author_details_with_date: T::Hash[Symbol, String]).returns(String)
543
558
  end
544
559
  def commit_signature(tree, author_details_with_date)
545
560
  CommitSigner.new(
546
561
  author_details: author_details_with_date,
547
562
  commit_message: commit_message,
548
- tree_sha: tree.sha,
563
+ tree_sha: github_string(tree, :sha, "tree"),
549
564
  parent_sha: base_commit,
550
565
  signature_key: T.must(signature_key)
551
566
  ).signature
@@ -570,6 +585,11 @@ module Dependabot
570
585
  )
571
586
  end
572
587
 
588
+ sig { returns(String) }
589
+ def github_response_source
590
+ source.url
591
+ end
592
+
573
593
  sig { params(err: StandardError).returns(T.noreturn) }
574
594
  def handle_error(err)
575
595
  cause = case err
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "gitlab"
@@ -6,6 +6,7 @@ require "sorbet-runtime"
6
6
 
7
7
  require "dependabot/clients/gitlab_with_retries"
8
8
  require "dependabot/pull_request_creator"
9
+ require "dependabot/errors"
9
10
 
10
11
  module Dependabot
11
12
  class PullRequestCreator
@@ -158,18 +159,19 @@ module Dependabot
158
159
  gitlab_client_for_source.commits(source.repo, ref_name: branch_name),
159
160
  T.nilable(::Gitlab::PaginatedResponse)
160
161
  )
161
- T.unsafe(@commits).first.message == commit_message
162
+ commit = @commits.first
163
+ return false unless commit
164
+
165
+ gitlab_string(commit, "message", "commit") == commit_message
162
166
  end
163
167
 
164
168
  sig { returns(T::Boolean) }
165
169
  def merge_request_exists?
166
- T.unsafe(
167
- gitlab_client_for_source.merge_requests(
168
- (target_project_id || source.repo).to_s,
169
- source_branch: branch_name,
170
- target_branch: source.branch || default_branch,
171
- state: "all"
172
- )
170
+ gitlab_client_for_source.merge_requests(
171
+ (target_project_id || source.repo).to_s,
172
+ source_branch: branch_name,
173
+ target_branch: source.branch || default_branch,
174
+ state: "all"
173
175
  ).any?
174
176
  end
175
177
 
@@ -240,7 +242,7 @@ module Dependabot
240
242
 
241
243
  gitlab_client_for_source.create_merge_request_level_rule(
242
244
  (target_project_id || source.repo).to_s,
243
- T.unsafe(merge_request).iid,
245
+ gitlab_integer(merge_request, "iid", "merge request"),
244
246
  name: "dependency-updates",
245
247
  approvals_required: 1,
246
248
  user_ids: approvers_hash[:approvers],
@@ -260,10 +262,34 @@ module Dependabot
260
262
  def default_branch
261
263
  @default_branch ||=
262
264
  T.let(
263
- T.unsafe(gitlab_client_for_source.project(source.repo)).default_branch,
265
+ gitlab_client_for_source.fetch_default_branch(source.repo),
264
266
  T.nilable(String)
265
267
  )
266
268
  end
269
+
270
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(String) }
271
+ def gitlab_string(resource, key, context)
272
+ value = T.cast(resource[key], Object)
273
+ return value if value.is_a?(String)
274
+
275
+ raise_bad_gitlab_response("#{context} #{key} must be a string")
276
+ end
277
+
278
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(Integer) }
279
+ def gitlab_integer(resource, key, context)
280
+ value = T.cast(resource[key], Object)
281
+ return value if value.is_a?(Integer)
282
+
283
+ raise_bad_gitlab_response("#{context} #{key} must be an integer")
284
+ end
285
+
286
+ sig { params(message: String).returns(T.noreturn) }
287
+ def raise_bad_gitlab_response(message)
288
+ raise Dependabot::PrivateSourceBadResponse.new(
289
+ source.url,
290
+ "Malformed GitLab response: #{message}"
291
+ )
292
+ end
267
293
  end
268
294
  end
269
295
  end