dependabot-common 0.391.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 (40) 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 +89 -13
  11. data/lib/dependabot/credential.rb +12 -3
  12. data/lib/dependabot/dependency_requirement.rb +28 -15
  13. data/lib/dependabot/errors.rb +7 -0
  14. data/lib/dependabot/file_fetchers/base.rb +196 -140
  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/pull_request_creator/azure.rb +1 -1
  21. data/lib/dependabot/pull_request_creator/branch_name_template.rb +1 -1
  22. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  23. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  24. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  25. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  26. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +21 -2
  27. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +53 -13
  28. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +20 -13
  29. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  30. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  31. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  32. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  33. data/lib/dependabot/pull_request_updater.rb +3 -3
  34. data/lib/dependabot/shared_helpers.rb +107 -39
  35. data/lib/dependabot/update_checkers/base.rb +9 -5
  36. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  37. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  38. data/lib/dependabot/workspace/git.rb +36 -5
  39. data/lib/dependabot.rb +1 -1
  40. 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"
@@ -19,40 +19,26 @@ require "dependabot/shared_helpers"
19
19
  # rubocop:disable Metrics/ClassLength
20
20
  module Dependabot
21
21
  module FileFetchers
22
- class RepositoryContent
23
- extend T::Sig
24
-
25
- sig { returns(T.nilable(String)) }
26
- attr_reader :name
27
-
28
- sig { returns(T.nilable(String)) }
29
- attr_reader :path
30
-
31
- sig { returns(T.nilable(String)) }
32
- attr_reader :type
33
-
34
- sig { returns(T.nilable(Integer)) }
35
- attr_reader :size
22
+ class RepositoryContent < T::ImmutableStruct
23
+ const :name, String
24
+ const :path, String
25
+ const :type, String
26
+ const :size, Integer
27
+ const :sha, T.nilable(String), default: nil
28
+ end
36
29
 
37
- sig { returns(T.nilable(String)) }
38
- attr_reader :sha
30
+ class LinkedPath < T::ImmutableStruct
31
+ const :repo, String
32
+ const :provider, String
33
+ const :commit, String
34
+ const :path, String
35
+ end
39
36
 
40
- sig do
41
- params(
42
- name: T.nilable(String),
43
- path: T.nilable(String),
44
- type: T.nilable(String),
45
- size: T.nilable(Integer),
46
- sha: T.nilable(String)
47
- ).void
48
- end
49
- def initialize(name: nil, path: nil, type: nil, size: nil, sha: nil)
50
- @name = name
51
- @path = path
52
- @type = type
53
- @size = size
54
- @sha = sha
55
- end
37
+ class RepositorySpecification < T::ImmutableStruct
38
+ const :provider, String
39
+ const :repo, String
40
+ const :path, String
41
+ const :commit, String
56
42
  end
57
43
 
58
44
  class Base
@@ -146,8 +132,9 @@ module Dependabot
146
132
  @repo_contents_path = repo_contents_path
147
133
  @update_config = T.let(update_config, T.nilable(Dependabot::Config::UpdateConfig))
148
134
  @exclude_paths = T.let(update_config&.exclude_paths || [], T::Array[String])
149
- @linked_paths = T.let({}, T::Hash[T.untyped, T.untyped])
150
- @submodules = T.let([], T::Array[T.untyped])
135
+ @linked_paths = T.let({}, T::Hash[String, LinkedPath])
136
+ @submodules = T.let([], T::Array[String])
137
+ @repo_contents = T.let(nil, T.nilable(T::Hash[String, T::Array[RepositoryContent]]))
151
138
  @options = options
152
139
 
153
140
  @files = T.let([], T::Array[DependencyFile])
@@ -202,7 +189,7 @@ module Dependabot
202
189
 
203
190
  branch = target_branch || default_branch_for_repo
204
191
 
205
- @commit ||= T.let(T.unsafe(client_for_provider).fetch_commit(repo, branch), T.nilable(String))
192
+ @commit ||= T.let(fetch_commit_for_provider(repo, branch), T.nilable(String))
206
193
  rescue *CLIENT_NOT_FOUND_ERRORS
207
194
  raise Dependabot::BranchNotFound, branch
