-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
/
bundle_version.rb
116 lines (84 loc) 路 3.79 KB
/
bundle_version.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# typed: true
# frozen_string_literal: true
require "system_command"
module Homebrew
# Representation of a macOS bundle version, commonly found in `Info.plist` files.
class BundleVersion
include Comparable
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
from_info_plist_content(plist)
end
sig { params(plist: T::Hash[String, T.untyped]).returns(T.nilable(T.attached_class)) }
def self.from_info_plist_content(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)
require "rexml/document"
xml = REXML::Document.new(package_info_path.read)
bundle_version_bundle = xml.get_elements("//pkg-info//bundle-version//bundle").first
bundle_id = bundle_version_bundle["id"] if bundle_version_bundle
return if bundle_id.blank?
bundle = xml.get_elements("//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)
# Remove version from short version, if present.
short_version = short_version&.sub(/\s*\(#{Regexp.escape(version)}\)\Z/, "") if 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)
return super unless instance_of?(other.class)
make_version = ->(v) { v ? Version.new(v) : Version::NULL }
version = self.version.then(&make_version)
other_version = other.version.then(&make_version)
difference = version <=> other_version
# If `version` is equal or cannot be compared, compare `short_version` instead.
if difference.nil? || difference.zero?
short_version = self.short_version.then(&make_version)
other_short_version = other.short_version.then(&make_version)
return short_version <=> other_short_version
end
difference
end
def ==(other)
instance_of?(other.class) && short_version == other.short_version && version == other.version
end
alias eql? ==
# Create a nicely formatted version (on a best effort 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
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