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"
@@ -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
@@ -506,18 +503,16 @@ module Dependabot
506
503
  params(path: String, fetch_submodules: T::Boolean, raise_errors: T::Boolean)
507
504
  .returns(T::Array[RepositoryContent])
508
505
  end
509
- def _fetch_repo_contents(path, fetch_submodules: false, raise_errors: true) # rubocop:disable Metrics/PerceivedComplexity
506
+ def _fetch_repo_contents(path, fetch_submodules: false, raise_errors: true)
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
- if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
517
- filter_excluded(entries)
518
- else
519
- entries
520
- end
515
+ filter_excluded(entries)
521
516
  rescue *CLIENT_NOT_FOUND_ERRORS
522
517
  raise Dependabot::DirectoryNotFound, directory if path == directory.gsub(%r{^/*}, "")
523
518
 
@@ -529,7 +524,7 @@ module Dependabot
529
524
  # a retry to get its contents.
530
525
  updated_path =
531
526
  _full_specification_for(path, fetch_submodules: fetch_submodules)
532
- .fetch(:path)
527
+ .path
533
528
  retry if updated_path != tmp_path
534
529
 
535
530
  return result.call unless fetch_submodules && !retrying
@@ -566,8 +561,8 @@ module Dependabot
566
561
  path = path.gsub(" ", "%20")
567
562
  github_response = github_client.contents(repo, path: path, ref: commit)
568
563
 
569
- if github_response.respond_to?(:type)
570
- update_linked_paths(repo, path, commit, T.unsafe(github_response))
564
+ if github_response.is_a?(Sawyer::Resource)
565
+ update_linked_paths(repo, path, commit, github_response)
571
566
  raise Octokit::NotFound
572
567
  end
573
568
 
@@ -598,15 +593,11 @@ module Dependabot
598
593
  size: 0 # NOTE: added for parity with github contents API
599
594
  )
600
595
  end
601
- if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
602
- filter_excluded(entries)
603
- else
604
- entries
605
- end
596
+ filter_excluded(entries)
606
597
  end
607
598
 
608
599
  # 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]) }
600
+ sig { params(entries: T::Array[RepositoryContent]).returns(T::Array[RepositoryContent]) }
610
601
  def filter_excluded(entries)
611
602
  Dependabot.logger.info("DEBUG filter_excluded: entries=#{entries.length}, exclude_paths=#{@exclude_paths.inspect}") # rubocop:disable Layout/LineLength
612
603
 
@@ -628,33 +619,76 @@ module Dependabot
628
619
 
629
620
  sig { params(file: Sawyer::Resource).returns(RepositoryContent) }
630
621
  def _build_github_file_struct(file)
622
+ name = sawyer_string(file, :name)
631
623
  RepositoryContent.new(
632
- name: file[:name],
633
- path: file[:path],
634
- type: file[:type],
635
- sha: file[:sha],
636
- size: file[:size]
624
+ name: name,
625
+ path: sawyer_optional_string(file, :path) || name,
626
+ type: sawyer_optional_string(file, :type) || "",
627
+ sha: sawyer_optional_string(file, :sha),
628
+ size: sawyer_optional_integer(file, :size) || 0
629
+ )
630
+ end
631
+
632
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(String) }
633
+ def sawyer_string(resource, key)
634
+ value = T.cast(resource[key], Object)
635
+ return value if value.is_a?(String)
636
+
637
+ raise_bad_repository_response("GitHub", "#{key} must be a string")
638
+ end
639
+
640
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(String)) }
641
+ def sawyer_optional_string(resource, key)
642
+ value = T.cast(resource[key], Object)
643
+ return if value.nil?
644
+ return value if value.is_a?(String)
645
+
646
+ raise_bad_repository_response("GitHub", "#{key} must be a string or nil")
647
+ end
648
+
649
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(Integer)) }
650
+ def sawyer_optional_integer(resource, key)
651
+ value = T.cast(resource[key], Object)
652
+ return if value.nil?
653
+ return value if value.is_a?(Integer)
654
+
655
+ raise_bad_repository_response("GitHub", "#{key} must be an integer or nil")
656
+ end
657
+
658
+ sig { params(resource: Gitlab::ObjectifiedHash, key: String).returns(String) }
659
+ def gitlab_string(resource, key)
660
+ value = T.cast(resource[key], Object)
661
+ return value if value.is_a?(String)
662
+
663
+ raise_bad_repository_response("GitLab", "#{key} must be a string")
664
+ end
665
+
666
+ sig { params(provider: String, message: String).returns(T.noreturn) }
667
+ def raise_bad_repository_response(provider, message)
668
+ raise Dependabot::PrivateSourceBadResponse.new(
669
+ source.url,
670
+ "Malformed #{provider} repository response: #{message}"
637
671
  )
638
672
  end
639
673
 
640
674
  sig { params(repo: String, path: String, commit: String).returns(T::Array[RepositoryContent]) }
641
675
  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|
676
+ gitlab_client
677
+ .repo_tree(repo, path: path, ref: commit, per_page: 100)
678
+ .map do |raw_file|
679
+ file = raw_file
680
+ object_type = gitlab_string(file, "type")
647
681
  # GitLab API essentially returns the output from `git ls-tree`
648
- type = case file.type
682
+ type = case object_type
649
683
  when "blob" then "file"
650
684
  when "tree" then "dir"
651
685
  when "commit" then "submodule"
652
- else file.fetch("type")
686
+ else object_type
653
687
  end
654
688
 
655
689
  RepositoryContent.new(
656
- name: file.name,
657
- path: file.path,
690
+ name: gitlab_string(file, "name"),
691
+ path: gitlab_string(file, "path"),
658
692
  type: type,
659
693
  size: 0 # GitLab doesn't return file size
660
694
  )
@@ -666,17 +700,19 @@ module Dependabot
666
700
  response = azure_client.fetch_repo_contents(commit, path)
667
701
 
668
702
  response.map do |entry|
669
- type = case entry.fetch("gitObjectType")
703
+ object_type = T.cast(entry.fetch("gitObjectType"), String)
704
+ relative_path = T.cast(entry.fetch("relativePath"), String)
705
+ type = case object_type
670
706
  when "blob" then "file"
671
707
  when "tree" then "dir"
672
- else entry.fetch("gitObjectType")
708
+ else object_type
673
709
  end
674
710
 
675
711
  RepositoryContent.new(
676
- name: File.basename(entry.fetch("relativePath")),
677
- path: entry.fetch("relativePath"),
712
+ name: File.basename(relative_path),
713
+ path: relative_path,
678
714
  type: type,
679
- size: entry.fetch("size")
715
+ size: T.cast(entry.fetch("size"), Integer)
680
716
  )
681
717
  end
682
718
  end
@@ -691,17 +727,19 @@ module Dependabot
691
727
  )
692
728
 
693
729
  response.map do |file|
694
- type = case file.fetch("type")
730
+ object_type = T.cast(file.fetch("type"), String)
731
+ path = T.cast(file.fetch("path"), String)
732
+ type = case object_type
695
733
  when "commit_file" then "file"
696
734
  when "commit_directory" then "dir"
697
- else file.fetch("type")
735
+ else object_type
698
736
  end
699
737
 
700
738
  RepositoryContent.new(
701
- name: File.basename(file.fetch("path")),
702
- path: file.fetch("path"),
739
+ name: File.basename(path),
740
+ path: path,
703
741
  type: type,
704
- size: file.fetch("size", 0)
742
+ size: T.cast(file.fetch("size", 0), Integer)
705
743
  )
706
744
  end
707
745
  end
@@ -714,7 +752,8 @@ module Dependabot
714
752
  path
715
753
  )
716
754
 
717
- response.files.map do |file|
755
+ output = T.cast(response.data, Aws::CodeCommit::Types::GetFolderOutput)
756
+ output.files.map do |file|
718
757
  RepositoryContent.new(
719
758
  name: File.basename(file.relative_path),
720
759
  path: file.relative_path,
@@ -724,29 +763,32 @@ module Dependabot
724
763
  end
725
764
  end
726
765
 
727
- sig { params(path: String, fetch_submodules: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
766
+ sig { params(path: String, fetch_submodules: T::Boolean).returns(RepositorySpecification) }
728
767
  def _full_specification_for(path, fetch_submodules:)
729
768
  if fetch_submodules && _linked_dir_for(path)
730
- linked_dir_details = @linked_paths[_linked_dir_for(path)]
769
+ linked_dir_details = T.must(@linked_paths[T.must(_linked_dir_for(path))])
731
770
  sub_path =
732
771
  path.gsub(%r{^#{Regexp.quote(T.must(_linked_dir_for(path)))}(/|$)}, "")
733
772
  new_path =
734
- Pathname.new(File.join(linked_dir_details.fetch(:path), sub_path))
773
+ Pathname.new(File.join(linked_dir_details.path, sub_path))
735
774
  .cleanpath.to_path
736
775
  .gsub(%r{^/}, "")
737
- {
738
- repo: linked_dir_details.fetch(:repo),
739
- commit: linked_dir_details.fetch(:commit),
740
- provider: linked_dir_details.fetch(:provider),
776
+ RepositorySpecification.new(
777
+ repo: linked_dir_details.repo,
778
+ commit: linked_dir_details.commit,
779
+ provider: linked_dir_details.provider,
741
780
  path: new_path
742
- }
781
+ )
743
782
  else
744
- {
783
+ resolved_commit = commit
784
+ raise Octokit::NotFound unless resolved_commit
785
+
786
+ RepositorySpecification.new(
745
787
  repo: source.repo,
746
788
  path: path,
747
- commit: commit,
789
+ commit: resolved_commit,
748
790
  provider: source.provider
749
- }
791
+ )
750
792
  end
751
793
  end
752
794
 
@@ -754,9 +796,11 @@ module Dependabot
754
796
  def _fetch_file_content(path, fetch_submodules: false)
755
797
  path = path.gsub(%r{^/*}, "")
756
798
 
757
- provider, repo, path, commit =
758
- _full_specification_for(path, fetch_submodules: fetch_submodules)
759
- .values_at(:provider, :repo, :path, :commit)
799
+ specification = _full_specification_for(path, fetch_submodules: fetch_submodules)
800
+ provider = specification.provider
801
+ repo = specification.repo
802
+ path = specification.path
803
+ commit = specification.commit
760
804
 
761
805
  _fetch_file_content_fully_specified(provider, repo, path, commit)
762
806
  rescue *CLIENT_NOT_FOUND_ERRORS
@@ -777,7 +821,7 @@ module Dependabot
777
821
  when "github"
778
822
  _fetch_file_content_from_github(path, repo, commit)
779
823
  when "gitlab"
780
- tmp = T.unsafe(gitlab_client.get_file(repo, path, commit)).content
824
+ tmp = gitlab_string(gitlab_client.get_file(repo, path, commit), "content")
781
825
  decode_binary_string(tmp)
782
826
  when "azure"
783
827
  azure_client.fetch_file_contents(commit, path)
@@ -789,33 +833,17 @@ module Dependabot
789
833
  end
790
834
  end
791
835
 
792
- # rubocop:disable Metrics/AbcSize
793
836
  sig { params(path: String, repo: String, commit: String).returns(String) }
794
837
  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)
798
-
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
838
+ tmp = github_file_resource(path, repo, commit)
812
839
 
813
- if T.unsafe(tmp).content == ""
840
+ content = sawyer_string(tmp, :content)
841
+ if content == ""
814
842
  # The file may have exceeded the 1MB limit
815
843
  # 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"))
844
+ github_client.raw_contents(repo, path: path, ref: commit)
817
845
  else
818
- decode_binary_string(T.unsafe(tmp).content)
846
+ decode_binary_string(content)
819
847
  end
820
848
  rescue Octokit::Forbidden => e
821
849
  raise unless e.message.include?("too_large")
@@ -827,17 +855,37 @@ module Dependabot
827
855
  file_details = repo_contents(dir: dir).find { |f| f.name == basename }
828
856
  raise unless file_details
829
857
 
830
- tmp = github_client.blob(repo, file_details.sha)
831
- return T.unsafe(tmp).content if T.unsafe(tmp).encoding == "utf-8"
858
+ tmp = github_client.blob(repo, T.must(file_details.sha))
859
+ content = sawyer_string(tmp, :content)
860
+ return content if sawyer_string(tmp, :encoding) == "utf-8"
832
861
 
833
- decode_binary_string(T.unsafe(tmp).content)
862
+ decode_binary_string(content)
863
+ end
864
+ sig { params(path: String, repo: String, commit: String).returns(Sawyer::Resource) }
865
+ def github_file_resource(path, repo, commit)
866
+ resource = github_client.contents(repo, path: path, ref: commit)
867
+ raise Octokit::NotFound if resource.is_a?(Array)
868
+ return resource unless sawyer_optional_string(resource, :type) == "symlink"
869
+
870
+ target = sawyer_string(resource, :target)
871
+ clean_target = Pathname.new(target).cleanpath.to_path
872
+ @linked_paths[path] = LinkedPath.new(
873
+ repo: repo,
874
+ provider: "github",
875
+ commit: commit,
876
+ path: clean_target
877
+ )
878
+
879
+ target_resource = github_client.contents(repo, path: clean_target, ref: commit)
880
+ return target_resource if target_resource.is_a?(Sawyer::Resource)
881
+
882
+ raise_bad_repository_response("GitHub", "symlink target must be a file")
834
883
  end
835
- # rubocop:enable Metrics/AbcSize
836
884
 
837
885
  # Update the @linked_paths hash by exploiting a side-effect of
838
886
  # recursively calling `repo_contents` for each directory up the tree
839
887
  # until a submodule or symlink is found
840
- sig { params(path: String).returns(T.nilable(T::Array[T.untyped])) }
888
+ sig { params(path: String).returns(T.nilable(T::Array[RepositoryContent])) }
841
889
  def _find_linked_dirs(path)
842
890
  path = Pathname.new(path).cleanpath.to_path.gsub(%r{^/*}, "")
843
891
  dir = File.dirname(path)
@@ -90,8 +90,6 @@ module Dependabot
90
90
  ).returns(T::Boolean)
91
91
  end
92
92
  def self.should_exclude_path?(path, context, exclude_paths)
93
- return false unless Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files)
94
-
95
93
  return false if exclude_paths.nil? || exclude_paths.empty?
96
94
 
97
95
  should_exclude = exclude_path?(path, exclude_paths)
@@ -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