module Dependabot::Bundler::UpdateChecker::SharedBundlerHelpers

Constants

GIT_REF_REGEX
GIT_REGEX
PATH_REGEX
RETRYABLE_ERRORS
RETRYABLE_PRIVATE_REGISTRY_ERRORS

Attributes

credentials[R]
dependency_files[R]
repo_contents_path[R]

Public Instance Methods

base_directory() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 64
def base_directory
  dependency_files.first.directory
end
gemfile() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 222
def gemfile
  dependency_files.find { |f| f.name == "Gemfile" } ||
    dependency_files.find { |f| f.name == "gems.rb" }
end
handle_bundler_errors(error) click to toggle source

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity rubocop:disable Metrics/AbcSize rubocop:disable Metrics/MethodLength

# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 81
def handle_bundler_errors(error)
  if error.error_class == "JSON::ParserError"
    msg = "Error evaluating your dependency files: #{error.message}"
    raise Dependabot::DependencyFileNotEvaluatable, msg
  end

  msg = error.error_class + " with message: " + error.message

  case error.error_class
  when "Bundler::Dsl::DSLError", "Bundler::GemspecError"
    # We couldn't evaluate the Gemfile, let alone resolve it
    raise Dependabot::DependencyFileNotEvaluatable, msg
  when "Bundler::Source::Git::MissingGitRevisionError"
    gem_name =
      error.message.match(GIT_REF_REGEX).
      named_captures["path"].
      split("/").last
    raise GitDependencyReferenceNotFound, gem_name
  when "Bundler::PathError"
    gem_name =
      error.message.match(PATH_REGEX).
      named_captures["path"].
      split("/").last.split("-")[0..-2].join
    raise Dependabot::PathDependenciesNotReachable, [gem_name]
  when "Bundler::Source::Git::GitCommandError"
    if error.message.match?(GIT_REGEX)
      # We couldn't find the specified branch / commit (or the two
      # weren't compatible).
      gem_name =
        error.message.match(GIT_REGEX).
        named_captures["path"].
        split("/").last.split("-")[0..-2].join
      raise GitDependencyReferenceNotFound, gem_name
    end

    bad_uris = inaccessible_git_dependencies.map do |spec|
      spec.fetch("uri")
    end
    raise unless bad_uris.any?

    # We don't have access to one of repos required
    raise Dependabot::GitDependenciesNotReachable, bad_uris.uniq
  when "Bundler::GemNotFound", "Gem::InvalidSpecificationException",
       "Bundler::VersionConflict", "Bundler::CyclicDependencyError"
    # Bundler threw an error during resolution. Any of:
    # - the gem doesn't exist in any of the specified sources
    # - the gem wasn't specified properly
    # - the gem was specified at an incompatible version
    raise Dependabot::DependencyFileNotResolvable, msg
  when "Bundler::Fetcher::AuthenticationRequiredError"
    regex = BundlerErrorPatterns::MISSING_AUTH_REGEX
    source = error.message.match(regex)[:source]
    raise Dependabot::PrivateSourceAuthenticationFailure, source
  when "Bundler::Fetcher::BadAuthenticationError"
    regex = BundlerErrorPatterns::BAD_AUTH_REGEX
    source = error.message.match(regex)[:source]
    raise Dependabot::PrivateSourceAuthenticationFailure, source
  when "Bundler::Fetcher::CertificateFailureError"
    regex = BundlerErrorPatterns::BAD_CERT_REGEX
    source = error.message.match(regex)[:source]
    raise Dependabot::PrivateSourceCertificateFailure, source
  when "Bundler::HTTPError"
    regex = BundlerErrorPatterns::HTTP_ERR_REGEX
    if error.message.match?(regex)
      source = error.message.match(regex)[:source]
      raise if source.end_with?("rubygems.org/")

      raise Dependabot::PrivateSourceTimedOut, source
    end

    # JFrog can serve a 403 if the credentials provided are good but
    # don't have access to a particular gem.
    raise unless error.message.include?("permitted to deploy")
    raise unless jfrog_source

    raise Dependabot::PrivateSourceAuthenticationFailure, jfrog_source
  else raise
  end
end
in_a_native_bundler_context(error_handling: true) { |tmp_dir| ... } click to toggle source

Bundler context setup #

# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 48
def in_a_native_bundler_context(error_handling: true)
  SharedHelpers.
    in_a_temporary_repo_directory(base_directory,
                                  repo_contents_path) do |tmp_dir|
    write_temporary_dependency_files

    yield(tmp_dir)
  end
rescue SharedHelpers::HelperSubprocessFailed => e
  retry_count ||= 0
  retry_count += 1
  sleep(rand(1.0..5.0)) && retry if retryable_error?(e) && retry_count <= 2

  error_handling ? handle_bundler_errors(e) : raise
end
inaccessible_git_dependencies() click to toggle source

rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/PerceivedComplexity rubocop:enable Metrics/AbcSize rubocop:enable Metrics/MethodLength

# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 165
def inaccessible_git_dependencies
  in_a_native_bundler_context(error_handling: false) do |tmp_dir|
    git_specs = NativeHelpers.run_bundler_subprocess(
      bundler_version: bundler_version,
      function: "git_specs",
      args: {
        dir: tmp_dir,
        gemfile_name: gemfile.name,
        credentials: credentials
      }
    )
    git_specs.reject do |spec|
      uri = URI.parse(spec.fetch("auth_uri"))
      next false unless %w(http https).include?(uri.scheme)

      Excon.get(
        uri.to_s,
        idempotent: true,
        **SharedHelpers.excon_defaults
      ).status == 200
    rescue Excon::Error::Socket, Excon::Error::Timeout
      false
    end
  end
end
jfrog_source() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 191
def jfrog_source
  return @jfrog_source unless defined?(@jfrog_source)

  @jfrog_source = in_a_native_bundler_context(error_handling: false) do |dir|
    NativeHelpers.run_bundler_subprocess(
      bundler_version: bundler_version,
      function: "jfrog_source",
      args: {
        dir: dir,
        gemfile_name: gemfile.name,
        credentials: credentials
      }
    )
  end
end
lockfile() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 227
def lockfile
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
    dependency_files.find { |f| f.name == "gems.locked" }
end
private_registry_credentials() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 217
def private_registry_credentials
  credentials.
    select { |cred| cred["type"] == "rubygems_server" }
end
retryable_error?(error) click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 68
def retryable_error?(error)
  return true if error.error_class == "JSON::ParserError"
  return true if RETRYABLE_ERRORS.include?(error.error_class)

  return false unless RETRYABLE_PRIVATE_REGISTRY_ERRORS.include?(error.error_class)

  private_registry_credentials.any?
end
sanitized_lockfile_body() click to toggle source

TODO: Stop sanitizing the lockfile once we have bundler 2 installed

# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 233
def sanitized_lockfile_body
  re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
  lockfile.content.gsub(re, "")
end
write_temporary_dependency_files() click to toggle source
# File lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb, line 207
def write_temporary_dependency_files
  dependency_files.each do |file|
    path = file.name
    FileUtils.mkdir_p(Pathname.new(path).dirname)
    File.write(path, file.content)
  end

  File.write(lockfile.name, sanitized_lockfile_body) if lockfile
end