class Dependabot::Dep::UpdateChecker::RequirementsUpdater

Constants

ALLOWED_UPDATE_STRATEGIES
VERSION_REGEX

Attributes

latest_resolvable_version[R]
latest_version[R]
requirements[R]
update_strategy[R]
updated_source[R]

Public Class Methods

new(requirements:, updated_source:, update_strategy:, latest_version:, latest_resolvable_version:) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 16
def initialize(requirements:, updated_source:, update_strategy:,
               latest_version:, latest_resolvable_version:)
  @requirements = requirements
  @updated_source = updated_source
  @update_strategy = update_strategy

  check_update_strategy

  if latest_version && version_class.correct?(latest_version)
    @latest_version = version_class.new(latest_version)
  end

  return unless latest_resolvable_version
  return unless version_class.correct?(latest_resolvable_version)

  @latest_resolvable_version =
    version_class.new(latest_resolvable_version)
end

Public Instance Methods

updated_requirements() click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 35
def updated_requirements
  requirements.map do |req|
    req = req.merge(source: updated_source)
    next req unless latest_resolvable_version
    next initial_req_after_source_change(req) unless req[:requirement]

    case update_strategy
    when :widen_ranges then widen_requirement(req)
    when :bump_versions then update_version(req)
    else raise "Unexpected update strategy: #{update_strategy}"
    end
  end
end

Private Instance Methods

check_update_strategy() click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 54
def check_update_strategy
  return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)

  raise "Unknown update strategy: #{update_strategy}"
end
create_new_range_requirement(string_reqs) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 151
def create_new_range_requirement(string_reqs)
  version = latest_resolvable_version

  lower_bound =
    string_reqs.
    map { |req| requirement_class.new(req) }.
    flat_map { |req| req.requirements.map(&:last) }.
    min.to_s

  upper_bound =
    if string_reqs.first.start_with?("~") &&
       version.to_s.split(".").count > 1
      create_upper_bound_for_tilda_req(string_reqs.first)
    else
      upper_bound_parts = [version.to_s.split(".").first.to_i + 1]
      upper_bound_parts.
        fill("0", 1..(lower_bound.split(".").count - 1)).
        join(".")
    end

  ">= #{lower_bound}, < #{upper_bound}"
end
create_upper_bound_for_tilda_req(string_req) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 181
def create_upper_bound_for_tilda_req(string_req)
  tilda_version = requirement_class.new(string_req).
                  requirements.map(&:last).
                  min.to_s

  upper_bound_parts = latest_resolvable_version.to_s.split(".")
  upper_bound_parts.slice(0, tilda_version.to_s.split(".").count)
  upper_bound_parts[-1] = "0"
  upper_bound_parts[-2] = (upper_bound_parts[-2].to_i + 1).to_s

  upper_bound_parts.join(".")
end
initial_req_after_source_change(req) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 67
def initial_req_after_source_change(req)
  return req unless updating_from_git_to_version?
  return req unless req.fetch(:requirement).nil?

  new_req =
    if req.fetch(:file) == "go.mod"
      "v#{latest_resolvable_version.to_s.gsub(/^v/, '')}"
    else
      "^#{latest_resolvable_version}"
    end
  req.merge(requirement: new_req)
end
requirement_class() click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 215
def requirement_class
  Dep::Requirement
end
ruby_requirements(requirement_string) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 130
def ruby_requirements(requirement_string)
  requirement_class.requirements_array(requirement_string)
end
update_greatest_version(old_version, version_to_be_permitted) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 194
def update_greatest_version(old_version, version_to_be_permitted)
  version = version_class.new(old_version)
  version = version.release if version.prerelease?

  index_to_update =
    version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max

  version.segments.map.with_index do |_, index|
    if index < index_to_update
      version_to_be_permitted.segments[index]
    elsif index == index_to_update
      version_to_be_permitted.segments[index] + 1
    else 0
    end
  end.join(".")
end
update_range_requirement(req_string) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 134
def update_range_requirement(req_string)
  range_requirement = req_string.split(",").
                      find { |r| r.match?(/<|(\s+-\s+)/) }

  versions = range_requirement.scan(VERSION_REGEX)
  upper_bound = versions.map { |v| version_class.new(v) }.max
  new_upper_bound = update_greatest_version(
    upper_bound,
    latest_resolvable_version
  )

  req_string.sub(
    upper_bound.to_s,
    new_upper_bound.to_s
  )
end
update_version(req) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 104
def update_version(req)
  current_requirement = req[:requirement]
  version = latest_resolvable_version

  ruby_reqs = ruby_requirements(current_requirement)
  reqs = current_requirement.strip.split(",").map(&:strip)

  if ruby_reqs.any? { |r| r.satisfied_by?(version) } &&
     current_requirement.match?(/(<|-\s|\|\|)/)
    return req
  end

  updated_requirement =
    if current_requirement.include?("||")
      # Further widen the range by adding another OR condition
      current_requirement + " || ^#{version}"
    elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
      # Further widen the range by updating the upper bound
      update_range_requirement(current_requirement)
    else
      update_version_requirement(reqs)
    end

  req.merge(requirement: updated_requirement)
end
update_version_requirement(string_reqs) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 174
def update_version_requirement(string_reqs)
  version = latest_resolvable_version.to_s.gsub(/^v/, "")
  current_req = string_reqs.first

  current_req.gsub(VERSION_REGEX, version)
end
updating_from_git_to_version?() click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 60
def updating_from_git_to_version?
  return false unless updated_source&.fetch(:type) == "default"

  original_source = requirements.map { |r| r[:source] }.compact.first
  original_source&.fetch(:type) == "git"
end
version_class() click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 211
def version_class
  Dep::Version
end
widen_requirement(req) click to toggle source
# File lib/dependabot/dep/update_checker/requirements_updater.rb, line 80
def widen_requirement(req)
  current_requirement = req[:requirement]
  version = latest_resolvable_version

  ruby_reqs = ruby_requirements(current_requirement)
  return req if ruby_reqs.any? { |r| r.satisfied_by?(version) }

  reqs = current_requirement.strip.split(",").map(&:strip)

  updated_requirement =
    if current_requirement.include?("||")
      # Further widen the range by adding another OR condition
      current_requirement + " || ^#{version}"
    elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
      # Further widen the range by updating the upper bound
      update_range_requirement(current_requirement)
    else
      # Convert existing requirement to a range
      create_new_range_requirement(reqs)
    end

  req.merge(requirement: updated_requirement)
end