Archiving iOS projects from command line using xcodebuild and xcrun

5.00 avg. rating (94% score) - 1 vote

In one of my recent projects, I need to provided 16 different builds from a single iOS application code base. Although the different builds targeting different customers, having different application names, server addresses and build configurations have already been configured as different schemes in xCode, having to perform these builds manually using xCode is still time consuming and error-prone.

Generating IPA file from command line

Fortunately, cleaning, building, archiving and exporting an iOS project to IPA file can be done easily using the following xcodebuild commands (assuming xCode 5 is installed):

xcodebuild -project Reporter.xcodeproj -scheme “InternalTest” -configuration “Release Adhoc” clean

xcodebuild -archivePath “InternalTestRelease.xcarchive” -project Reporter.xcodeproj -sdk iphoneos  -scheme “InternalTest” -configuration “Release Adhoc” archive

xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile “My Release Profile” -archivePath “InternalTestRelease.xcarchive” -exportPath “InternalTestRelease.ipa”

After cleaning, the project is archived to a .xcarchive file, exported to an IPA file and signed using the given provisioning profile, ready to be distributed for internal testing.

Bug with xcodebuild

This seems easy. However, as with xCode (or many other Apple developer tools for that matter), xcodebuild comes with bugs, and sometimes hard-to-find ones. After a few round of testing, I realized that the generated signed IPA file would sometimes fail to be installed on the device. When attempting to install the IPA file, the iPhone configuration utility says “The executable was signed with invalid entitlements” with the following detailed messages in the console log:

Admin-iPhone installd[31] : 0x2ffee000 MobileInstallationInstall_Server: Installing app com.ios.testapp
Admin-iPhone installd[31] : 0x2ffee000 verify_signer_identity: MISValidateSignatureAndCopyInfo failed for /var/tmp/install_staging.9sdyqR/TestApp.app/TestApp: 0xe8008016
Admin-iPhone installd[31] : 0x2ffee000 do_preflight_verification: Could not verify executable at /var/tmp/install_staging.9sdyqR/TestApp.app

Basically the IPA file was not signed properly and could not be installed. What made this very strange is that although all the provisioning profiles and signing identities were configured correctly, the signing issue still occurred intermittently – one attempt would produce an incorrectly signed IPA file while the next attempt would produce a correctly signed IPA file that can be installed on the device. Frustrated, I decided to investigate and found out the root cause of the issue.

First I checked if the correct provisioning profile was used to sign the IPA file by extracting the file as if it was a ZIP file and searching the .app folder for a file called embedded.mobileprovision. It was indeed the correct signing profile – even when the generated IPA file was corrupted.

Secondly, I compared the extracted files from the correctly signed IPA package and the corrupted IPA package to see the differences. The digital signatures for the signed components can be found in a file named CodeResources, an XML-formatted file located in the .app folder, and they look like below:

<?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>files</key>
<dict>
<key>32x32_arrow.png</key>
<data>
P/XDWeYKpPpwSzLCFxSXV23inIQ=
</data>
<key>logo.jpg</key>
<data>
L+8Od1POJVPM7BFJPofhiR2rDso=
</data>
......
</dict>
</dict>
</plist>

After comparing the CodeResources file of the correct and corrupted IPA packages, I realized that most of the digital signatures were actually the same, with the only difference being the signature for the application executable file, e.g. TestApp. I checked the MD5 hashes of these two files and they were indeed different, explaining the difference in the signatures. This however did not yet explain the reason for the corrupted package, until I checked the MD5 hash of all the extracted files from both packages and realized that, although the generated core data model files (.mom and .omo) from the two packages were different, they both had the same digital signatures in CodeSignatures! This explained why the corrupted package failed the integrity check and could not be installed on the device. To verify this theory, I tried to overwrite the .omo and .mom files in the corrupted package with those from the correct IPA package while keeping the rest of the files the same. The modified IPA file could indeed be installed and run on the device. This showed that the incorrect digital signatures were indeed the issue.