208
195
  rescue Octokit::Conflict => e
@@ -226,7 +213,7 @@ module Dependabot
226
213
  raise Dependabot::RepoNotFound.new(source, e.message)
227
214
  end
228
215
 
229
- sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
216
+ sig { overridable.returns(T.nilable(T::Hash[Symbol, T.anything])) }
230
217
  def ecosystem_versions; end
231
218
 
232
219
  sig { abstract.returns(T::Array[DependencyFile]) }
@@ -236,7 +223,9 @@ module Dependabot
236
223
 
237
224
  sig { params(name: String).returns(T.nilable(Dependabot::DependencyFile)) }
238
225
  def fetch_support_file(name)
239
- fetch_file_if_present(name)&.tap { |f| f.support_file = true }
226
+ file = fetch_file_if_present(name)
227
+ file.support_file = true if file
228
+ file
240
229
  end
241
230
 
242
231
  sig { params(filename: String, fetch_submodules: T::Boolean).returns(T.nilable(DependencyFile)) }
@@ -304,7 +293,10 @@ module Dependabot
304
293
 
305
294
  linked_path = symlinked_subpath(clean_path)
306
295
  type = "symlink" if linked_path
307
- symlink_target = clean_path.sub(T.must(linked_path), @linked_paths.dig(linked_path, :path)) if type == "symlink"
296
+ if type == "symlink"
297
+ linked_path_details = T.must(@linked_paths[T.must(linked_path)])
298
+ symlink_target = clean_path.sub(T.must(linked_path), linked_path_details.path)
299
+ end
308
300
 
309
301
  DependencyFile.new(
310
302
  name: Pathname.new(filename).cleanpath.to_path,
@@ -342,7 +334,7 @@ module Dependabot
342
334
  raise_errors: T::Boolean,
343
335
  fetch_submodules: T::Boolean
344
336
  )
345
- .returns(T::Array[T.untyped])
337
+ .returns(T::Array[RepositoryContent])
346
338
  end
