Merge pull request #10029 from reitermarkus/bundle-version
Add `BundleVersion` class.
This commit is contained in:
		
						commit
						1ed31206bc
					
				
							
								
								
									
										92
									
								
								Library/Homebrew/bundle_version.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Library/Homebrew/bundle_version.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "system_command"
 | 
			
		||||
 | 
			
		||||
module Homebrew
 | 
			
		||||
  # Representation of a macOS bundle version, commonly found in `Info.plist` files.
 | 
			
		||||
  #
 | 
			
		||||
  # @api private
 | 
			
		||||
  class BundleVersion
 | 
			
		||||
    extend T::Sig
 | 
			
		||||
 | 
			
		||||
    extend SystemCommand::Mixin
 | 
			
		||||
 | 
			
		||||
    sig { params(info_plist_path: Pathname).returns(T.nilable(T.attached_class)) }
 | 
			
		||||
    def self.from_info_plist(info_plist_path)
 | 
			
		||||
      plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", info_plist_path]).plist
 | 
			
		||||
 | 
			
		||||
      short_version = plist["CFBundleShortVersionString"].presence
 | 
			
		||||
      version = plist["CFBundleVersion"].presence
 | 
			
		||||
 | 
			
		||||
      new(short_version, version) if short_version || version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(package_info_path: Pathname).returns(T.nilable(T.attached_class)) }
 | 
			
		||||
    def self.from_package_info(package_info_path)
 | 
			
		||||
      Homebrew.install_bundler_gems!
 | 
			
		||||
      require "nokogiri"
 | 
			
		||||
 | 
			
		||||
      xml = Nokogiri::XML(package_info_path.read)
 | 
			
		||||
 | 
			
		||||
      bundle_id = xml.xpath("//pkg-info//bundle-version//bundle").first&.attr("id")
 | 
			
		||||
      return unless bundle_id
 | 
			
		||||
 | 
			
		||||
      bundle = xml.xpath("//pkg-info//bundle").find { |b| b["id"] == bundle_id }
 | 
			
		||||
      return unless bundle
 | 
			
		||||
 | 
			
		||||
      short_version = bundle["CFBundleShortVersionString"]
 | 
			
		||||
      version = bundle["CFBundleVersion"]
 | 
			
		||||
 | 
			
		||||
      new(short_version, version) if short_version || version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T.nilable(String)) }
 | 
			
		||||
    attr_reader :short_version, :version
 | 
			
		||||
 | 
			
		||||
    sig { params(short_version: T.nilable(String), version: T.nilable(String)).void }
 | 
			
		||||
    def initialize(short_version, version)
 | 
			
		||||
      @short_version = short_version.presence
 | 
			
		||||
      @version = version.presence
 | 
			
		||||
 | 
			
		||||
      return if @short_version || @version
 | 
			
		||||
 | 
			
		||||
      raise ArgumentError, "`short_version` and `version` cannot both be `nil` or empty"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def <=>(other)
 | 
			
		||||
      [version, short_version].map { |v| v&.yield_self(&Version.public_method(:new)) } <=>
 | 
			
		||||
        [other.version, other.short_version].map { |v| v&.yield_self(&Version.public_method(:new)) }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Create a nicely formatted version (on a best effor basis).
 | 
			
		||||
    sig { returns(String) }
 | 
			
		||||
    def nice_version
 | 
			
		||||
      nice_parts.join(",")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T::Array[String]) }
 | 
			
		||||
    def nice_parts
 | 
			
		||||
      short_version = self.short_version
 | 
			
		||||
      version = self.version
 | 
			
		||||
 | 
			
		||||
      short_version = short_version&.delete_suffix("(#{version})") if version
 | 
			
		||||
 | 
			
		||||
      return [T.must(short_version)] if short_version == version
 | 
			
		||||
 | 
			
		||||
      if short_version && version
 | 
			
		||||
        return [version] if version.match?(/\A\d+(\.\d+)+\Z/) && version.start_with?("#{short_version}.")
 | 
			
		||||
        return [short_version] if short_version.match?(/\A\d+(\.\d+)+\Z/) && short_version.start_with?("#{version}.")
 | 
			
		||||
 | 
			
		||||
        if short_version.match?(/\A\d+(\.\d+)*\Z/) && version.match?(/\A\d+\Z/)
 | 
			
		||||
          return [short_version] if short_version.start_with?("#{version}.") || short_version.end_with?(".#{version}")
 | 
			
		||||
 | 
			
		||||
          return [short_version, version]
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      [short_version, version].compact
 | 
			
		||||
    end
 | 
			
		||||
    private :nice_parts
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										29
									
								
								Library/Homebrew/test/bundle_version_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Library/Homebrew/test/bundle_version_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "bundle_version"
 | 
			
		||||
 | 
			
		||||
