| | |
| | |
| |
|
| | require "excon" |
| | require "sorbet-runtime" |
| | require "gitlab" |
| | require "dependabot/clients/github_with_retries" |
| | require "dependabot/clients/gitlab_with_retries" |
| | require "dependabot/clients/bitbucket_with_retries" |
| | require "dependabot/metadata_finders" |
| | require "dependabot/errors" |
| | require "dependabot/utils" |
| | require "dependabot/source" |
| | require "dependabot/dependency" |
| | require "dependabot/credential" |
| | require "dependabot/git_metadata_fetcher" |
| | module Dependabot |
| | |
| | class GitCommitChecker |
| | extend T::Sig |
| |
|
| | VERSION_REGEX = / |
| | (?<version> |
| | (?<=^v)[0-9]+(?:\-[a-z0-9]+)? |
| | | |
| | [0-9]+\.[0-9]+(?:\.[a-z0-9\-]+)* |
| | )$ |
| | /ix |
| |
|
| | |
| | VERSION_TAG_MATCH_PATTERN = "(?:[^0-9\\.]|\\A)%s\\z" |
| |
|
| | sig do |
| | params( |
| | dependency: Dependabot::Dependency, |
| | credentials: T::Array[Dependabot::Credential], |
| | ignored_versions: T::Array[String], |
| | raise_on_ignored: T::Boolean, |
| | consider_version_branches_pinned: T::Boolean, |
| | dependency_source_details: T.nilable(T::Hash[Symbol, String]) |
| | ) |
| | .void |
| | end |
| | def initialize( |
| | dependency:, |
| | credentials:, |
| | ignored_versions: [], |
| | raise_on_ignored: false, |
| | consider_version_branches_pinned: false, |
| | dependency_source_details: nil |
| | ) |
| | @dependency = dependency |
| | @credentials = credentials |
| | @ignored_versions = ignored_versions |
| | @raise_on_ignored = raise_on_ignored |
| | @consider_version_branches_pinned = consider_version_branches_pinned |
| | @dependency_source_details = dependency_source_details |
| | end |
| |
|
| | sig { returns(T::Boolean) } |
| | def git_dependency? |
| | return false if dependency_source_details.nil? |
| |
|
| | dependency_source_details&.fetch(:type) == "git" |
| | end |
| |
|
| | |
| | sig { returns(T::Boolean) } |
| | def pinned? |
| | raise "Not a git dependency!" unless git_dependency? |
| |
|
| | branch = dependency_source_details&.fetch(:branch) |
| |
|
| | return false if ref.nil? |
| | return false if branch == ref |
| | return true if branch |
| | return true if dependency.version&.start_with?(T.must(ref)) |
| | return true if ref_matches_tag? |
| |
|
| | |
| | return true unless local_upload_pack&.match?(%r{ refs/heads/#{ref}$}) |
| |
|
| | |
| | @consider_version_branches_pinned && version_tag?(T.must(ref)) |
| | end |
| | |
| |
|
| | sig { returns(T::Boolean) } |
| | def pinned_ref_looks_like_version? |
| | return false unless pinned? |
| |
|
| | version_tag?(T.must(ref)) |
| | end |
| |
|
| | sig { returns(T::Boolean) } |
| | def pinned_ref_looks_like_commit_sha? |
| | return false unless ref && ref_looks_like_commit_sha?(T.must(ref)) |
| |
|
| | return false unless pinned? |
| |
|
| | local_repo_git_metadata_fetcher.head_commit_for_ref(T.must(ref)).nil? |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def head_commit_for_pinned_ref |
| | local_repo_git_metadata_fetcher.head_commit_for_ref_sha(T.must(ref)) |
| | end |
| |
|
| | sig { returns(Excon::Response) } |
| | def ref_details_for_pinned_ref |
| | T.must( |
| | T.let( |
| | GitMetadataFetcher.new( |
| | url: dependency.source_details&.fetch(:url, nil), |
| | credentials: credentials |
| | ).ref_details_for_pinned_ref(ref_pinned), |
| | T.nilable(Excon::Response) |
| | ) |
| | ) |
| | end |
| |
|
| | sig { params(ref: String).returns(T::Boolean) } |
| | def ref_looks_like_commit_sha?(ref) |
| | ref.match?(/^[0-9a-f]{6,40}$/) |
| | end |
| |
|
| | sig { params(version: T.any(String, Gem::Version)).returns(T::Boolean) } |
| | def branch_or_ref_in_release?(version) |
| | pinned_ref_in_release?(version) || branch_behind_release?(version) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def head_commit_for_current_branch |
| | ref = ref_or_branch || "HEAD" |
| |
|
| | sha = head_commit_for_local_branch(ref) |
| | return sha if pinned? || sha |
| |
|
| | raise Dependabot::GitDependencyReferenceNotFound, dependency.name |
| | end |
| |
|
| | sig { params(name: String).returns(T.nilable(String)) } |
| | def head_commit_for_local_branch(name) |
| | local_repo_git_metadata_fetcher.head_commit_for_ref(name) |
| | end |
| |
|
| | sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def local_ref_for_latest_version_matching_existing_precision |
| | allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs |
| |
|
| | max_local_tag_for_current_precision(allowed_refs) |
| | end |
| |
|
| | sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def local_ref_for_latest_version_lower_precision |
| | allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs |
| |
|
| | max_local_tag_for_lower_precision(allowed_refs) |
| | end |
| |
|
| | sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def local_tag_for_latest_version |
| | max_local_tag(allowed_version_tags) |
| | end |
| |
|
| | sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } |
| | def local_tags_for_allowed_versions_matching_existing_precision |
| | select_matching_existing_precision(allowed_version_tags).filter_map { |t| to_local_tag(t) } |
| | end |
| |
|
| | sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } |
| | def local_tags_for_allowed_versions |
| | allowed_version_tags.filter_map { |t| to_local_tag(t) } |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitRef]) } |
| | def allowed_version_tags |
| | allowed_versions(local_tags) |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitRef]) } |
| | def allowed_version_refs |
| | allowed_versions(local_refs) |
| | end |
| |
|
| | sig { returns(T.nilable(Gem::Version)) } |
| | def current_version |
| | return unless dependency.version && version_tag?(T.must(dependency.version)) |
| |
|
| | version_from_ref(T.must(dependency.version)) |
| | end |
| |
|
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T::Array[T.any(Dependabot::GitRef, Gem::Version)]) } |
| | def filter_lower_versions(tags) |
| | return tags unless current_version |
| |
|
| | versions = tags.map do |t| |
| | version_from_tag(t) |
| | end |
| |
|
| | versions.select do |version| |
| | version > current_version |
| | end |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def most_specific_tag_equivalent_to_pinned_ref |
| | commit_sha = head_commit_for_local_branch(T.must(ref)) |
| | most_specific_version_tag_for_sha(commit_sha) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def local_tag_for_pinned_sha |
| | return unless pinned_ref_looks_like_commit_sha? |
| |
|
| | @local_tag_for_pinned_sha = T.let( |
| | most_specific_version_tag_for_sha(ref), |
| | T.nilable(String) |
| | ) |
| | end |
| |
|
| | sig { returns(T.nilable(Gem::Version)) } |
| | def version_for_pinned_sha |
| | return unless local_tag_for_pinned_sha && version_class.correct?(local_tag_for_pinned_sha) |
| |
|
| | version_class.new(local_tag_for_pinned_sha) |
| | end |
| |
|
| | sig { returns(T::Boolean) } |
| | def git_repo_reachable? |
| | local_upload_pack |
| | true |
| | rescue Dependabot::GitDependenciesNotReachable |
| | false |
| | end |
| |
|
| | sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) } |
| | def dependency_source_details |
| | @dependency_source_details || dependency.source_details(allowed_types: ["git"]) |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitTagWithDetail]) } |
| | def refs_for_tag_with_detail |
| | local_repo_git_metadata_fetcher.refs_for_tag_with_detail |
| | end |
| |
|
| | sig { params(commit_sha: T.nilable(String)).returns(T.nilable(String)) } |
| | def most_specific_version_tag_for_sha(commit_sha) |
| | tags = local_tags.select { |t| t.commit_sha == commit_sha && version_class.correct?(t.name) } |
| | .sort_by { |t| version_class.new(t.name) } |
| | return if tags.empty? |
| |
|
| | tags[-1]&.name |
| | end |
| |
|
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def max_local_tag(tags) |
| | max_version_tag = tags.max_by { |t| version_from_tag(t) } |
| |
|
| | to_local_tag(max_version_tag) |
| | end |
| |
|
| | private |
| |
|
| | sig { returns(Dependabot::Dependency) } |
| | attr_reader :dependency |
| |
|
| | sig { returns(T::Array[Dependabot::Credential]) } |
| | attr_reader :credentials |
| |
|
| | sig { returns(T::Array[String]) } |
| | attr_reader :ignored_versions |
| |
|
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def max_local_tag_for_current_precision(tags) |
| | max_local_tag(select_matching_existing_precision(tags)) |
| | end |
| |
|
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def max_local_tag_for_lower_precision(tags) |
| | max_local_tag(select_lower_precision(tags)) |
| | end |
| |
|
| | |
| | sig { returns(T::Boolean) } |
| | def ref_matches_tag? |
| | return false unless ref |
| |
|
| | |
| | if version_tag?(T.must(ref)) && local_tags.any? do |tag| |
| | tag.name =~ Regexp.new(VERSION_TAG_MATCH_PATTERN % Regexp.escape(T.must(ref))) |
| | end |
| | return true |
| | end |
| |
|
| | |
| | local_upload_pack&.match?(%r{ refs/tags/#{ref}$}) || false |
| | end |
| |
|
| | |
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T::Array[Dependabot::GitRef]) } |
| | def select_matching_existing_precision(tags) |
| | current_precision = precision(T.must(dependency.version)) |
| |
|
| | tags.select { |tag| precision(scan_version(tag.name)) == current_precision } |
| | end |
| |
|
| | |
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T::Array[Dependabot::GitRef]) } |
| | def select_lower_precision(tags) |
| | current_precision = precision(T.must(dependency.version)) |
| |
|
| | tags.select { |tag| precision(scan_version(tag.name)) <= current_precision } |
| | end |
| |
|
| | sig { params(version: String).returns(Integer) } |
| | def precision(version) |
| | version.split(".").length |
| | end |
| |
|
| | sig { params(local_tags: T::Array[Dependabot::GitRef]).returns(T::Array[Dependabot::GitRef]) } |
| | def allowed_versions(local_tags) |
| | tags = |
| | local_tags |
| | .select { |t| version_tag?(t.name) && matches_existing_prefix?(t.name) } |
| | filtered = tags |
| | .reject { |t| tag_included_in_ignore_requirements?(t) } |
| | if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(tags).any? |
| | raise Dependabot::AllVersionsIgnored |
| | end |
| |
|
| | filtered |
| | .reject { |t| tag_is_prerelease?(t) && !wants_prerelease? } |
| | end |
| |
|
| | sig { params(version: T.any(String, Gem::Version)).returns(T::Boolean) } |
| | def pinned_ref_in_release?(version) |
| | raise "Not a git dependency!" unless git_dependency? |
| |
|
| | return false unless pinned? |
| | return false if listing_source_url.nil? |
| |
|
| | tag = listing_tag_for_version(version.to_s) |
| | return false unless tag |
| |
|
| | commit_included_in_tag?( |
| | commit: T.must(ref), |
| | tag: tag, |
| | allow_identical: true |
| | ) |
| | end |
| |
|
| | sig { params(version: T.any(String, Gem::Version)).returns(T::Boolean) } |
| | def branch_behind_release?(version) |
| | raise "Not a git dependency!" unless git_dependency? |
| |
|
| | return false if ref_or_branch.nil? |
| | return false if listing_source_url.nil? |
| |
|
| | tag = listing_tag_for_version(version.to_s) |
| | return false unless tag |
| |
|
| | |
| | |
| | commit_included_in_tag?( |
| | commit: T.must(ref_or_branch), |
| | tag: tag, |
| | allow_identical: false |
| | ) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def local_upload_pack |
| | local_repo_git_metadata_fetcher.upload_pack |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitRef]) } |
| | def local_refs |
| | handle_tag_prefix(local_repo_git_metadata_fetcher.refs_for_upload_pack) |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitRef]) } |
| | def local_tags |
| | handle_tag_prefix(local_repo_git_metadata_fetcher.tags_for_upload_pack) |
| | end |
| |
|
| | sig { params(tags: T::Array[Dependabot::GitRef]).returns(T::Array[Dependabot::GitRef]) } |
| | def handle_tag_prefix(tags) |
| | if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/") |
| | tags = tags.map do |tag| |
| | tag.dup.tap { |t| t.name = "tags/#{tag.name}" } |
| | end |
| | end |
| |
|
| | tags |
| | end |
| |
|
| | sig do |
| | params( |
| | tag: String, |
| | commit: String, |
| | allow_identical: T::Boolean |
| | ) |
| | .returns(T::Boolean) |
| | end |
| | def commit_included_in_tag?(tag:, commit:, allow_identical: false) |
| | status = |
| | case Source.from_url(listing_source_url)&.provider |
| | when "github" then github_commit_comparison_status(tag, commit) |
| | when "gitlab" then gitlab_commit_comparison_status(tag, commit) |
| | when "bitbucket" then bitbucket_commit_comparison_status(tag, commit) |
| | when "codecommit" then nil |
| | else raise "Unknown source" |
| | end |
| |
|
| | return true if status == "behind" |
| |
|
| | allow_identical && status == "identical" |
| | rescue Octokit::NotFound, Gitlab::Error::NotFound, |
| | Clients::Bitbucket::NotFound, |
| | Octokit::InternalServerError |
| | false |
| | end |
| |
|
| | sig { params(ref1: String, ref2: String).returns(String) } |
| | def github_commit_comparison_status(ref1, ref2) |
| | client = Clients::GithubWithRetries |
| | .for_github_dot_com(credentials: credentials) |
| |
|
| | |
| | T.unsafe(client).compare(listing_source_repo, ref1, ref2).status |
| | end |
| |
|
| | sig { params(ref1: String, ref2: String).returns(String) } |
| | def gitlab_commit_comparison_status(ref1, ref2) |
| | client = Clients::GitlabWithRetries |
| | .for_gitlab_dot_com(credentials: credentials) |
| |
|
| | comparison = T.unsafe(client).compare(listing_source_repo, ref1, ref2) |
| |
|
| | if comparison.commits.none? then "behind" |
| | elsif comparison.compare_same_ref then "identical" |
| | else |
| | "ahead" |
| | end |
| | end |
| |
|
| | sig { params(ref1: String, ref2: String).returns(String) } |
| | def bitbucket_commit_comparison_status(ref1, ref2) |
| | url = "https://api.bitbucket.org/2.0/repositories/" \ |
| | "#{listing_source_repo}/commits/?" \ |
| | "include=#{ref2}&exclude=#{ref1}" |
| |
|
| | client = Clients::BitbucketWithRetries |
| | .for_bitbucket_dot_org(credentials: credentials) |
| |
|
| | response = T.unsafe(client).get(url) |
| |
|
| | |
| | |
| | if JSON.parse(response.body).fetch("values", ["x"]).none? then "behind" |
| | else |
| | "ahead" |
| | end |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def ref_or_branch |
| | ref || dependency_source_details&.fetch(:branch) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def ref |
| | dependency_source_details&.fetch(:ref) |
| | end |
| |
|
| | sig { params(tag: String).returns(T::Boolean) } |
| | def version_tag?(tag) |
| | tag.match?(VERSION_REGEX) |
| | end |
| |
|
| | sig { params(tag: String).returns(T::Boolean) } |
| | def matches_existing_prefix?(tag) |
| | return true unless ref_or_branch |
| |
|
| | if version_tag?(T.must(ref_or_branch)) |
| | same_prefix?(T.must(ref_or_branch), tag) |
| | else |
| | local_tag_for_pinned_sha.nil? || same_prefix?(T.must(local_tag_for_pinned_sha), tag) |
| | end |
| | end |
| |
|
| | sig { params(tag: String, other_tag: String).returns(T::Boolean) } |
| | def same_prefix?(tag, other_tag) |
| | tag.gsub(VERSION_REGEX, "").gsub(/v$/i, "") == |
| | other_tag.gsub(VERSION_REGEX, "").gsub(/v$/i, "") |
| | end |
| |
|
| | sig { params(tag: T.nilable(Dependabot::GitRef)).returns(T.nilable(T::Hash[Symbol, T.untyped])) } |
| | def to_local_tag(tag) |
| | return unless tag |
| |
|
| | version = version_from_tag(tag) |
| | { |
| | tag: tag.name, |
| | version: version, |
| | commit_sha: tag.commit_sha, |
| | tag_sha: tag.ref_sha |
| | } |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def listing_source_url |
| | @listing_source_url ||= T.let( |
| | begin |
| | |
| | |
| | candidate_dep = Dependency.new( |
| | name: dependency.name, |
| | version: dependency.version, |
| | requirements: [], |
| | package_manager: dependency.package_manager |
| | ) |
| |
|
| | MetadataFinders |
| | .for_package_manager(dependency.package_manager) |
| | .new(dependency: candidate_dep, credentials: credentials) |
| | .source_url |
| | end, |
| | T.nilable(String) |
| | ) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def listing_source_repo |
| | return unless listing_source_url |
| |
|
| | Source.from_url(listing_source_url)&.repo |
| | end |
| |
|
| | sig { params(version: String).returns(T.nilable(String)) } |
| | def listing_tag_for_version(version) |
| | listing_tags |
| | .find { |t| t.name =~ Regexp.new(VERSION_TAG_MATCH_PATTERN % Regexp.escape(version)) } |
| | &.name |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::GitRef]) } |
| | def listing_tags |
| | return [] unless listing_source_url |
| |
|
| | @listing_tags ||= T.let( |
| | begin |
| | tags = listing_repo_git_metadata_fetcher.tags |
| |
|
| | if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/") |
| | tags = tags.map do |tag| |
| | tag.dup.tap { |t| t.name = "tags/#{tag.name}" } |
| | end |
| | end |
| |
|
| | tags |
| | rescue GitDependenciesNotReachable |
| | [] |
| | end, |
| | T.nilable(T::Array[Dependabot::GitRef]) |
| | ) |
| | end |
| |
|
| | sig { returns(T.nilable(String)) } |
| | def listing_upload_pack |
| | return unless listing_source_url |
| |
|
| | listing_repo_git_metadata_fetcher.upload_pack |
| | end |
| |
|
| | sig { returns(T::Array[Dependabot::Requirement]) } |
| | def ignore_requirements |
| | ignored_versions.flat_map { |req| requirement_class.requirements_array(req) } |
| | end |
| |
|
| | sig { returns(T::Boolean) } |
| | def wants_prerelease? |
| | return false unless dependency_source_details&.fetch(:ref, nil) |
| | return false unless pinned_ref_looks_like_version? |
| |
|
| | version = version_from_ref(T.must(ref)) |
| | version.prerelease? |
| | end |
| |
|
| | sig { params(tag: Dependabot::GitRef).returns(T::Boolean) } |
| | def tag_included_in_ignore_requirements?(tag) |
| | version = version_from_tag(tag) |
| | ignore_requirements.any? { |r| r.satisfied_by?(version) } |
| | end |
| |
|
| | sig { params(tag: Dependabot::GitRef).returns(T::Boolean) } |
| | def tag_is_prerelease?(tag) |
| | return true if version_from_tag(tag).prerelease? |
| |
|
| | |
| | github_release_prerelease?(tag.name) |
| | end |
| |
|
| | sig { params(tag_name: String).returns(T::Boolean) } |
| | def github_release_prerelease?(tag_name) |
| | return false unless listing_source_url |
| |
|
| | source = Source.from_url(listing_source_url) |
| | return false unless source&.provider == "github" |
| |
|
| | release = github_releases.find { |r| r.tag_name == tag_name } |
| | return false unless release |
| |
|
| | release.prerelease |
| | rescue StandardError => e |
| | Dependabot.logger.debug("Error checking GitHub release prerelease status: #{e.message}") |
| | false |
| | end |
| |
|
| | sig { returns(T::Array[T.untyped]) } |
| | def github_releases |
| | @github_releases ||= T.let( |
| | begin |
| | return [] unless listing_source_url |
| |
|
| | source = Source.from_url(listing_source_url) |
| | return [] unless source&.provider == "github" |
| |
|
| | client = Dependabot::Clients::GithubWithRetries.for_source( |
| | source: T.must(source), |
| | credentials: credentials |
| | ) |
| | T.unsafe(client).releases(T.must(source).repo, per_page: 100) |
| | rescue Octokit::Error |
| | [] |
| | end, |
| | T.nilable(T::Array[T.untyped]) |
| | ) |
| | end |
| |
|
| | sig { params(tag: Dependabot::GitRef).returns(Gem::Version) } |
| | def version_from_tag(tag) |
| | version_from_ref(tag.name) |
| | end |
| |
|
| | sig { params(name: String).returns(Gem::Version) } |
| | def version_from_ref(name) |
| | version_class.new(scan_version(name)) |
| | end |
| |
|
| | sig { params(name: String).returns(String) } |
| | def scan_version(name) |
| | T.must(T.must(name.match(VERSION_REGEX)).named_captures.fetch("version")) |
| | end |
| |
|
| | sig { returns(T.class_of(Gem::Version)) } |
| | def version_class |
| | @version_class ||= T.let( |
| | dependency.version_class, |
| | T.nilable(T.class_of(Gem::Version)) |
| | ) |
| | end |
| |
|
| | sig { returns(T.class_of(Dependabot::Requirement)) } |
| | def requirement_class |
| | @requirement_class ||= T.let( |
| | dependency.requirement_class, |
| | T.nilable(T.class_of(Dependabot::Requirement)) |
| | ) |
| | end |
| |
|
| | sig { returns(Dependabot::GitMetadataFetcher) } |
| | def local_repo_git_metadata_fetcher |
| | @local_repo_git_metadata_fetcher ||= |
| | T.let( |
| | GitMetadataFetcher.new( |
| | url: dependency_source_details&.fetch(:url), |
| | credentials: credentials |
| | ), |
| | T.nilable(Dependabot::GitMetadataFetcher) |
| | ) |
| | end |
| |
|
| | sig { returns(Dependabot::GitMetadataFetcher) } |
| | def listing_repo_git_metadata_fetcher |
| | @listing_repo_git_metadata_fetcher ||= |
| | T.let( |
| | GitMetadataFetcher.new( |
| | url: T.must(listing_source_url), |
| | credentials: credentials |
| | ), |
| | T.nilable(Dependabot::GitMetadataFetcher) |
| | ) |
| | end |
| |
|
| | sig { returns(String) } |
| | def ref_pinned |
| | dependency.source_details&.fetch(:ref, nil) || |
| | dependency.source_details&.fetch(:branch, nil) || "HEAD" |
| | end |
| | end |
| | |
| | end |
| |
|