In my Wednesday session for MacSysAdmin 2020 Online – “This One Goes to 11” – (http://macsysadmin.se/program/program.html) I talk about the implications of macOS Big Sur’s version numbering.
I didn’t talk in too much detail about how that might affect Munki admins specifically, and I’ll remedy that here.
Conditional items
Munki manifests support “conditional_items” (https://github.com/munki/munki/wiki/Conditional-Items) and Munki pkginfo items can also have “installable_conditions” (https://github.com/munki/munki/wiki/Pkginfo-Files#installable_condition). Both use Apple’s NSPredicate and the predicate syntax to define conditional comparisons (https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html).
A common thing to do in a conditional_item in a manifest is to say that you want to install “Foo” only if the macOS version is in a certain range. There is an os_vers
attribute, and it can be used to install “SomeVPNClient” only on machines running 10.14.x like so:
<dict>
<key>condition</key>
<string>os_vers BEGINSWITH "10.14"</string>
<key>managed_installs</key>
<array>
<string>SomeVPNClient</string>
</array>
</dict>
But this doesn’t really work very well if you want to, say, install on 10.14 and up. os_vers
is a string, and you can’t reliably do
os_vers >= "10.14"
because “10.9” is greater than “10.14” when doing string comparisons. You could do
os_vers BEGINSWITH "10.14" OR os_vers BEGINSWITH "10.15"
but that quickly gets unwieldy and you have to remember to keep adding new versions to the comparison.
Complex OS version comparisons
So many admins (including me) switched to using the os_vers_minor
and os_version_patch
attributes, which are integers:
<dict>
<key>condition</key>
<string>(os_vers_minor > 10) OR ((os_vers_minor == 10) AND (os_vers_patch >= 3))</string>
<key>optional_installs</key>
<array>
<string>DockerForMac</string>
</array>
</dict>
The intention here was to make DockerForMac available if macOS was greater than or equal to 10.10.3. We completely ignore os_vers_major
because we assumed it was always 10 and we didn’t need to check it. But now with macOS 11, we need to revise this comparison:
<dict>
<key>condition</key>
<string>(os_vers_major >= 11) OR (os_vers_major == 10 AND os_vers_minor > 10) OR (os_vers_major == 10 AND os_vers_minor == 10 AND os_vers_patch >= 3)</string>
<key>optional_installs</key>
<array>
<string>DockerForMac</string>
</array>
</dict>
Yuck. That rapidly grows unreadable and hard to maintain. While editing OS version comparisons like these, I’ve made far too many mistakes and had invalid predicate strings. There’s got to be a better way.
Using the Build Number
In this post: https://scriptingosx.com/2020/09/macos-version-big-sur-update/ – Armin Briegel suggests using the build number instead for most OS version comparisons in shell scripts. You can do the same thing in Munki conditional comparisons. Here’s how the above comparison would look if we use os_build_number
instead of trying to use os_ver_major
, os_vers_minor
, and os_vers_patch
:
<dict>
<key>condition</key>
<string>os_build_number > "14D"</string>
<key>optional_installs</key>
<array>
<string>DockerForMac</string>
</array>
</dict>
This is much shorter and more forward-compatible unless Apple changes the format of OS build numbers (which of course they could do).
The only disadvantage here is the need to map this style of version to the macOS “product version” or marketing numbers. To get the Darwin version number (before Big Sur), add 4 to the minor version, so Darwin 14 corresponds to macOS 10.10. The letter corresponds to the “patch” version: A is 0, B is 1, C is 2, etc. So “14D” corresponds to “10.10.3”. macOS Big Sur is Darwin version 20, no matter if you call it “macOS 10.16” or “macOS 11.0”. We’ll have to just hope that macOS 12 is Darwin version 21!
Caveats
When doing these comparisons, note that the actual build number will be something like “14D1234” and so you’ll want to use only > and < for most comparisons — the build number will never be equal to “14D”, it will only be greater or less than that. In other words, any real build number starting with “14D” will be greater than “14D”; a build number starting with “14C” will be less than “14D”.
This approach will break in about 80 years when the Darwin version goes to 100. I don’t expect to be doing this same job then, so I’ll leave it to the attendees and presenters of MacSysAdmin 2100 to figure out!