In its place is the hdiutil udifrez which writes legacy resources to a UDIF disk image. However, no matter what I've tried, it breaks our distribution disk images.
Specifically, what our release scripts used to do was this (simplified version):
This process starts with the application to release (PRODUCT_CLIENT_APP) and an existing Disk Image file (PROTO_IMAGE_SRC) that's all pretty (has a custom background, window size, ...).
The prototype disk image is duplicated, mounted, and the release app is copied onto it:
Code Block cp -f "${PROTO_IMAGE_SRC}" "${PROTO_IMAGE_WORK}" hdiutil attach "${PROTO_IMAGE_WORK}" cp -R "${PRODUCT_CLIENT_APP}" "${PROTO_VOLUME_DIR}"
The image is then unmounted and compressed
Code Block hdiutil detach "${PROTO_DEVICE}" hdiutil convert "${PROTO_IMAGE_WORK}" -format UDCO -o "${DMG_RELEASE_SIGNED}"
Here's where the license was attached. The disk image was converted to a forked disk image file, the resource file was compiled and attached, and then converted back to a flat disk image file.
Code Block hdiutil unflatten "${DMG_RELEASE_SIGNED}" Rez -a "${BUILD_TOOLS_DIR}/software_license.r" -o "${DMG_RELEASE_SIGNED}" hdiutil flatten "${DMG_RELEASE_SIGNED}"
The completed disk image is then signed and uploaded for notarization:
Code Block codesign --sign "${DMG_SIGN_IDENTITY}" --verbose=3 "${DMG_RELEASE_SIGNED}" xcrun altool --notarize-app --primary-bundle-id "${PRIMARY_BUNDLE_IDENTIFIER}" -f "${DMG_RELEASE_SIGNED}" ...
After noterization is complete, the signed image is stapled and verified:
Code Block cp "${DMG_RELEASE_SIGNED}" "${DMG_RELEASE_FINAL}" stapler staple --verbose "${DMG_RELEASE_FINAL}" spctl --assess --type open --context context:primary-signature --verbose=2 "${DMG_RELEASE_FINAL}"
This all used to work just fine, until the flatten and unflatten commands went away.
Since the hdiutil udifrez -rsrcfork option is either unimplemted or broken, we tried using the hdiutil udifrez -xml option. Since the XML file for this command isn't documented anywhere, we created one using the udifderez command using the license resources from a previous release.
Armed with this XML file, I replaced the (unflatten/Rez/flatten) license attachment step with this:
Code Block hdiutil udifrez -xml "${BUILD_TOOLS_DIR}/software_license.xml" '' "${DMG_RELEASE_SIGNED}"
(note the extra, blank, argument because there's apparently another bug—or documentation failure—in udifrez, and if omitted the command fails)
Everything proceeds swimmingly, through the signing and stapling, except the resulting file is broken. When you open the disk image the SLA appears. Agree to the license, you're presented with a failure dialog:
Code Block text Warning The following disk images couldn't be opened Image Reason release_2.2.3.dmg image data corrupt
If I leave out the single hdiutil udifrez -xml ... step, everything works (except that there's no attached license agreement).
I can't attach the license before the disk image is compressed, because it's the wrong format.
I can't attach the license after the disk image is signed or stapled, for obvious reasons.
So we're stuck. I can't find any way of attaching a SLA to a signed and notarized distribution disk image, using the current set of tools, without breaking the disk image.
TL;DR: remove the array identified by the key "blkx" from the extracted xml file before using it.
Read on for more details...
I examined the extracted xml file - which is a plist - and noticed that there are several sections to it, each section identified by a key. If it helps, the XPath to the section key is /plist/dict/key. There are other keys in the structure, but they are all nested deeper in arrays and dictionaries (I am quite ignorant of plists; there is probably a much better way to do this)
These are the sections I found, in order:
<key>LPic</key>
<key>STR#</key>
<key>TEXT</key>
<key>TMPL</key>
<key>blkx</key>
<key>styl</key>
When I used that modified plist as input for hdiutil udifrez -xml ..., the resulting image worked fine: displayed the EULA and opened as expected.
Note that based on what I have read elsewhere online, the "TEXT" section may be "RTF" or something else, depending on the format of the EULA. Yours is named with a ".r" extension; I'm not sure what format that would be, but you should be able to find the correct section for it.
You should be able to continue using your EULA file, instead of relying on updating the plist every time the EULA changes, instead keeping the plist as a template, and inserting the EULA into it as a build step. The EULA needs to be base64-encoded; I was careful to slavishly copy the format in the extracted plist, so I wrapped mine at 52 characters and indented it with three tabs characters.
My template looks like this (truncated for brevity):
Code Block ... <key>TEXT</key> <array> <dict> <key>Attributes</key> <string>0x0000</string> <key>Data</key> <data> ${ENGLISH_SLA} </data> <key>ID</key> <string>5000</string> <key>Name</key> <string>English SLA</string> </dict> </array> ...
I save it as my-eula.plist.template.
Note, again, that you may need to use a section other than "TEXT", depending on the format of your EULA.
And I use this process to insert the EULA into it:
Code Block # base64-encode the English-language EULA, breaking at character 52 because that's how it was done in the plist extracted by udifderez # And add three tab characters to the beginning of every line, slavishly following the format of the extracted plist ENGLISH_SLA="$(base64 -b 52 my-eula.txt | sed s$'/^\(.*\)$/\t\t\t\\1/')" # this is one way of doing token replacement, when the tokens are formatted as shell interpreter variables eval "cat >my-eula.plist <<EOF $(<my-eula.plist.template) EOF " # add the eula to the dmg # note that udifrez requires an argument after the plist and before any other documented argument. # I am using a blank argument, but behavior appears to be the same no matter what value is there hdiutil udifrez -xml my-eula.plist '' -quiet "${appname}-${appversion}.dmg" || { echo "failed to add the license to the image" 1>&2 exit 1 }