347
339
  def repo_contents(
348
340
  dir: ".",
@@ -353,7 +345,7 @@ module Dependabot
353
345
  dir = File.join(directory, dir) unless ignore_base_directory
354
346
  path = Pathname.new(dir).cleanpath.to_path.gsub(%r{^/*}, "")
355
347
 
356
- @repo_contents ||= T.let({}, T.nilable(T::Hash[String, T::Array[T.untyped]]))
348
+ @repo_contents ||= {}
357
349
  @repo_contents[dir.to_s] ||= if repo_contents_path
358
350
  _cloned_repo_contents(path)
359
351
  else
@@ -378,7 +370,7 @@ module Dependabot
378
370
 
379
371
  sig { returns(String) }
380
372
  def default_branch_for_repo
381
- @default_branch_for_repo ||= T.let(T.unsafe(client_for_provider).fetch_default_branch(repo), T.nilable(String))
373
+ @default_branch_for_repo ||= T.let(fetch_default_branch_for_provider(repo), T.nilable(String))
382
374
  rescue *CLIENT_NOT_FOUND_ERRORS
383
375
  raise Dependabot::RepoNotFound, source
384
376
  end
@@ -390,49 +382,54 @@ module Dependabot
390
382
  commit: String,
391
383
  github_response: Sawyer::Resource
392
384
  )
393
- .returns(T.nilable(T::Hash[String, T.untyped]))
385
+ .returns(T.nilable(LinkedPath))
394
386
  end
395
387
  def update_linked_paths(repo, path, commit, github_response)
396
- case github_response[:type]
388
+ case sawyer_string(github_response, :type)
397
389
  when "submodule"
398
- sub_source = Source.from_url(github_response[:submodule_git_url])
390
+ submodule_url = sawyer_optional_string(github_response, :submodule_git_url)
391
+ return unless submodule_url
392
+
393
+ sub_source = Source.from_url(submodule_url)
399
394
  return unless sub_source
400
395
 
401
- @linked_paths[path] = {
396
+ @linked_paths[path] = LinkedPath.new(
402
397
  repo: sub_source.repo,
403
398
  provider: sub_source.provider,
404
- commit: github_response[:sha],
399
+ commit: sawyer_string(github_response, :sha),
405
400
  path: "/"
406
- }
401
+ )
407
402
  when "symlink"
408
- updated_path = File.join(File.dirname(path), github_response[:target])
409
- @linked_paths[path] = {
403
+ updated_path = File.join(File.dirname(path), sawyer_string(github_response, :target))
404
+ @linked_paths[path] = LinkedPath.new(
410
405
  repo: repo,
411
406
  provider: "github",
412
407
  commit: commit,
413
408
  path: Pathname.new(updated_path).cleanpath.to_path
414
- }
409
+ )
415
410
  end
416
411
  end
417
412
 
418
- sig do
419
- returns(
420
- T.any(
421
- Dependabot::Clients::GithubWithRetries,
422
- Dependabot::Clients::GitlabWithRetries,
423
- Dependabot::Clients::Azure,
424
- Dependabot::Clients::BitbucketWithRetries,
425
- Dependabot::Clients::CodeCommit
426
- )
427
- )
413
+ sig { params(repo: String, branch: String).returns(String) }
414
+ def fetch_commit_for_provider(repo, branch)
415
+ case source.provider
416
+ when "github" then github_client.fetch_commit(repo, branch)
417
+ when "gitlab" then gitlab_client.fetch_commit(repo, branch)
418
+ when "azure" then azure_client.fetch_commit(repo, branch)
419
+ when "bitbucket" then bitbucket_client.fetch_commit(repo, branch)
420
+ when "codecommit" then codecommit_client.fetch_commit(repo, branch)
421
+ else raise "Unsupported provider '#{source.provider}'."
422
+ end
428
423
  end
429
- def client_for_provider
424
+
425
+ sig { params(repo: String).returns(String) }
426
+ def fetch_default_branch_for_provider(repo)
430
427
  case source.provider
431
- when "github" then github_client
432
- when "gitlab" then gitlab_client
433
- when "azure" then azure_client
434
- when "bitbucket" then bitbucket_client
435
- when "codecommit" then codecommit_client
428
+ when "github" then github_client.fetch_default_branch(repo)
429
+ when "gitlab" then gitlab_client.fetch_default_branch(repo)
430
+ when "azure" then azure_client.fetch_default_branch(repo)
431
+ when "bitbucket" then bitbucket_client.fetch_default_branch(repo)
432
+ when "codecommit" then codecommit_client.fetch_default_branch(repo)
436
433
  else raise "Unsupported provider '#{source.provider}'."
437
434
  end
438
435
  end
@@ -508,9 +505,11 @@ module Dependabot
508
505
  end
509
506
  def _fetch_repo_contents(path, fetch_submodules: false, raise_errors: true) # rubocop:disable Metrics/PerceivedComplexity
510
507
  path = path.gsub(" ", "%20")
511
- provider, repo, tmp_path, commit =
512
- _full_specification_for(path, fetch_submodules: fetch_submodules)
513
- .values_at(:provider, :repo, :path, :commit)
508
+ specification = _full_specification_for(path, fetch_submodules: fetch_submodules)
509
+ provider = specification.provider
510
+ repo = specification.repo
511
+ tmp_path = specification.path
512
+ commit = specification.commit
514
513
 
515
514
  entries = _fetch_repo_contents_fully_specified(provider, repo, tmp_path, commit)
516
515
  if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
@@ -529,7 +528,7 @@ module Dependabot
529
528
  # a retry to get its contents.
530
529
  updated_path =
531
530
  _full_specification_for(path, fetch_submodules: fetch_submodules)
532
- .fetch(:path)
531
+ .path
533
532
  retry if updated_path != tmp_path
534
533
 
535
534
  return result.call unless fetch_submodules && !retrying
@@ -566,8 +565,8 @@ module Dependabot
566
565
  path = path.gsub(" ", "%20")
567
566
  github_response = github_client.contents(repo, path: path, ref: commit)
568
567
 
569
- if github_response.respond_to?(:type)
570
- update_linked_paths(repo, path, commit, T.unsafe(github_response))
568
+ if github_response.is_a?(Sawyer::Resource)
569
+ update_linked_paths(repo, path, commit, github_response)
571
570
  raise Octokit::NotFound
572
571
  end
573
572
 
@@ -606,7 +605,7 @@ module Dependabot
606
605
  end
607
606
 
608
607
  # Filters out any entries whose paths match one of the exclude_paths globs.
609
- sig { params(entries: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
608
+ sig { params(entries: T::Array[RepositoryContent]).returns(T::Array[RepositoryContent]) }
610
609
  def filter_excluded(entries)
611
610
  Dependabot.logger.info("DEBUG filter_excluded: entries=#{entries.length}, exclude_paths=#{@exclude_paths.inspect}") # rubocop:disable Layout/LineLength
612
611
 
@@ -628,33 +627,76 @@ module Dependabot
628
627
 
629
628
  sig { params(file: Sawyer::Resource).returns(RepositoryContent) }
630
629
  def _build_github_file_struct(file)
630
+ name = sawyer_string(file, :name)
631
631
  RepositoryContent.new(
632
- name: file[:name],
633
- path: file[:path],
634
- type: file[:type],
635
- sha: file[:sha],
636
- size: file[:size]
632
+ name: name,
633
+ path: sawyer_optional_string(file, :path) || name,
634
+ type: sawyer_optional_string(file, :type) || "",
635
+ sha: sawyer_optional_string(file, :sha),
636
+ size: sawyer_optional_integer(file, :size) || 0
637
+ )
638
+ end
639
+
640
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(String) }
641
+ def sawyer_string(resource, key)
642
+ value = T.cast(resource[key], Object)
643
+ return value if value.is_a?(String)
644
+
645
+ raise_bad_repository_response("GitHub", "#{key} must be a string")
646
+ end
647
+
648
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(String)) }
649
+ def sawyer_optional_string(resource, key)
650
+ value = T.cast(resource[key], Object)
651
+ return if value.nil?
652
+ return value if value.is_a?(String)
653
+
654
+ raise_bad_repository_response("GitHub", "#{key} must be a string or nil")
655
+ end
656
+
657
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(Integer)) }
658
+ def sawyer_optional_integer(resource, key)
659
+ value = T.cast(resource[key], Object)
660
+ return if value.nil?
661
+ return value if value.is_a?(Integer)
662
+
663
+ raise_bad_repository_response("GitHub", "#{key} must be an integer or nil")
664
+ end
665
+
666
+ sig { params(resource: Gitlab::ObjectifiedHash, key: String).returns(String) }
667
+ def gitlab_string(resource, key)
668
+ value = T.cast(resource[key], Object)
669
+ return value if value.is_a?(String)
670
+
671
+ raise_bad_repository_response("GitLab", "#{key} must be a string")
672
+ end
673
+
674
+ sig { params(provider: String, message: String).returns(T.noreturn) }
675
+ def raise_bad_repository_response(provider, message)
676
+ raise Dependabot::PrivateSourceBadResponse.new(
677
+ source.url,
678
+ "Malformed #{provider} repository response: #{message}"
637
679
  )
638
680
  end
639
681
 
640
682
  sig { params(repo: String, path: String, commit: String).returns(T::Array[RepositoryContent]) }
641
683
  def _gitlab_repo_contents(repo, path, commit)
642
- T.unsafe(
643
- gitlab_client
644
- .repo_tree(repo, path: path, ref: commit, per_page: 100)
645
- )
646
- .map do |file|
684
+ gitlab_client
685
+ .repo_tree(repo, path: path, ref: commit, per_page: 100)
686
+ .map do |raw_file|
687
+ file = raw_file
688
+ object_type = gitlab_string(file, "type")
647
689
  # GitLab API essentially returns the output from `git ls-tree`
648
- type = case file.type
690
+ type = case object_type
649
691
  when "blob" then "file"
650
692
  when "tree" then "dir"
651
693
  when "commit" then "submodule"
652
- else file.fetch("type")
694
+ else object_type
653
695
  end
654
696
 
655
697
  RepositoryContent.new(
656
- name: file.name,
657
- path: file.path,
698
+ name: gitlab_string(file, "name"),
699
+ path: gitlab_string(file, "path"),
658
700
  type: type,
659
701
  size: 0 # GitLab doesn't return file size
660
702
  )
@@ -666,17 +708,19 @@ module Dependabot
666
708
  response = azure_client.fetch_repo_contents(commit, path)
667
709
 
668
710
  response.map do |entry|
669
- type = case entry.fetch("gitObjectType")
711
+ object_type = T.cast(entry.fetch("gitObjectType"), String)
712
+ relative_path = T.cast(entry.fetch("relativePath"), String)
713
+ type = case object_type
670
714
  when "blob" then "file"
671
715
  when "tree" then "dir"
672
- else entry.fetch("gitObjectType")
716
+ else object_type
673
717
  end
674
718
 
675
719
  RepositoryContent.new(
676
- name: File.basename(entry.fetch("relativePath")),
677
- path: entry.fetch("relativePath"),
720
+ name: File.basename(relative_path),
721
+ path: relative_path,
678
722
  type: type,
679
- size: entry.fetch("size")
723
+ size: T.cast(entry.fetch("size"), Integer)
680
724
  )
681
725
  end
682
726
  end
@@ -691,17 +735,19 @@ module Dependabot
691
735
  )
692
736
 
693
737
  response.map do |file|
694
- type = case file.fetch("type")
738
+ object_type = T.cast(file.fetch("type"), String)
739
+ path = T.cast(file.fetch("path"), String)
740
+ type = case object_type
695
741
  when "commit_file" then "file"
696
742
  when "commit_directory" then "dir"
697
- else file.fetch("type")
743
+ else object_type
698
744
  end
699
745
 
700
746
  RepositoryContent.new(
701
- name: File.basename(file.fetch("path")),
702
- path: file.fetch("path"),
747
+ name: File.basename(path),
748
+ path: path,
703
749
  type: type,
704
- size: file.fetch("size", 0)
750
+ size: T.cast(file.fetch("size", 0), Integer)
705
751
  )
706
752
  end
707
753
  end
@@ -714,7 +760,8 @@ module Dependabot
714
760
  path
715
761
  )
716
762
 
717
- response.files.map do |file|
763
+ output = T.cast(response.data, Aws::CodeCommit::Types::GetFolderOutput)
764
+ output.files.map do |file|
718
765
  RepositoryContent.new(
719
766
  name: File.basename(file.relative_path),
720
767
  path: file.relative_path,
@@ -724,29 +771,32 @@ module Dependabot
724
771
  end
725
772
  end
726
773
 
727
- sig { params(path: String, fetch_submodules: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
774
+ sig { params(path: String, fetch_submodules: T::Boolean).returns(RepositorySpecification) }
728
775
  def _full_specification_for(path, fetch_submodules:)
729
776
  if fetch_submodules && _linked_dir_for(path)
730
- linked_dir_details = @linked_paths[_linked_dir_for(path)]
777
+ linked_dir_details = T.must(@linked_paths[T.must(_linked_dir_for(path))])
731
778
  sub_path =
732
779
  path.gsub(%r{^#{Regexp.quote(T.must(_linked_dir_for(path)))}(/|$)}, "")
733
780
  new_path =
734
- Pathname.new(File.join(linked_dir_details.fetch(:path), sub_path))
781
+ Pathname.new(File.join(linked_dir_details.path, sub_path))
735
782
  .cleanpath.to_path
736
783
  .gsub(%r{^/}, "")
737
- {
738
- repo: linked_dir_details.fetch(:repo),
739
- commit: linked_dir_details.fetch(:commit),
740
- provider: linked_dir_details.fetch(:provider),
784
+ RepositorySpecification.new(
785
+ repo: linked_dir_details.repo,
786
+ commit: linked_dir_details.commit,
787
+ provider: linked_dir_details.provider,
741
788
  path: new_path
742
- }
789
+ )
743
790
  else
744
- {
791
+ resolved_commit = commit
792
+ raise Octokit::NotFound unless resolved_commit
793
+
794
+ RepositorySpecification.new(
745
795
  repo: source.repo,
746
796
  path: path,
747
- commit: commit,
797
+ commit: resolved_commit,
748
798
  provider: source.provider
749
- }
799
+ )
750
800
  end
751
801
  end
752
802
 
@@ -754,9 +804,11 @@ module Dependabot
754
804
  def _fetch_file_content(path, fetch_submodules: false)
755
805
  path = path.gsub(%r{^/*}, "")
756
806
 
757
- provider, repo, path, commit =
758
- _full_specification_for(path, fetch_submodules: fetch_submodules)
759
- .values_at(:provider, :repo, :path, :commit)
807
+ specification = _full_specification_for(path, fetch_submodules: fetch_submodules)
808
+ provider = specification.provider
809
+ repo = specification.repo
810
+ path = specification.path
811
+ commit = specification.commit
760
812
 
761
813
  _fetch_file_content_fully_specified(provider, repo, path, commit)
762
814
  rescue *CLIENT_NOT_FOUND_ERRORS
@@ -777,7 +829,7 @@ module Dependabot
777
829
  when "github"
778
830
  _fetch_file_content_from_github(path, repo, commit)
779
831
  when "gitlab"
780
- tmp = T.unsafe(gitlab_client.get_file(repo, path, commit)).content
832
+ tmp = gitlab_string(gitlab_client.get_file(repo, path, commit), "content")
781
833
  decode_binary_string(tmp)
782
834
  when "azure"
783
835
  azure_client.fetch_file_contents(commit, path)
@@ -789,33 +841,17 @@ module Dependabot
789
841
  end
790
842
  end
791
843
 
792
- # rubocop:disable Metrics/AbcSize
793
844
  sig { params(path: String, repo: String, commit: String).returns(String) }
794
845
  def _fetch_file_content_from_github(path, repo, commit)
795
- tmp = github_client.contents(repo, path: path, ref: commit)
796
-
797
- raise Octokit::NotFound if tmp.is_a?(Array)
846
+ tmp = github_file_resource(path, repo, commit)
798
847
 
799
- if T.unsafe(tmp).type == "symlink"
800
- @linked_paths[path] = {
801
- repo: repo,
802
- provider: "github",
803
- commit: commit,
804
- path: Pathname.new(T.unsafe(tmp).target).cleanpath.to_path
805
- }
806
- tmp = github_client.contents(
807
- repo,
808
- path: Pathname.new(T.unsafe(tmp).target).cleanpath.to_path,
809
- ref: commit
810
- )
811
- end
812
-
813
- if T.unsafe(tmp).content == ""
848
+ content = sawyer_string(tmp, :content)
849
+ if content == ""
814
850
  # The file may have exceeded the 1MB limit
815
851
  # see https://github.blog/changelog/2022-05-03-increased-file-size-limit-when-retrieving-file-contents-via-rest-api/
816
- T.unsafe(github_client.contents(repo, path: path, ref: commit, accept: "application/vnd.github.v3.raw"))
852
+ github_client.raw_contents(repo, path: path, ref: commit)
817
853
  else
818
- decode_binary_string(T.unsafe(tmp).content)
854
+ decode_binary_string(content)
819
855
  end
820
856
  rescue Octokit::Forbidden => e
821
857
  raise unless e.message.include?("too_large")
@@ -827,17 +863,37 @@ module Dependabot
827
863
  file_details = repo_contents(dir: dir).find { |f| f.name == basename }
828
864
  raise unless file_details
829
865
 
830
- tmp = github_client.blob(repo, file_details.sha)
831
- return T.unsafe(tmp).content if T.unsafe(tmp).encoding == "utf-8"
866
+ tmp = github_client.blob(repo, T.must(file_details.sha))
867
+ content = sawyer_string(tmp, :content)
868
+ return content if sawyer_string(tmp, :encoding) == "utf-8"
832
869
 
833
- decode_binary_string(T.unsafe(tmp).content)
870
+ decode_binary_string(content)
871
+ end
872
+ sig { params(path: String, repo: String, commit: String).returns(Sawyer::Resource) }
873
+ def github_file_resource(path, repo, commit)
874
+ resource = github_client.contents(repo, path: path, ref: commit)
875
+ raise Octokit::NotFound if resource.is_a?(Array)
876
+ return resource unless sawyer_optional_string(resource, :type) == "symlink"
877
+
878
+ target = sawyer_string(resource, :target)
879
+ clean_target = Pathname.new(target).cleanpath.to_path
880
+ @linked_paths[path] = LinkedPath.new(
881
+ repo: repo,
882
+ provider: "github",
883
+ commit: commit,
884
+ path: clean_target
885
+ )
886
+
887
+ target_resource = github_client.contents(repo, path: clean_target, ref: commit)
888
+ return target_resource if target_resource.is_a?(Sawyer::Resource)
889
+
890
+ raise_bad_repository_response("GitHub", "symlink target must be a file")
834
891
  end
835
- # rubocop:enable Metrics/AbcSize
836
892
 
837
893
  # Update the @linked_paths hash by exploiting a side-effect of
838
894
  # recursively calling `repo_contents` for each directory up the tree
839
895
  # until a submodule or symlink is found
840
- sig { params(path: String).returns(T.nilable(T::Array[T.untyped])) }
896
+ sig { params(path: String).returns(T.nilable(T::Array[RepositoryContent])) }
841
897
  def _find_linked_dirs(path)
842
898
  path = Pathname.new(path).cleanpath.to_path.gsub(%r{^/*}, "")
843
899
  dir = File.dirname(path)
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -27,7 +27,13 @@ module Dependabot
27
27
  super(repo_contents_path: @repo_contents_path, target_directory: @vendor_dir)
28
28
  end
29
29
 
30
- T.unsafe(self).alias_method :updated_vendor_cache_files, :updated_files
30
+ sig do
31
+ params(base_directory: String, only_paths: T.nilable(T::Array[String]))
32
+ .returns(T::Array[Dependabot::DependencyFile])
33
+ end
34
+ def updated_vendor_cache_files(base_directory:, only_paths: nil)
35
+ updated_files(base_directory: base_directory, only_paths: only_paths)
36
+ end
31
37
 
32
38
  private
33
39
 
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -18,22 +18,16 @@ module Dependabot
18
18
  # release date for cooldown handling.
19
19
  #
20
20
  # Subclasses Hash so it is a drop-in replacement at the many call sites that
21
- # read entries with [:key] / fetch and treat them as
22
- # T::Hash[Symbol, T.untyped], while exposing typed readers for the well-known
21
+ # read entries with [:key] / fetch, while exposing typed readers for the well-known
23
22
  # keys. Instances compare equal (Hash#==) to plain hashes with the same
24
23
  # content, so existing comparisons and API payloads are unaffected.
25
24
  class GitTagDetails < Hash
26
25
  extend T::Sig
27
26
  extend T::Generic
28
27
 
29
- # The values are heterogeneous (strings and a version object), so this
30
- # bridge class is necessarily untyped at the Hash level; the typed readers
31
- # below are the migration path.
32
- # rubocop:disable Sorbet/ForbidTUntyped
33
28
  K = type_member { { fixed: Symbol } }
34
- V = type_member { { fixed: T.untyped } }
35
- Elem = type_member { { fixed: [Symbol, T.untyped] } }
36
- # rubocop:enable Sorbet/ForbidTUntyped
29
+ V = type_member { { fixed: Object } }
30
+ Elem = type_member { { fixed: [Symbol, Object] } }
37
31
 
38
32
  sig do
39
33
  params(
@@ -54,25 +48,25 @@ module Dependabot
54
48
  # The tag or ref name, e.g. "v1.2.0".
55
49
  sig { returns(String) }
56
50
  def tag
57
- self[:tag]
51
+ T.cast(self[:tag], String)
58
52
  end
59
53
 
60
54
  # The version parsed from the tag name.
61
55
  sig { returns(T.nilable(Gem::Version)) }
62
56
  def version
63
- self[:version]
57
+ T.cast(self[:version], T.nilable(Gem::Version))
64
58
  end
65
59
 
66
60
  # The SHA of the commit the tag points at.
67
61
  sig { returns(T.nilable(String)) }
68
62
  def commit_sha
69
- self[:commit_sha]
63
+ T.cast(self[:commit_sha], T.nilable(String))
70
64
  end
71
65
 
72
66
  # The SHA of the tag object itself (nil for lightweight tags).
73
67
  sig { returns(T.nilable(String)) }
74
68
  def tag_sha
75
- self[:tag_sha]
69
+ T.cast(self[:tag_sha], T.nilable(String))
76
70
  end
77
71
  end
78
72
  end