# frozen_string_literal: truemoduleBundler# This class contains all of the logic for determining the next version of a# Gem to update to based on the requested level (patch, minor, major).# Primarily designed to work with Resolver which will provide it the list of# available dependency versions as found in its index, before returning it to# to the resolution engine to select the best version.classGemVersionPromoterattr_reader:levelattr_accessor:pre# By default, strict is false, meaning every available version of a gem# is returned from sort_versions. The order gives preference to the# requested level (:patch, :minor, :major) but in complicated requirement# cases some gems will by necessity be promoted past the requested level,# or even reverted to older versions.## If strict is set to true, the results from sort_versions will be# truncated, eliminating any version outside the current level scope.# This can lead to unexpected outcomes or even VersionConflict exceptions# that report a version of a gem not existing for versions that indeed do# existing in the referenced source.attr_accessor:strict# Creates a GemVersionPromoter instance.## @return [GemVersionPromoter]definitialize@level=:major@strict=false@pre=falseend# @param value [Symbol] One of three Symbols: :major, :minor or :patch.deflevel=(value)v=casevaluewhenString,Symbolvalue.to_symendraiseArgumentError,"Unexpected level #{v}. Must be :major, :minor or :patch"unless[:major,:minor,:patch].include?(v)@level=vend# Given a Resolver::Package and an Array of Specifications of available# versions for a gem, this method will return the Array of Specifications# sorted (and possibly truncated if strict is true) in an order to give# preference to the current level (:major, :minor or :patch) when resolution# is deciding what versions best resolve all dependencies in the bundle.# @param package [Resolver::Package] The package being resolved.# @param specs [Specification] An array of Specifications for the package.# @return [Specification] A new instance of the Specification Array sorted and# possibly filtered.defsort_versions(package,specs)specs=filter_dep_specs(specs,package)ifstrictsort_dep_specs(specs,package)end# @return [bool] Convenience method for testing value of level variable.defmajor?level==:majorend# @return [bool] Convenience method for testing value of level variable.defminor?level==:minorend# @return [bool] Convenience method for testing value of pre variable.defpre?pre==trueendprivatedeffilter_dep_specs(specs,package)locked_version=package.locked_versionreturnspecsiflocked_version.nil?||major?specs.selectdo|spec|gsv=spec.versionmust_match=minor??[0]:[0,1]all_match=must_match.all?{|idx|gsv.segments[idx]==locked_version.segments[idx]}all_match&&gsv>=locked_versionendenddefsort_dep_specs(specs,package)locked_version=package.locked_versionresult=specs.sortdo|a,b|unlesslocked_version&&(package.prerelease_specified?||pre?)a_pre=a.prerelease?b_pre=b.prerelease?next-1ifa_pre&&!b_prenext1ifb_pre&&!a_preendifmajor?a<=>belsifeither_version_older_than_locked?(a,b,locked_version)a<=>belsifsegments_do_not_match?(a,b,:major)b<=>aelsif!minor?&&segments_do_not_match?(a,b,:minor)b<=>aelsea<=>bendendpost_sort(result,package.unlock?,locked_version)enddefeither_version_older_than_locked?(a,b,locked_version)locked_version&&(a.version<locked_version||b.version<locked_version)enddefsegments_do_not_match?(a,b,level)index=[:major,:minor].index(level)a.segments[index]!=b.segments[index]end# Specific version moves can't always reliably be done during sorting# as not all elements are compared against each other.defpost_sort(result,unlock,locked_version)# default :major behavior in Bundler does not do thisreturnresultifmajor?ifunlock||locked_version.nil?resultelsemove_version_to_end(result,locked_version)endenddefmove_version_to_end(result,version)move,keep=result.partition{|s|s.version.to_s==version.to_s}keep.concat(move)endendend