diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 0100de7b2721a7b1a475eac6e42820c8f54c1e03..7f6a2e2f819961c7786450c7f150dc3d94e41880 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -1019,7 +1019,7 @@ module Homebrew problem "missing version" elsif !version.detected_from_url? version_text = version - version_url = Version.detect(url, specs) + version_url = Version.detect(url, **specs) if version_url.to_s == version_text.to_s && version.instance_of?(Version) problem "version #{version_text} is redundant with version scanned from URL" end diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index 4789167e99f702d42a03a10365cb7f2c9124778f..5463ca934fc4aa054648eab70933c4c1805bb9be 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -468,7 +468,7 @@ module Homebrew unless version specs = {} specs[:tag] = tag if tag - version = Version.detect(url, specs) + version = Version.detect(url, **specs) end # if we haven't already found open requests, try for an exact match across closed requests GitHub.check_for_duplicate_pull_requests("#{formula.name} #{version}", tap_full_name, state: "closed", args: args) diff --git a/Library/Homebrew/formula_creator.rb b/Library/Homebrew/formula_creator.rb index 0be15b4f881cba4e29c55f770f180dbbe1a3268a..9d424cb0cb9b10e6caa22762f9793b66a428d18e 100644 --- a/Library/Homebrew/formula_creator.rb +++ b/Library/Homebrew/formula_creator.rb @@ -34,7 +34,7 @@ module Homebrew @version = if @version Version.create(@version) else - Version.detect(url, {}) + Version.detect(url) end end diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index e498069eaefe919163d915b22180977ab5d4e442..dd11a9f981f287ce575a65f2264bf3f65ea48dc2 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -203,7 +203,7 @@ class Resource return Version::NULL if val.nil? && url.nil? case val - when nil then Version.detect(url, specs) + when nil then Version.detect(url, **specs) when String then Version.create(val) when Version then val else diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb index 51a8641b820798eb91d7897603b56a18f220acb5..38bcf00cb4c474f32a54c736727bac2b8bfe7447 100644 --- a/Library/Homebrew/test/version_spec.rb +++ b/Library/Homebrew/test/version_spec.rb @@ -63,17 +63,19 @@ describe Version::NULL do end end -describe Version::NullToken do - specify "#inspect" do - expect(subject.inspect).to eq("#<Version::NullToken>") - end +describe Version do + describe "::NULL_TOKEN" do + subject { described_class::NULL_TOKEN } - it "is equal to itself" do - expect(subject).to be == described_class.new + specify "#inspect" do + expect(subject.inspect).to eq("#<Version::NullToken>") + end + + it "is equal to itself" do + expect(subject).to be == described_class::NULL_TOKEN + end end -end -describe Version do specify "comparison" do expect(described_class.create("0.1")).to be == described_class.create("0.1.0") expect(described_class.create("0.1")).to be < described_class.create("0.2") @@ -259,9 +261,17 @@ describe Version do end end - specify "#detected_from_url?" do - expect(described_class.create("1.0")).not_to be_detected_from_url - expect(Version::FromURL.new("1.0")).to be_detected_from_url + describe "#detected_from_url?" do + it "is false if created explicitly" do + expect(described_class.new("1.0.0")).not_to be_detected_from_url + end + + it "is true if the version was detected from a URL" do + version = described_class.detect("https://example.org/archive-1.0.0.tar.gz") + + expect(version).to eq "1.0.0" + expect(version).to be_detected_from_url + end end specify "#head?" do @@ -363,9 +373,9 @@ describe Version do end describe "::detect" do - matcher :be_detected_from do |url, specs = {}| + matcher :be_detected_from do |url, **specs| match do |expected| - @detected = described_class.detect(url, specs) + @detected = described_class.detect(url, **specs) @detected == expected end diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb index ae2aec8bb1bc61cf33436a2de4654cafeb537611..daa83bf73e57d5fc7edb4a408f1b6948f0219952 100644 --- a/Library/Homebrew/version.rb +++ b/Library/Homebrew/version.rb @@ -2,6 +2,9 @@ require "version/null" +# A formula's version. +# +# @api private class Version include Comparable @@ -9,6 +12,7 @@ class Version /#{"^" if full}#{Regexp.escape(name)}(@\d[\d.]*)?#{"$" if full}/ end + # A part of a `Version`. class Token include Comparable @@ -67,9 +71,10 @@ class Version end end + # A pseudo-token representing the absence of a token. class NullToken < Token - def initialize(value = nil) - super + def initialize + super(nil) end def <=>(other) @@ -95,9 +100,12 @@ class Version "#<#{self.class.name}>" end end + private_constant :NullToken + # Represents the absence of a token. NULL_TOKEN = NullToken.new.freeze + # A token string. class StringToken < Token PATTERN = /[a-z]+/i.freeze @@ -119,6 +127,7 @@ class Version end end + # A token consisting of only numbers. class NumericToken < Token PATTERN = /[0-9]+/i.freeze @@ -146,12 +155,14 @@ class Version end end + # A token consisting of an alphabetic and a numeric part. class CompositeToken < StringToken def rev value[/[0-9]+/].to_i end end + # A token representing the part of a version designating it is an alpha release. class AlphaToken < CompositeToken PATTERN = /alpha[0-9]*|a[0-9]+/i.freeze @@ -169,6 +180,7 @@ class Version end end + # A token representing the part of a version designating it is a beta release. class BetaToken < CompositeToken PATTERN = /beta[0-9]*|b[0-9]+/i.freeze @@ -188,6 +200,7 @@ class Version end end + # A token representing the part of a version designating it is a pre-release. class PreToken < CompositeToken PATTERN = /pre[0-9]*/i.freeze @@ -207,6 +220,7 @@ class Version end end + # A token representing the part of a version designating it is a release-candidate. class RCToken < CompositeToken PATTERN = /rc[0-9]*/i.freeze @@ -226,6 +240,7 @@ class Version end end + # A token representing the part of a version designating it is a patch release. class PatchToken < CompositeToken PATTERN = /p[0-9]*/i.freeze @@ -252,19 +267,10 @@ class Version NumericToken::PATTERN, StringToken::PATTERN, ).freeze + private_constant :SCAN_PATTERN - class FromURL < Version - def detected_from_url? - true - end - end - - def self.detect(url, specs) - if specs.key?(:tag) - FromURL.parse(specs[:tag]) - else - FromURL.parse(url) - end + def self.detect(url, **specs) + parse(specs.fetch(:tag, url), detected_from_url: true) end def self.create(val) @@ -277,9 +283,9 @@ class Version end end - def self.parse(spec) + def self.parse(spec, detected_from_url: false) version = _parse(spec) - version.nil? ? NULL : new(version) + version.nil? ? NULL : new(version, detected_from_url: detected_from_url) end def self._parse(spec) @@ -424,14 +430,15 @@ class Version private_class_method :_parse - def initialize(val) + def initialize(val, detected_from_url: false) raise TypeError, "Version value must be a string; got a #{val.class} (#{val})" unless val.respond_to?(:to_str) @version = val.to_str + @detected_from_url = detected_from_url end def detected_from_url? - false + @detected_from_url end def head? @@ -550,10 +557,13 @@ class Version end end +# A formula's [HEAD version](https://docs.brew.sh/Formula-Cookbook#unstable-versions-head). +# +# @api private class HeadVersion < Version attr_reader :commit - def initialize(val) + def initialize(*) super @commit = @version[/^HEAD-(.+)$/, 1] end diff --git a/Library/Homebrew/version/null.rb b/Library/Homebrew/version/null.rb index b15546a6cb914b09cb4dc6a676bd663e8cbb1063..7b63523b43c446c8781cfff6df4ecebf9df1fe9f 100644 --- a/Library/Homebrew/version/null.rb +++ b/Library/Homebrew/version/null.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Version + # Represents the absence of a version. NULL = Class.new do include Comparable