Dev Journal — Automate notarizing macOS apps

Dev Journal — Automate notarizing macOS appsZeplinBlockedUnblockFollowFollowingJan 9With the release of macOS Mojave, Apple introduced a notary service to validate macOS apps that are not distributed through App Store.

Although this process is currently optional, in a blog post published past October, Apple announced that Gatekeeper will require software to be notarized in an upcoming release.

This most likely means that in the near future, users will be prompted with a warning while launching apps that are not notarized.

Unlike the manual app review process, notarization service is an automated system that performs common security checks, scanning for malicious content, checking for code-signing issues and so on.

Compared to traditional App Store reviews, it only takes a few minutes and the issues are easier to reason about.

????Xcode 10 introduced a built-in feature to notarize apps from the Organizer window.

To learn more about this feature, you can follow Apple’s documentation.

Notarizing apps within Xcode is pretty useful, but it’s a manual task.

If you’re using a Continuous Integration environment like Bitrise, CircleCI or Travis CI, or merely want to simplify this process, this post will go into detail on how to automate notarizing a macOS app using command line tools.

We’ll also introduce a fastlane plugin simplifying this process into a one-liner, which we recently open sourced.

You can skip to the Notarizing via fastlane section, if prefer learning more about that.

Notarizing via Xcode Command Line ToolsAs an alternative to Xcode’s built-in feature, Apple also provides command line tools to notarize a macOS app.

Yet this process requires a handful of steps, mainly using tools xcrun altool and xcrun stapler.

Uploading to notary serviceInitially, we need to upload the built app to the notary service.

It’s not possible to upload .

app packages directly though, as they’re directories in disguise.

We can upload a zip instead.

zip command line tool should do the trick here but we usually prefer ditto for compressing files as it produces smaller packages:ditto -c -k –rsrc –keepParent Example.

app Example.

app.

zip☝️ If you’re uploading a .

dmg, an installer package or similar, you can skip the compression step.

To upload the package, we’ll use the –notarize-app subcommand of xcrun altool, which looks like so:xcrun altool –notarize-app -t osx -f Example.

app.

zip –primary-bundle-id <Bundle identifier> -u <Apple ID username> -p <Apple ID password> –output-format xmlNotice that we also need to pass the bundle identifier of the app — which you can read from the Info.

plist file, if necessary.

Specifically for CI environments, we’d most likely set the Apple ID password as a secret environment variable.

In that case, you can pass the environment variable name, instead of the password:-p @env:<Environment variable name>If the upload is successful, this command simply returns a request identifier.

As the notarization operation continues in the cloud, we can use this identifier to query the status.

As we specified in the arguments, the output will be returned in an XML format — more specifically in a property list format.

The request identifier we’re looking for is located in the notarization-upload object, under the RequestUUID key.

Querying status of the notarization operationAs the notarization operation continues, we now need to periodically query the status using xcrun altool:xcrun altool –notarization-info <Request identifier> -u <Apple ID username> -p <Apple ID password> –output-format xmlSimilarly, this command returns a property list as well.

Within the notarization-info object, we can switch over the Status field to see where the operation is:in progress: The operation is still in progress, so we can query again later.

Currently for Zeplin’s macOS app this step takes about 2 minutes, so checking back every minute or so seems to make sense (for now).

invalid: Notarization failed due to one or multiple issues.

In this case, we can download the log, which is a JSON file with details about all of the issues.

URL for the log file is located in the output of the command, under the LogFileURL key.

success: Notarization succeeded.

In this case, Apple still recommends reading through the log file we just mentioned, as there might be some warnings.

Stapling notary ticketNow that the app is successfully notarized, we have a final step where we need to staple the app with the notary ticket.

This is done via xcrun stapler:xcrun stapler staple Example.

appNotice that we need to staple the .

app package, not the zip.

So after stapling, we’ll need to compress the app again and we’re ready to distribute it!Notarizing via fastlaneWe’ve been using fastlane for quite some time to simplify Zeplin’s bootstrap, build and deployment tasks.

fastlane provides actions to simplify these repetitive tasks.

The different steps we discussed above makes the notarization process a perfect fit to be a custom fastlane action.

For the past couple of weeks, we’ve been working on a fastlane plugin that introduces a notarize action:zeplin/fastlane-plugin-notarizefastlane plugin to notarize a macOS app ????.

Contribute to zeplin/fastlane-plugin-notarize development by creating an…github.

comAfter installing the plugin following the instructions, we’ll add this line to our Fastfile which invokes the notarize action, after building the app:notarize(package: app_path)…and that’s basically it!.This action covers all the notarization steps mentioned above.

You can follow along the Ruby code to explore how it uploads the app and queries the result periodically until it’s complete.

In case of success, the action also staples the app with the notarization ticket and in case of failure, it prints the log file listing all of the issues.

Common notarization issuesIf you’ve been distributing your macOS app outside of the App Store so far, after sending your app to Apple for notarization for the first time, it’s likely that you’ll run into one or more issues.

Apple communicates these issues through a log file that looks like so:{ "archiveFilename": "Example.

app", "issues": [ { "message": "The signature of the binary is invalid.

", "path": "Example.

app/Contents/MacOS/Example", "severity": "error" } ], "jobId": "00000000-0000-0000-0000-000000000000", "logFormatVersion": 1, "status": "Invalid", "statusSummary": "Archive contains critical validation errors", "ticketContents": null, "uploadDate": "2019-01-09T18:23:04Z"}Here are a few common notarization issues and how to correct them:The signature does not include a secure timestamp.

Currently a secure timestamp is only included if you’re manually notarizing an app through Xcode.

If you’re using command line tools, you’ll likely need to add –timestamp as a custom code signing flag.

This can be done within the build settings of your project:As generating a secure timestamp requires an internet connection, you can avoid this flag for the debug configuration.

The executable does not have the hardened runtime enabled.

Enabling hardened runtime adds security restrictions to your app while allowing you to request specific exceptions.

Hardened runtime can be enabled directly from the Capabilities tab under your project settings:The executable requests the com.

apple.

security.

get-task-allow entitlement.

When you create a new macOS project, Xcode automatically adds the com.

apple.

security.

get-task-allow entitlement which facilitates debugging on systems using SIP.

In fact, while manually exporting and notarizing an app through Xcode, this entitlement is automatically removed.

If you’re using command line tools as we mentioned above, you’ll need to disable this from your project settings per configuration:Similarly, make sure to keep this setting enabled for the debug configuration though, to be able to debug your builds.

For a list of more notarization issues, you can check out Apple’s documentation which covers most.

Cheers to our lovely @berkcebi for the post — let us know what you think, leave a comment below.

????Zeplin is a connected space for product teams where they can share designs, generate specs, assets and code snippets, tailored for iOS, Android and Web.

.

. More details

Leave a Reply