class Dependabot::Python::UpdateChecker::LatestVersionFinder
Attributes
credentials[R]
dependency[R]
dependency_files[R]
ignored_versions[R]
security_advisories[R]
Public Class Methods
new(dependency:, dependency_files:, credentials:, ignored_versions:, raise_on_ignored: false, security_advisories:)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 20 def initialize(dependency:, dependency_files:, credentials:, ignored_versions:, raise_on_ignored: false, security_advisories:) @dependency = dependency @dependency_files = dependency_files @credentials = credentials @ignored_versions = ignored_versions @raise_on_ignored = raise_on_ignored @security_advisories = security_advisories end
Public Instance Methods
latest_version(python_version: nil)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 31 def latest_version(python_version: nil) @latest_version ||= fetch_latest_version(python_version: python_version) end
latest_version_with_no_unlock(python_version: nil)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 36 def latest_version_with_no_unlock(python_version: nil) @latest_version_with_no_unlock ||= fetch_latest_version_with_no_unlock(python_version: python_version) end
lowest_security_fix_version(python_version: nil)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 41 def lowest_security_fix_version(python_version: nil) @lowest_security_fix_version ||= fetch_lowest_security_fix_version(python_version: python_version) end
Private Instance Methods
available_versions()
click to toggle source
See www.python.org/dev/peps/pep-0503/ for details of the Simple Repository API we use here.
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 143 def available_versions @available_versions ||= index_urls.flat_map do |index_url| sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted") index_response = registry_response_for_dependency(index_url) if [401, 403].include?(index_response.status) && [401, 403].include?(registry_index_response(index_url).status) raise PrivateSourceAuthenticationFailure, sanitized_url end version_links = [] index_response.body.scan(%r{<a\s.*?>.*?</a>}m) do details = version_details_from_link(Regexp.last_match.to_s) version_links << details if details end version_links.compact rescue Excon::Error::Timeout, Excon::Error::Socket raise if MAIN_PYPI_INDEXES.include?(index_url) raise PrivateSourceTimedOut, sanitized_url end end
build_python_requirement_from_link(link)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 193 def build_python_requirement_from_link(link) req_string = Nokogiri::XML(link). at_css("a")&. attribute("data-requires-python")&. content return unless req_string requirement_class.new(CGI.unescapeHTML(req_string)) rescue Gem::Requirement::BadRequirementError nil end
fetch_latest_version(python_version:)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 51 def fetch_latest_version(python_version:) versions = available_versions versions = filter_yanked_versions(versions) versions = filter_unsupported_versions(versions, python_version) versions = filter_prerelease_versions(versions) versions = filter_ignored_versions(versions) versions.max end
fetch_latest_version_with_no_unlock(python_version:)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 60 def fetch_latest_version_with_no_unlock(python_version:) versions = available_versions versions = filter_yanked_versions(versions) versions = filter_unsupported_versions(versions, python_version) versions = filter_prerelease_versions(versions) versions = filter_ignored_versions(versions) versions = filter_out_of_range_versions(versions) versions.max end
fetch_lowest_security_fix_version(python_version:)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 70 def fetch_lowest_security_fix_version(python_version:) versions = available_versions versions = filter_yanked_versions(versions) versions = filter_unsupported_versions(versions, python_version) versions = filter_prerelease_versions(versions) versions = Dependabot::UpdateCheckers::VersionFilters.filter_vulnerable_versions(versions, security_advisories) versions = filter_ignored_versions(versions) versions = filter_lower_versions(versions) versions.min end
filter_ignored_versions(versions_array)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 104 def filter_ignored_versions(versions_array) filtered = versions_array. reject { |v| ignore_requirements.any? { |r| r.satisfied_by?(v) } } if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(versions_array).any? raise Dependabot::AllVersionsIgnored end filtered end
filter_lower_versions(versions_array)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 114 def filter_lower_versions(versions_array) return versions_array unless dependency.version && version_class.correct?(dependency.version) versions_array.select { |version| version > version_class.new(dependency.version) } end
filter_out_of_range_versions(versions_array)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 120 def filter_out_of_range_versions(versions_array) reqs = dependency.requirements.map do |r| requirement_class.requirements_array(r.fetch(:requirement)) end.compact versions_array. select { |v| reqs.all? { |r| r.any? { |o| o.satisfied_by?(v) } } } end
filter_prerelease_versions(versions_array)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 98 def filter_prerelease_versions(versions_array) return versions_array if wants_prerelease? versions_array.reject(&:prerelease?) end
filter_unsupported_versions(versions_array, python_version)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 87 def filter_unsupported_versions(versions_array, python_version) versions_array.map do |details| python_requirement = details.fetch(:python_requirement) next details.fetch(:version) unless python_version next details.fetch(:version) unless python_requirement next unless python_requirement.satisfied_by?(python_version) details.fetch(:version) end.compact end
filter_yanked_versions(versions_array)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 83 def filter_yanked_versions(versions_array) versions_array.reject { |details| details.fetch(:yanked) } end
get_version_from_filename(filename)
click to toggle source
rubocop:enable Metrics/PerceivedComplexity
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 186 def get_version_from_filename(filename) filename. gsub(/#{name_regex}-/i, ""). split(/-|\.tar\.|\.zip|\.whl/). first end
ignore_requirements()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 230 def ignore_requirements ignored_versions.flat_map { |req| requirement_class.requirements_array(req) } end
index_urls()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 206 def index_urls @index_urls ||= IndexFinder.new( dependency_files: dependency_files, credentials: credentials ).index_urls end
name_regex()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 238 def name_regex parts = normalised_name.split(/[\s_.-]/).map { |n| Regexp.quote(n) } /#{parts.join("[\s_.-]")}/i end
normalised_name()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 234 def normalised_name NameNormaliser.normalise(dependency.name) end
registry_index_response(index_url)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 222 def registry_index_response(index_url) Excon.get( index_url, idempotent: true, **SharedHelpers.excon_defaults(headers: { "Accept" => "text/html" }) ) end
registry_response_for_dependency(index_url)
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 214 def registry_response_for_dependency(index_url) Excon.get( index_url + normalised_name + "/", idempotent: true, **SharedHelpers.excon_defaults(headers: { "Accept" => "text/html" }) ) end
requirement_class()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 247 def requirement_class Utils.requirement_class_for_package_manager( dependency.package_manager ) end
version_class()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 243 def version_class Utils.version_class_for_package_manager(dependency.package_manager) end
version_details_from_link(link)
click to toggle source
rubocop:disable Metrics/PerceivedComplexity
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 169 def version_details_from_link(link) doc = Nokogiri::XML(link) filename = doc.at_css("a")&.content url = doc.at_css("a")&.attributes&.fetch("href", nil)&.value return unless filename&.match?(name_regex) || url&.match?(name_regex) version = get_version_from_filename(filename) return unless version_class.correct?(version) { version: version_class.new(version), python_requirement: build_python_requirement_from_link(link), yanked: link&.include?("data-yanked") } end
wants_prerelease?()
click to toggle source
# File lib/dependabot/python/update_checker/latest_version_finder.rb, line 129 def wants_prerelease? if dependency.version version = version_class.new(dependency.version.tr("+", ".")) return version.prerelease? end dependency.requirements.any? do |req| reqs = (req.fetch(:requirement) || "").split(",").map(&:strip) reqs.any? { |r| r.match?(/[A-Za-z]/) } end end