This One Goes to 11: macOS version comparisons and Munki

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 &gt; 10) OR ((os_vers_minor == 10) AND (os_vers_patch &gt;= 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 &gt;= 11) OR (os_vers_major == 10 AND os_vers_minor &gt; 10) OR (os_vers_major == 10 AND os_vers_minor == 10 AND os_vers_patch &gt;= 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 &gt; "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!

This One Goes to 11: macOS version comparisons and Munki

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s