Class: Dependabot::Vcpkg::Version
- Inherits:
-
Dependabot::Version
- Object
- Dependabot::Version
- Dependabot::Vcpkg::Version
- Extended by:
- T::Sig
- Defined in:
- lib/dependabot/vcpkg/version.rb
Overview
vcpkg has four version schemes and a #<port-version> suffix, and it refuses to compare
versions whose schemes are incomparable. This class mirrors compare_any in vcpkg-tool's
src/vcpkg/versions.cpp: a version's comparison class is inferred from its text, dates
compare with dates, dot-versions with dot-versions, and arbitrary strings only with
themselves.
See https://learn.microsoft.com/vcpkg/users/versioning#version-schemes
Constant Summary collapse
- DATE_PATTERN =
version-date: an ISO-8601 date with optional dot-separated disambiguators. /\d{4}-\d{2}-\d{2}(?:\.(?:0|[1-9]\d*))*/- DOT_PATTERN =
versionandversion-semver: dot-separated numbers with optional semver pre-release identifiers and build metadata. / (?:0|[1-9]\d*)(?:\.(?:0|[1-9]\d*))* (?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)? (?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)? /x- STRING_PATTERN =
version-stringaccepts arbitrary text.#is reserved for the port version. /[^\s#]+/- PORT_VERSION_PATTERN =
/(?:\#(?:0|[1-9]\d*))?/- VERSION_PATTERN =
/#{STRING_PATTERN}#{PORT_VERSION_PATTERN}/- REQUIREMENT_VERSION_PATTERN =
Requirement strings prefix a version with an operator, so the string scheme must not be allowed to swallow one.
/(?![=<>~!])#{VERSION_PATTERN}/- ANCHORED_VERSION_PATTERN =
/\A\s*#{VERSION_PATTERN}\s*\z/- ANCHORED_DATE_PATTERN =
/\A#{DATE_PATTERN}\z/- ANCHORED_DOT_PATTERN =
/\A#{DOT_PATTERN}\z/- DIGIT_PATTERN =
A vcpkg version always contains a digit. Requiring one keeps git refs out: a registry baseline's previous version is a ref such as
master, andMetadataFinders,LabelerandUpdateCheckers::Baseall usecorrect?to tell a version from a ref. Treatingmasteras aversion-stringmadeReleaseFindercompare release tags against it and discard them, dropping the release notes from baseline pull requests. /[0-9]/- GIT_SHA_PATTERN =
A handful of ancient ports use a bare commit SHA as a
version-string, but so does every vcpkg registry baseline. Treating those as versions would stopUpdateCheckers::Basefrom recognizing a baseline as a git SHA, so this rejects them. Digit-only strings still count, because they are valid relaxed versions. /\A(?=[0-9a-f]*[a-f])[0-9a-f]{7,40}\z/- DATE_LENGTH =
10- SCHEME_CLASSES =
A version's scheme is declared in the versions database, not implied by its text: vcpkg publishes plenty of dotted values under
version-string. Inference is only a fallback for values that arrive without one, such as advisory version lists. T.let( { "version" => :dot, "version-semver" => :dot, "version-date" => :date, "version-string" => :string }.freeze, T::Hash[String, Symbol] )
Instance Attribute Summary collapse
-
#comparison_class ⇒ Object
readonly
Returns the value of attribute comparison_class.
-
#port_version ⇒ Object
readonly
Returns the value of attribute port_version.
-
#text ⇒ Object
readonly
Returns the value of attribute text.
Class Method Summary collapse
- .coerce(other) ⇒ Object
- .comparable?(left, left_scheme, right, right_scheme) ⇒ Boolean
- .compare_identifier(left, right) ⇒ Object
- .compare_identifiers(left, right) ⇒ Object
- .comparison_class_for(text) ⇒ Object
- .comparison_class_for_scheme(scheme) ⇒ Object
- .correct?(version) ⇒ Boolean
- .new(version) ⇒ Object
- .numeric_surrogate(text) ⇒ Object
- .split_port_version(version_string) ⇒ Object
Instance Method Summary collapse
- #<=>(other) ⇒ Object
- #comparable_with?(other) ⇒ Boolean
- #date? ⇒ Boolean
- #dot? ⇒ Boolean
- #eql?(other) ⇒ Boolean
- #hash ⇒ Object
-
#initialize(version) ⇒ Version
constructor
A new instance of Version.
- #inspect ⇒ Object
- #prerelease? ⇒ Boolean
- #string? ⇒ Boolean
- #to_s ⇒ Object
- #to_semver ⇒ Object
Constructor Details
#initialize(version) ⇒ Version
Returns a new instance of Version.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/dependabot/vcpkg/version.rb', line 63 def initialize(version) @version_string = T.let(version.to_s.strip, String) raise ArgumentError, "Malformed version number string #{version}" unless self.class.correct?(@version_string) text, port_version = self.class.split_port_version(@version_string) @text = T.let(text, String) @port_version = T.let(port_version, Integer) @comparison_class = T.let(self.class.comparison_class_for(text), Symbol) @dot_parts = T.let(nil, T.nilable([T::Array[Integer], T::Array[String]])) @date_identifiers = T.let(nil, T.nilable(T::Array[Integer])) @natural_key = T.let(nil, T.nilable(T::Array[T::Array[T.any(Integer, String)]])) super(self.class.numeric_surrogate(text)) end |
Instance Attribute Details
#comparison_class ⇒ Object (readonly)
Returns the value of attribute comparison_class.
87 88 89 |
# File 'lib/dependabot/vcpkg/version.rb', line 87 def comparison_class @comparison_class end |
#port_version ⇒ Object (readonly)
Returns the value of attribute port_version.
83 84 85 |
# File 'lib/dependabot/vcpkg/version.rb', line 83 def port_version @port_version end |
#text ⇒ Object (readonly)
Returns the value of attribute text.
80 81 82 |
# File 'lib/dependabot/vcpkg/version.rb', line 80 def text @text end |
Class Method Details
.coerce(other) ⇒ Object
169 170 171 172 173 174 175 |
# File 'lib/dependabot/vcpkg/version.rb', line 169 def self.coerce(other) return other if other.is_a?(Vcpkg::Version) return nil unless other.is_a?(String) || other.is_a?(Integer) || other.is_a?(Gem::Version) string = other.to_s correct?(string) ? new(string) : nil end |
.comparable?(left, left_scheme, right, right_scheme) ⇒ Boolean
150 151 152 153 154 155 156 157 158 |
# File 'lib/dependabot/vcpkg/version.rb', line 150 def self.comparable?(left, left_scheme, right, right_scheme) left_class = comparison_class_for_scheme(left_scheme) || left.comparison_class right_class = comparison_class_for_scheme(right_scheme) || right.comparison_class return left.text == right.text if left_class == :string && right_class == :string return false if left_class == :string || right_class == :string left_class == right_class end |
.compare_identifier(left, right) ⇒ Object
193 194 195 196 197 198 199 200 201 202 |
# File 'lib/dependabot/vcpkg/version.rb', line 193 def self.compare_identifier(left, right) left_numeric = left.match?(/\A\d+\z/) right_numeric = right.match?(/\A\d+\z/) return left.to_i <=> right.to_i if left_numeric && right_numeric return -1 if left_numeric return 1 if right_numeric T.must(left <=> right) end |
.compare_identifiers(left, right) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/dependabot/vcpkg/version.rb', line 180 def self.compare_identifiers(left, right) left.each_with_index do |identifier, index| other_identifier = right[index] return 1 if other_identifier.nil? comparison = compare_identifier(identifier, other_identifier) return comparison unless comparison.zero? end left.length <=> right.length end |
.comparison_class_for(text) ⇒ Object
114 115 116 117 118 119 |
# File 'lib/dependabot/vcpkg/version.rb', line 114 def self.comparison_class_for(text) return :date if ANCHORED_DATE_PATTERN.match?(text) return :dot if ANCHORED_DOT_PATTERN.match?(text) :string end |
.comparison_class_for_scheme(scheme) ⇒ Object
135 136 137 |
# File 'lib/dependabot/vcpkg/version.rb', line 135 def self.comparison_class_for_scheme(scheme) scheme && SCHEME_CLASSES[scheme] end |
.correct?(version) ⇒ Boolean
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/dependabot/vcpkg/version.rb', line 95 def self.correct?(version) return true if version.is_a?(Gem::Version) return false if version.nil? string = version.to_s.strip return false if string.empty? return false unless DIGIT_PATTERN.match?(string) return false if GIT_SHA_PATTERN.match?(string) ANCHORED_VERSION_PATTERN.match?(string) end |
.new(version) ⇒ Object
90 91 92 |
# File 'lib/dependabot/vcpkg/version.rb', line 90 def self.new(version) T.cast(super, Dependabot::Vcpkg::Version) end |
.numeric_surrogate(text) ⇒ Object
164 165 166 |
# File 'lib/dependabot/vcpkg/version.rb', line 164 def self.numeric_surrogate(text) text[/\A\d+(?:\.\d+)*/] || "0" end |
.split_port_version(version_string) ⇒ Object
108 109 110 111 |
# File 'lib/dependabot/vcpkg/version.rb', line 108 def self.split_port_version(version_string) text, port_version = version_string.split("#", 2) [text.to_s, port_version.to_i] end |
Instance Method Details
#<=>(other) ⇒ Object
245 246 247 248 249 250 251 252 253 |
# File 'lib/dependabot/vcpkg/version.rb', line 245 def <=>(other) other = self.class.coerce(other) return nil unless other comparison = compare_text(other) return comparison unless comparison.zero? port_version <=> other.port_version end |
#comparable_with?(other) ⇒ Boolean
217 218 219 220 221 222 |
# File 'lib/dependabot/vcpkg/version.rb', line 217 def comparable_with?(other) return true if dot? && other.dot? return true if date? && other.date? string? && other.string? && text == other.text end |
#date? ⇒ Boolean
208 |
# File 'lib/dependabot/vcpkg/version.rb', line 208 def date? = comparison_class == :date |
#dot? ⇒ Boolean
205 |
# File 'lib/dependabot/vcpkg/version.rb', line 205 def dot? = comparison_class == :dot |
#eql?(other) ⇒ Boolean
237 238 239 |
# File 'lib/dependabot/vcpkg/version.rb', line 237 def eql?(other) other.is_a?(Vcpkg::Version) && text == other.text && port_version == other.port_version end |
#hash ⇒ Object
242 |
# File 'lib/dependabot/vcpkg/version.rb', line 242 def hash = [text, port_version].hash |
#inspect ⇒ Object
231 |
# File 'lib/dependabot/vcpkg/version.rb', line 231 def inspect = "#<#{self.class} #{@version_string.inspect}>" |
#prerelease? ⇒ Boolean
234 |
# File 'lib/dependabot/vcpkg/version.rb', line 234 def prerelease? = dot? && dot_identifiers.any? |
#string? ⇒ Boolean
211 |
# File 'lib/dependabot/vcpkg/version.rb', line 211 def string? = comparison_class == :string |
#to_s ⇒ Object
225 |
# File 'lib/dependabot/vcpkg/version.rb', line 225 def to_s = @version_string |
#to_semver ⇒ Object
228 |
# File 'lib/dependabot/vcpkg/version.rb', line 228 def to_semver = @version_string |