lib/jss/utility.rb in ruby-jss-1.5.3 vs lib/jss/utility.rb in ruby-jss-1.6.0b1
- old
+ new
@@ -48,20 +48,19 @@
10 => 5,
11 => 6,
12 => 6,
13 => 6,
14 => 6,
- 15 => 7,
- 16 => 12
+ 15 => 7
}
# Hash of 'major' => 'minor'
# The maximum minor release for macOS major.minor
# e.g. the highest release of 11 is 11.12
#
# 12 is the default for the current OS and higher
- # (and hoping apple doesn't release 11.13)
+ # (and hoping apple doesn't release, e.g., 11.13)
MAC_OS_MAXS = {
11 => 12,
12 => 12,
13 => 12,
14 => 12,
@@ -72,17 +71,17 @@
19 => 12,
20 => 12
}
# Converts an OS Version into an Array of equal or higher OS versions, up to
- # some non-existant max, hopefully far in the future, currently 20.12
+ # some non-existant max, hopefully far in the future, currently 20.12.10
#
# This array can then be joined with commas and used as the value of the
# os_requirements for Packages and Scripts.
#
# It's unlikely that this method, as written, will still be in use by
- # the release of macOS 20.12, but currently thats the upper limit.
+ # the release of macOS 20.12.10, but currently thats the upper limit.
#
# Hopefully well before then JAMF will implement a "minimum OS" in Jamf Pro
# itself, then we could avoid the inherant limitations in using a method like
# this.
#
@@ -91,17 +90,17 @@
# e.g. '10.16.12', '11.12', '12.12', etc.
#
# Apple has never released more than 11 updates to a version of macOS
# (that being 10.4), so hopefully 12 is enough
#
- # Since Big Sur might report itself as either '10.16.x' or '11.x', this method
+ # Since Big Sur might report itself as either '10.16' or '11.x.x', this method
# will allow for both possibilities, and the array will contain whatever
# iterations needed for both version numbers
#
# @param min_os [String] the mimimum OS version to expand, e.g. ">=10.9.4" or "11.1"
#
- # @return [Array] Nearly all potential OS versions from the minimum to 20.12
+ # @return [Array] Nearly all potential OS versions from the minimum to 20.12.10
#
# @example
# JSS.expand_min_os ">=10.9.4" # => returns this array
# # ["10.9.4",
# # "10.9.5",
@@ -117,67 +116,104 @@
def self.expand_min_os(min_os)
min_os = min_os.delete '>='
# split the version into major, minor and maintenance release numbers
major, minor, maint = min_os.split('.')
+ minor = 'x' if minor.nil? || minor == '0'
maint = 'x' if maint.nil? || maint == '0'
ok_oses = []
- # if major == 11 e.g. big sur, e.g. 10.16,
- # reset major & min so that we get coverage for both
- # 10.16 and 11, since clients may report it as either
- if major == '11'
- major = '10'
- maint = minor
- minor = '16'
- end
-
# Deal with 10.x.x up to 10.16
if major == '10'
- # if the maint release number is an "x" just start the list of OK OS's with it
- if maint == 'x'
- ok_oses << min_os
- # otherwise, start with it and explicitly add all maint releases for
- # that maint version. if unknown because its a current or future OS,
- # go as h
+ # In big sur with SYSTEM_VERSION_COMPAT
+ # set, it will only ever report as `10.16`
+ # So if major is 10 and minor is 16, ignore maint
+ # and start explicitly at '10.16'
+ if minor == '16'
+ ok_oses << '10.16'
+
+ # But for Catalina and below, we need to
+ # expand things out
else
- max_maint_for_minor = OS_TEN_MAXS[minor.to_i]
- (maint.to_i..max_maint_for_minor).each do |m|
- ok_oses << "#{major}.#{minor}.#{m}"
- end # each m
- end
+ # e.g. 10.14.x
+ # doesn't expand to anything
+ if maint == 'x'
+ ok_oses << "10.#{minor}.x"
- # now account for all macOS versions starting with 10.
- # up to 10.16.x
- ((minor.to_i + 1)..16).each do |v|
- ok_oses << "#{major}.#{v}.x"
- end # each v
+ # e.g. 10.15.5
+ # expand to 10.15.5, 10.15.6, 10.15.7
+ else
+ max_maint_for_minor = OS_TEN_MAXS[minor.to_i]
- # now reset major to 11, so we can continue with BigSur and up
+ (maint.to_i..max_maint_for_minor).each do |m|
+ ok_oses << "#{major}.#{minor}.#{m}"
+ end # each m
+ end # if maint == x
+
+ # now if we started below catalina, account for everything
+ # up to 10.15.x
+ if minor.to_i < 15
+ ((minor.to_i + 1)..15).each { |v| ok_oses << "10.#{v}.x" }
+ end
+
+ # and add big sur with SYSTEM_VERSION_COMPAT
+ ok_oses << '10.16'
+ end # if minor == 16
+
+ # now reset these so we can go higher
major = '11'
- minor = minor == '16' ? maint : 'x'
+ minor = 'x'
+ maint = 'x'
end # if major == 10
- # Now deal with Big Sur and higher, macOS 10.16/11
+ # if the min os is 11.0.0 or equiv, and we aven't added 10.16
+ # for SYSTEM_VERSION_COMPAT, add it now
+ if ['11', '11.x', '11.x.x', '11.0', '11.0.0'].include?(min_os) && !ok_oses.include?('10.16')
+ ok_oses << '10.16'
+ end
+
+ # e.g. 11.x, or 11.x.x
+ # expand to 11.x, 12.x, 13.x, ... 20.x
if minor == 'x'
- ok_oses << "#{major}.#{minor}"
- else
+ ((major.to_i)..20).each { |v| ok_oses << "#{v}.x" }
+
+ # e.g. 11.2.x
+ # expand to 11.2.x, 11.3.x, ... 11.12.x,
+ # 12.x, 13.x, ... 20.x
+ elsif maint == 'x'
+ # first expand the minors out to their max
+ # e.g. 11.2.x, 11.3.x, ... 11.12.x
max_minor_for_major = MAC_OS_MAXS[major.to_i]
- (minor.to_i..max_minor_for_major).each do |m|
- ok_oses << "#{major}.#{m}"
+ ((minor.to_i)..max_minor_for_major).each do |m|
+ ok_oses << "#{major}.#{m}.x"
end # each m
- end # if minor == x
- # now account for all macOS versions 11 and up
- ((major.to_i + 1)..MAC_OS_MAXS.keys.max).each do |v|
- ok_oses << "#{v}.x"
- end # each v
+ # then add the majors out to 20
+ ((major.to_i + 1)..20).each { |v| ok_oses << "#{v}.x" }
+ # e.g. 11.2.3
+ # expand to 11.2.3, 11.2.4, ... 11.2.10,
+ # 11.3.x, 11.4.x, ... 11.12.x,
+ # 12.x, 13.x, ... 20.x
+ else
+ # first expand the maints out to 10
+ # e.g. 11.2.3, 11.2.4, ... 11.2.10
+ ((maint.to_i)..10).each { |mnt| ok_oses << "#{major}.#{minor}.#{mnt}" }
+
+ # then expand the minors out to their max
+ # e.g. 11.3.x, ... 11.12.x
+ max_minor_for_major = MAC_OS_MAXS[major.to_i]
+ ((minor.to_i + 1)..max_minor_for_major).each { |min| ok_oses << "#{major}.#{min}.x" }
+
+ # then add the majors out to 20
+ ((major.to_i + 1)..20).each { |v| ok_oses << "#{v}.x" }
+ end
+
ok_oses
- end
+ end # def self.expand_min_os(min_os)
# Scripts and packages can have processor limitations.
# This method tests a given processor, against a requirement
# to see if the requirement is met.
#
@@ -272,23 +308,29 @@
raise JSS::InvalidDataError, 'Input must be a comma-separated String or an Array of Strings'
end # case
{ stringform: valstr, arrayform: valarr }
end # to_s_and_a
- # Parse a plist into a Ruby data structure.
- # This enhances Plist::parse_xml taking file paths, as well as XML Strings
- # and reading the files regardless of binary/XML format.
+ # Parse a plist into a Ruby data structure. The plist parameter may be
+ # a String containing an XML plist, or a path to a plist file, or it may be
+ # a Pathname object pointing to a plist file. The plist files may be XML or
+ # binary.
#
# @param plist[Pathname, String] the plist XML, or the path to a plist file
#
+ # @param symbol_keys[Boolean] should any Hash keys in the result be converted
+ # into Symbols rather than remain as Strings?
+ #
# @return [Object] the parsed plist as a ruby hash,array, etc.
#
- def self.parse_plist(plist)
+ def self.parse_plist(plist, symbol_keys: false)
+ require 'cfpropertylist'
+
# did we get a string of xml, or a string pathname?
case plist
when String
- return Plist.parse_xml plist if plist.include? '</plist>'
+ return CFPropertyList.native_types(CFPropertyList::List.new(data: plist).value, symbol_keys) if plist.include? '</plist>'
plist = Pathname.new plist
when Pathname
true
else
@@ -296,11 +338,32 @@
end # case plist
# if we're here, its a Pathname
raise JSS::MissingDataError, "No such file: #{plist}" unless plist.file?
- Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape(plist.to_s)}`.force_encoding('UTF-8')
+ CFPropertyList.native_types(CFPropertyList::List.new(file: plist).value, symbol_keys)
end # parse_plist
+
+ # Convert any ruby data to an XML plist.
+ #
+ # NOTE: Binary data is tricky. Easiest way is to pass in a
+ # Pathname or IO object (anything that responds to `read` and
+ # returns a bytestring)
+ # and then the CFPropertyList.guess method will read it and
+ # convert it to a Plist <data> element with base64 encoded
+ # data.
+ # For more info, see CFPropertyList.guess
+ #
+ # @param data [Object] the data to be converted, usually a Hash
+ #
+ # @return [String] the object converted into an XML plist
+ #
+ def self.xml_plist_from(data)
+ require 'cfpropertylist'
+ plist = CFPropertyList::List.new
+ plist.value = CFPropertyList.guess(data, convert_unknown_to_string: true)
+ plist.to_str(CFPropertyList::List::FORMAT_XML)
+ end
# Converts anything that responds to #to_s to a Time, or nil
#
# Return nil if the item is nil, 0 or an empty String.
#