Notarizing a command-line tool

Hi all,


I have a command-line tool that I need to notarize.


Well, actually, notarizing is the easy part. The tool is used inside an installer package, which is inside a dmg, and when we notarize the dmg, everything inside the dmg gets notarized transparantly, which is quite nice.


Except that for notarizing to pass, the tool needs secure timestamps and hardened runtime enabled.


Okay, so far so good. Documentation says that Xcode does this for us. When in debug mode, Xcode enables get-task-allow, and we can debug the tool. When in production mode (via archive/export), Xcode enables secure timestamps and removes get-task-allow.


Except, I'm having trouble exporting the tool for production. I can archive using `xcodebuild archive -scheme mytool -archivePath mytool`, and I get an Xcode Archive bundle that looks a lot like one would expect:


$ find mytool.xcarchive

mytool.xcarchive

mytool.xcarchive/Products

mytool.xcarchive/Products/usr

mytool.xcarchive/Products/usr/local

mytool.xcarchive/Products/usr/local/bin

mytool.xcarchive/Products/usr/local/bin/mytool

mytool.xcarchive/dSYMs

mytool.xcarchive/dSYMs/mytool.dSYM

mytool.xcarchive/dSYMs/mytool.dSYM/Contents

mytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resources

mytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resources/DWARF

mytool.xcarchive/dSYMs/mytool.dSYM/Contents/Resources/DWARF/mytool

mytool.xcarchive/dSYMs/mytool.dSYM/Contents/Info.plist

mytool.xcarchive/Info.plist


But then when I try to export, I get:


$ xcodebuild -exportArchive -archivePath mytool.xcarchive -exportPath prod-mytool -exportOptionsPlist mytool_codesigning_options.plist

** EXPORT FAILED **


2020-02-13 06:05:23.453 xcodebuild[54323:1508625] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/mz/kgcq5n9j7yn2s9v0ch850tz80000gq/T/mytool_2020-02-13_06-05-23.453.xcdistributionlogs'.

2020-02-13 06:05:23.474 xcodebuild[54323:1508625] [MT] IDEDistributionMethodManager: -[IDEDistributionMethodManager orderedDistributionMethodsForTask:archive:]: Error = Error Domain=IDEDistributionMethodManagerErrorDomain Code=2 "Unknown Distribution Error" UserInfo={NSLocalizedDescription=Unknown Distribution Error}

error: exportArchive: exportOptionsPlist error for key 'method': expected one of {}, but found developer-id


Error Domain=IDEFoundationErrorDomain Code=1 "exportOptionsPlist error for key 'method': expected one of {}, but found developer-id" UserInfo={NSLocalizedDescription=exportOptionsPlist error for key 'method': expected one of {}, but found developer-id}


Google says that the error is caused by the Xcode Archive not containing a normal app, and that you can't export a tool.


Can I export this tool?


If not, then is it still possible to take advantage of Xcode's automatic debug/production switching behavior even though I need secure timestamps and the hardened runtime turned on in production?

Replies

I've never exported a tool. From the error message, it looks like it doesn't like the contents of you options plist. Can you post the contents?
Normally I just build in release mode and copy the executable to where it needs to go. You could manually code sign at that point to give it the time stamp and hardened runtime.

Here's the contents of my options plist:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>developer-id</string>
</dict>
</plist>


What you suggest is exactly what I'm doing at the moment; the apps I can build using archive/export, and Xcode does most of everything automatically. For the tool, I added --timestamp to OTHER_CODE_SIGN_FLAGS (only in Release mode), and I set CODE_SIGN_INJECT_BASE_ENTITLEMENTS to NO (only in Release mode), and then I simply build it in Release mode. This approach works just fine, but results in some forking of logic in our build scripts. To clean up our build scripts and to standardize how each Xcode target is built, I was hoping I could use archive/export for both apps and tools.

My guess is that export archive doesn't support command line tools. I tried a simple HelloWorld tool and got the same results you did. I tried playing around with options but it always seems to say that there are no valid methods at all.

Can I export this tool?

Not using

-exportArchive
. In situations like this I usually create a small script that takes as its input the
.xcarchive
and outputs a file that’s ‘notarisation ready’ (typically a disk image). For hints and tips on how to manually sign your tool, see my [Signing a Mac Product For Distribution][ref] post.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I've noticed that in the case of apps exported from an archive using -exportArchive, the exported copy is different than the copy inside the archive. I don't know what the mutation is, but I'm assuming it's important.


Unlike an app, can you yank a tool out of an archive with cp or ditto?

I've noticed that in the case of apps exported from an archive using

-exportArchive
, the exported copy is different than the copy inside the archive.
-exportArchive
re-signs the app, which changes it.

can you yank a tool out of an archive with

cp
or
ditto
?

Yes.

Unlike an app

This is not unlike an app. You can do this with an app as well, it’s just that with apps you can take advantage of

-exportArchive
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"