But why is there such an issue? During my testing the corruption seemed to happen more often if xcodebuild is run repeatedly from command line to build one project after another. It does not happen that often if xcodebuild is run once in a while, or with sufficient delay between executions. Sounds kind of some caching issue, causing the program to use back the old digital signatures. The exact answer, of course, is only known by Apple.

The solution: xcrun

To fix this problem, we need to use xcrun, another tool with similar commands to build and export the project to an IPA file:

xcodebuild -project Reporter.xcodeproj -scheme “InternalTest” -configuration “Release Adhoc” clean

xcodebuild -project Reporter.xcodeproj -sdk iphoneos  -scheme “InternalTest” -configuration “Release Adhoc”

xcrun -sdk iphoneos PackageApplication -v “Internaltest/TestApp.app” -o “InternalTestRelease.ipa” –sign “iPhone Distribution: My Company Pte Ltd (XCDEFV)”

A minor inconvenience is that xcrun requires the exact provisioning identity, e.g iPhone Distribution: My Company Pte Ltd (XCDEFV)) and the full path to the application to be signed. The project would therefore need to be built first using xcodebuild, with the build output path specified in the “Per-configuration Build Products Path” settings and passed to xcrun via the -v parameter.

However, at least with xcrun, I encountered no signing issues after repeated testings, and the generated IPA packages can always be installed on the device successfully. 

5.00 avg. rating (94% score) - 1 vote
ToughDev

ToughDev

A tough developer who likes to work on just about anything, from software development to electronics, and share his knowledge with the rest of the world.

6 thoughts on “Archiving iOS projects from command line using xcodebuild and xcrun

  • July 12, 2014 at 6:50 pm
    Permalink

    Thanks for posting this info. I spent many frustrating hours before finding it.

  • August 14, 2014 at 7:25 am
    Permalink

    Holy smokes! I think you just saved me hours of agony. Thank you for describing this issue.

  • August 21, 2014 at 7:02 am
    Permalink

    Is there a way to get the provisioning identity name based off a provisioning profile? I'm in this same scenario except I have multiple signing keys

  • August 21, 2014 at 10:02 am
    Permalink

    Hi,

    Although the .mobileprovision profile file is a binary file, it contains readable XML text which describes the profile and determines certain properties for the plist file of the application being installed. To get the provisioning identity name from an adhoc release provisioning profile, open the file as text and look for the TeamIdentifier (which looks like BCD3JZ141M) and TeamName property (which looks like My Company Pte Ltd) towards the end of the readable XML part of the file, indicated by the < /plist > closing tag. From this you can form the signing identity name, something like "My Company Pte Ltd (BCD3JZ141M)". This process can be automated, for example, by usage of 'sed' from a bash shell script.

    I do not know of a way to get the signing identity of a debug profile (e.g. something like iPhone Developer: John Smith…) just by parsing that file – it contains no such information in clear text. It may be embedded in the DeveloperCertificates data section if it's indeed contained in the profile file. However I believe there is no need to know this as most of the time building and archiving the IPA file should be done with an adhoc release profile, and not with a debug profile.

  • August 21, 2014 at 11:30 pm
    Permalink

    great awesome. Here's a bash script I made if anyone reading this needed it:

    ORIGINAL_PROVISION="./appname.mobileprovision"
    security cms -D -i $ORIGINAL_PROVISION > tmp.plist
    TMP_FILE="./tmp.plist"

    result=()
    for var in TeamName TeamIdentifier ; do
    val=$( /usr/libexec/PlistBuddy -c "Print $var" "$TMP_FILE" )
    declare -x $var="$val"
    for name in ${val[@]} ; do
    if [[ "${name}" != "{" && "${name}" != "}" && "${name}" != "Array" ]] ; then
    result+=("${name}")
    fi
    done
    done

    TEAM_NAME="${result[0]}"
    TEAM_IDENTIFIER="${result[1]}"

    echo $TEAM_NAME
    echo $TEAM_IDENTIFIER

  • August 21, 2014 at 11:32 pm
    Permalink

    Great, thank you for sharing your Bash script to get the signing identity name too :) I believe it will help others with similar problem.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>