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. #