describe Homebrew::BundleVersion do
 | 
			
		||||
  describe "#nice_version" do
 | 
			
		||||
    expected_mappings = {
 | 
			
		||||
      ["1.2", nil]            => "1.2",
 | 
			
		||||
      [nil, "1.2.3"]          => "1.2.3",
 | 
			
		||||
      ["1.2", "1.2.3"]        => "1.2.3",
 | 
			
		||||
      ["1.2.3", "1.2"]        => "1.2.3",
 | 
			
		||||
      ["1.2.3", "8312"]       => "1.2.3,8312",
 | 
			
		||||
      ["2021", "2006"]        => "2021,2006",
 | 
			
		||||
      ["1.0", "1"]            => "1.0",
 | 
			
		||||
      ["1.0", "0"]            => "1.0",
 | 
			
		||||
      ["1.2.3.4000", "4000"]  => "1.2.3.4000",
 | 
			
		||||
      ["5", "5.0.45"]         => "5.0.45",
 | 
			
		||||
      ["2.5.2(3329)", "3329"] => "2.5.2,3329",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expected_mappings.each do |(short_version, version), expected_version|
 | 
			
		||||
      it "maps (#{short_version.inspect}, #{version.inspect}) to #{expected_version.inspect}" do
 | 
			
		||||
        expect(described_class.new(short_version, version).nice_version)
 | 
			
		||||
          .to eq expected_version
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,29 +0,0 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "unversioned_cask_checker"
 | 
			
		||||
 | 
			
		||||
describe Homebrew::UnversionedCaskChecker do
 | 
			
		||||
  describe "::decide_between_versions" do
 | 
			
		||||
    expected_mappings = {
 | 
			
		||||
      [nil, nil]             => nil,
 | 
			
		||||
      ["1.2", nil]           => "1.2",
 | 
			
		||||
      [nil, "1.2.3"]         => "1.2.3",
 | 
			
		||||
      ["1.2", "1.2.3"]       => "1.2.3",
 | 
			
		||||
      ["1.2.3", "1.2"]       => "1.2.3",
 | 
			
		||||
      ["1.2.3", "8312"]      => "1.2.3,8312",
 | 
			
		||||
      ["2021", "2006"]       => "2021,2006",
 | 
			
		||||
      ["1.0", "1"]           => "1.0",
 | 
			
		||||
      ["1.0", "0"]           => "1.0",
 | 
			
		||||
      ["1.2.3.4000", "4000"] => "1.2.3.4000",
 | 
			
		||||
      ["5", "5.0.45"]        => "5.0.45",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expected_mappings.each do |(short_version, version), expected_version|
 | 
			
		||||
      it "maps (#{short_version}, #{version}) to #{expected_version}" do
 | 
			
		||||
        expect(described_class.decide_between_versions(short_version, version))
 | 
			
		||||
          .to eq expected_version
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "bundle_version"
 | 
			
		||||
require "cask/cask"
 | 
			
		||||
require "cask/installer"
 | 
			
		||||
 | 
			
		||||
@ -45,56 +46,6 @@ module Homebrew
 | 
			
		||||
      pkgs.count == 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(info_plist_path: Pathname).returns(T.nilable(String)) }
 | 
			
		||||
    def self.version_from_info_plist(info_plist_path)
 | 
			
		||||
      plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", info_plist_path]).plist
 | 
			
		||||
 | 
			
		||||
      short_version = plist["CFBundleShortVersionString"].presence
 | 
			
		||||
      version = plist["CFBundleVersion"].presence
 | 
			
		||||
 | 
			
		||||
      return decide_between_versions(short_version, version) if short_version && version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(package_info_path: Pathname).returns(T.nilable(String)) }
 | 
			
		||||
    def self.version_from_package_info(package_info_path)
 | 
			
		||||
      Homebrew.install_bundler_gems!
 | 
			
		||||
      require "nokogiri"
 | 
			
		||||
 | 
			
		||||
      xml = Nokogiri::XML(package_info_path.read)
 | 
			
		||||
 | 
			
		||||
      bundle_id = xml.xpath("//pkg-info//bundle-version//bundle").first&.attr("id")
 | 
			
		||||
      return unless bundle_id
 | 
			
		||||
 | 
			
		||||
      bundle = xml.xpath("//pkg-info//bundle").find { |b| b["id"] == bundle_id }
 | 
			
		||||
      return unless bundle
 | 
			
		||||
 | 
			
		||||
      short_version = bundle["CFBundleShortVersionString"]
 | 
			
		||||
      version = bundle["CFBundleVersion"]
 | 
			
		||||
 | 
			
		||||
      return decide_between_versions(short_version, version) if short_version && version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig do
 | 
			
		||||
      params(short_version: T.nilable(String), version: T.nilable(String))
 | 
			
		||||
        .returns(T.nilable(String))
 | 
			
		||||
    end
 | 
			
		||||
    def self.decide_between_versions(short_version, version)
 | 
			
		||||
      return short_version if short_version == version
 | 
			
		||||
 | 
			
		||||
      if short_version && version
 | 
			
		||||
        return version if version.match?(/\A\d+(\.\d+)+\Z/) && version.start_with?("#{short_version}.")
 | 
			
		||||
        return short_version if short_version.match?(/\A\d+(\.\d+)+\Z/) && short_version.start_with?("#{version}.")
 | 
			
		||||
 | 
			
		||||
        if short_version.match?(/\A\d+(\.\d+)*\Z/) && version.match?(/\A\d+\Z/)
 | 
			
		||||
          return short_version if short_version.start_with?("#{version}.") || short_version.end_with?(".#{version}")
 | 
			
		||||
 | 
			
		||||
          return "#{short_version},#{version}"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      short_version || version
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(T.nilable(String)) }
 | 
			
		||||
    def guess_cask_version
 | 
			
		||||
      if apps.empty? && pkgs.empty?
 | 
			
		||||
@ -120,7 +71,7 @@ module Homebrew
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        info_plist_paths.each do |info_plist_path|
 | 
			
		||||
          if (version = self.class.version_from_info_plist(info_plist_path))
 | 
			
		||||
          if (version = BundleVersion.from_info_plist(info_plist_path)&.nice_version)
 | 
			
		||||
            return version
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -149,7 +100,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
            package_info_path = extract_dir/"PackageInfo"
 | 
			
		||||
            if package_info_path.exist?
 | 
			
		||||
              if (version = self.class.version_from_package_info(package_info_path))
 | 
			
		||||
              if (version = BundleVersion.from_package_info(package_info_path)&.nice_version)
 | 
			
		||||
                return version
 | 
			
		||||
              end
 | 
			
		||||
            elsif packages.count == 1
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user