AutoPkg recipe writing: things to look out for

AutoPkg is a cool project for Mac admins (in theory, Windows admins could use it, too, and there are even a few Windows recipes). Although it's a flexible framework that can be applied in many different ways, what it's most useful for is automating the tedious process of going to a website, downloading a new version of the software, and then importing that download into whatever you're using to push updates out to your Mac clients.

For a while, I was using existing recipes (there are many, so this is a totally valid approach), but eventually there was software I didn't see recipes for, so I started writing my own recipes. At first, I just started by copying existing templates and just modifying certain parts (the download URL, or the regular expressions to search for within the search URL).

Here are some things I noticed, in case you ever want to write your own recipes and run into these issues.

Arguments need to be separate

I ran into this issue where I was trying to purge the destination before unarchiving a .zip file, but it didn't seem to be working. Even though the archive_path and destination_path seemed to work fine without being in the Arguments dictionary, the purge_destination key wasn't registering until I put them all into the Arguments dictionary, as I should have from the start... so, remember to always put all arguments in an actual Arguments dictionary. Example:

<dict>
<key>Processor</key>
<string>Unarchiver</string>
<key>Arguments</key>
<dict>
<key>purge_destination</key>
<true/>
<key>archive_path</key>
<string>%RECIPE_CACHE_DIR%/downloads/%NAME%.zip</string>
<key>destination_path</key>
<string>%RECIPE_CACHE_DIR%/%NAME%/</string>
</dict>
</dict>

Code signature verification within disk images

When you're doing code signature verification on a disk image, you don't have to explicitly use the DmgMounter processor to mount the disk image. Instead, you can just treat the .dmg as a folder that includes the bundle to be verified. Here's an example (where %pathname% refers to the downloaded .dmg):

<dict>
<key>Processor</key>
<string>CodeSignatureVerifier</string>
<key>Arguments</key>
<dict>
<key>input_path</key>
<string>%pathname%/DiskMaker*.app</string>
<key>requirement</key>
<string>identifier "net.gete.diskmakerx" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "2U4ZFMT67D"</string>
</dict>
</dict>

Dealing with regular expressions

If you're not a regex expert, some of the regular expression searches for the URLTextSearcher processor may look like gibberish to you.

A few tips to help with that, apart from (or maybe in addition to?) reading up on all the details of the Python regex documentation:

  • Before you put the regex into your recipe, you can test out your regex using Regex101 (select the Python one).
  • Generally speaking, the most useful thing I've found is creating a capture group with
    (?P<nameofcapturegroup>bunchofregex)
  • Just as you're about to put the regex into your recipe, make sure to substitute &lt; for < and &gt; for >