Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Archive & Distribute iOS app to App Store via command line

Normally when submitting an iOS app to the App Store I do Product -> Archive from Xcode and then choose distribute to the App Store. I can successfully archive a build with:

xcodebuild -scheme "myScheme" archive -archivePath /my/path/myArchive

but how do I do the signing process with the correct provisioning profile and also distribute via command line?

For ad hoc builds, I generate my ipa after archiving with:

xcodebuild -exportArchive -exportFormat IPA -archivePath myArchive.xcarchive -exportPath /my/path/myFile.ipa -exportProvisioningProfile 'my adhoc profile name'

But do I even need to generate an ipa when distributing to the app store? Either way, how do I do the signing with correct profile and distributing via command line?

like image 927
Adam Johns Avatar asked Feb 18 '14 17:02

Adam Johns


People also ask

What is a meaning of archive?

Definition of archive (Entry 1 of 2) 1 : a place in which public records or historical materials (such as documents) are preserved an archive of historical manuscripts a film archive also : the material preserved —often used in plural reading through the archives. 2 : a repository or collection especially of ...

What is an example of a archive?

An example of archive is to put away family photos into albums and keep in a special place. An archive is defined as a place to keep important information, documents, or objects. An example of an archive is a room in a library where old manuscripts are kept.

What does it mean to put something in archive?

archive Add to list Share. An archive is a collection of older things — documents, books, movies, or something else — that's meant to preserve them. Archives tell us about history. An archive involves old stuff — specifically, a collection of old stuff, often put together by a librarian.

What archive order means?

An archived order is an order that has been completed and the closed by the shopper or the admin. Archiving an order on an online shopping site or app is also different from just deleting your order.


1 Answers

See update for Xcode 8 at bottom of answer.

To answer the last part of the question first - Yes an App Store Provisioning Profile is needed to submit your app through iTunes connect. It will not pass the preverification steps unless it has a correct provisioning profile. You will need to create an App Store distribution profile in the Member Centre

Add iOS Provisioning Profile Screenshot

Select "App Store" and click on continue

The first part of the question is a little more difficult, as creating, signing and distributing archives and IPA files using command line tools is poorly documented. Implementing a scripted solution is full of pitfalls because tools don't behave as expected under some circumstances and a more detailed knowledge of the relationship between your developer account, your keychain, the signing certs and the provisioning profiles is required.

Here is a sample of a script that can be used to create an archive with an embedded Ad Hoc provisioning profile, create an IPA for Ad Hoc distribution. As a bonus the DSYMs zip file is created for upload to TestFlight. Then two more scripts are presented. The first will create an App Store version of the IPA from the existing xcarchive, the second will show how to modify an xcarchive so it can be resigned by a third party for Enterprise In House distribution.

This automated build script assumes that the Provisioning Profiles are available in a directory called ProvisioningProfiles checked in with the source code. It is also assumes the password to unlock the keychain holding the signing cert is stored in a protected file in the build users home directory.

#!/bin/sh

# SETME
# set to name of signing certification usually starts something like "iPhone Distribution: ...."
# (the associated private key must be available in the key store)
#
# use the command "security find-identity" to list all the possible values available
#
codeSignIdentity="iPhone Distribution"

# SETME
# set to location of Ad Hoc provisioning profile
# (this profile must have the codeSignIdentity specified above included in it)
#
provisioningProfile=ProvisioningProfiles/MyAppAdHocDistribution.mobileprovision

# The keychain needs to be unlocked for signing, which requires the keychain
# password. This is stored in a file in the build account only accessible to
# the build account user
if [ ! -f $HOME/.pass ] ; then
    echo "no keychain password file available"
    exit 1
fi

case `stat -L -f "%p" $HOME/.pass`
in
    *400) ;;
    *)
        echo "keychain password file permissions are not restrictive enough"
        echo "chmod 400 $HOME/.pass"
        exit 1
        ;;
esac

#
# turn off tracing if it is on for security command
# to prevent logging of password
#
case `set -o | grep xtrace`
in
    *on) xon=yes ;;
    *) xon=no ;;
esac

#
# unlock the keychain, automatically lock keychain on script exit
#
[ $xon == yes ] && set +x
security unlock-keychain -p `cat $HOME/.pass` $HOME/Library/Keychains/login.keychain
[ $xon == yes ] && set -x
trap "security lock-keychain $HOME/Library/Keychains/login.keychain" EXIT

#
# Extract the profile UUID from the checked in Provisioning Profile.
#
uuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $provisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the build,
# specifying which profile and signing identity to use for the archived app
#
cp -f $provisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision"

#
# Build the xcarchive - this will only be done once, will will then
# distribute it for Ad Hoc, App Store and Enterprise In House scenarios
# (profile must be specified by UUID for this step)
#
xcodebuild \
    -workspace MyApp.xcworkspace \
    -scheme MyApp \
    -archivePath build/MyApp.xcarchive \
    archive \
    PROVISIONING_PROFILE="$uuid" \
    CODE_SIGN_IDENTITY="$codeSignIdentity"

#
# Create a zip of the DSYMs for TestFlight
#
/usr/bin/zip -r MyApp.dSYM.zip build/MyApp.xcarchive/dSYMs/MyApp.app.dSYM

#
# now distribute the xcarchive using an Ad Hoc profile
# (for QA testing for example)
#
profileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        \`security cms -D -i $provisioningProfile\``

#
# The profile must be specified by name for this step
#
xcodebuild \
        -exportArchive \
        -exportFormat IPA \
        -archivePath build/MyApp.xcarchive \
        -exportPath MyAppForAdHoc.ipa \
        -exportProvisioningProfile "$profileName"

To redistribute the xcarchive with the App Store Distribution profile, re-export the xcarchive with a new profile (the signing identity is the same for both the Ad Hoc and the App Store profiles).

# SETME
# set to location of App Store provisioning profile
#
appStoreProvisioningProfile=ProvisioningProfiles/MyAppAppStoreDistribution.mobileprovision

#
# Extract the App Store profile UUID from the checked in Provisioning Profile.
#
uuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $appStoreProvisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the export,
# specifying which profile to use for the archived app
# (Profile must match with signing identity used to create xcarchive)
#
cp -f $appStoreProvisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision"

#
# Extract the enterprise profile name from the checked in App Store Provisioning Profile.
# and redistribute the xcarchive as an App Store ready IPA
#
profileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        \`security cms -D -i $appStoreProvisioningProfile\``

#
# Profile must be specified by name for this step
#
xcodebuild \
    -exportArchive \
    -exportFormat IPA \
    -archivePath build/MyApp.xcarchive \
    -exportPath MyAppForStore.ipa \
    -exportProvisioningProfile "$profileName"

Finally just to be complete, what if you want to resign the xcarchive with a new identity and provisioning profile? This might happen if you distribute xcarchives for in-house distribution to third party companies. The recipient needs to sign your xcarchive for distribution using their enterprise certificate. xcodebuild cannot be coerced into overwriting the existing code signature in the xcarchive, therefore codesign must be used directly.

# SETME
# set to name of enterprise signing certification usually starts something like
# "iPhone Distribution: ...."
#
# use the command "security find-identity" to list all the possible values available
#
enterpriseCodeSignIdentity="iPhone Distribution: Acme Ltd"

# SETME
# set to location of Enterprise In-House provisioning profile
# (this profile must be associated with the enterprise code signing identity)
#
enterpriseProvisioningProfile=ProvisioningProfiles/MyAppInHouseDistribution.mobileprovision

# SETME
# A resigning of the app with a different certificate requires a new bundle ID
# that is registered by the Enterprise and is included in the In-House distribution
# profile (This could be automatically extracted from the Enterprise In-House distribution
# profile, I leave that as an ETTR)
enterpriseBundleId="com.enterprise.myapp"

#
# Extract the enterprise profile UUID from the checked in Provisioning Profile.
#
euuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $enterpriseProvisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the build,
# specifying which profile and signing identity to use for the archived app
#
cp -f $enterpriseProvisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$euuid.mobileprovision"

#
# Copy, modify and resign the xcarchive ready for Enterprise deployment
# (has to be resigned as the production certificate is different for enterprise)
#
cp -Rp build/MyApp.xcarchive build/MyAppEnterprise.xcarchive

#
# Remove old code signature
#
rm -rf build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/_CodeSignature

#
# copy in the enterprise provisioning profile
#
cp $enterpriseProvisioningProfile \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/embedded.mobileprovision

#
# Modify the bundle id to that of the enterprise bundle id
#   
/usr/libexec/plistbuddy -c "Set:CFBundleIdentifier $enterpriseBundleId" \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/Info.plist

#
# resign the xcarchive with the enterprise code signing identity
#
/usr/bin/codesign -f -v -s $enterpriseCodeSignIdentity \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app 

#
# Update the DSYM bundle id and create a zip of the DSYMs for TestFlight (if applicable)
#
/usr/libexec/plistbuddy -c "Set:CFBundleIdentifier com.apple.xcode.dsym.${enterpriseBundleId}" \
        build/MyAppEnterprise.xcarchive/dSYMs/MyApp.app.dSYM/Contents/Info.plist
/usr/bin/zip -r MyAppEnterprise.dSYM.zip build/MyAppEnterprise.xcarchive/dSYMs/MyApp.app.dSYM

#
# Extract the enterprise profile Name from the checked in Provisioning Profile.
#
enterpriseProfileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        l\`security cms -D -i $enterpriseProvisioningProfile\``

#
# Profile must be specified by name for this step
#
xcodebuild \
    -exportArchive \
    -exportFormat IPA \
    -archivePath build/MyAppEnterprise.xcarchive \
    -exportPath MyAppEnterprise.ipa \
    -exportProvisioningProfile "$enterpriseProfileName"

If the script is being run from as a launchd daemon, see this answer https://stackoverflow.com/a/9482707/2351246 to solve the problem with accessing the login keychain from a launchd daemon.

UPDATE for OSX Mavericks and Yosemite

On OSX Mavericks (v10.9.5) and OSX Yosemite you may see code signing errors:

Codesign check fails : ...../MyApp.app: resource envelope is obsolete

Check this posting here for the cause xcodebuild - codesign -vvvv says"resource envelope is obsolete"

To implement the change suggested by Apple Support in the referenced post, run the following command:

 sudo perl -pi.bak -e 's/--verify"./--verify", "--no-strict",/ if /codesign.*origApp/;' `xcrun -sdk iphoneos -f PackageApplication`

UPDATE for Xcode8

In Xcode8, the procedure described in my previous answer no longer works with the new Automatically manage signing feature, so you will need to select manual signing to use this method.

If you wish to use automatic signing, here are some observations based on our attempts to get it working with both a IBM Jazz and a Jenkins a CI environment.

  1. It is possible if you have one CI machine to get auto code signing working. I found you had to create and assign a developer account to the instance of Xcode on the CI machine. This was a manual step and I found no way to import a developer profile from the commandline.

  2. If you use a distributed CI environment with multiple build machines, it just doesn't work well. First you have the issue above, you have to manually add a developer account to all instances of Xcode, and second, each of those accounts has to be a different Apple ID, otherwise you get certificate generation issues for the common build account (All machines are sharing an account which causes a collision in the developer certificate because it is tied to a specific machine).

We run a distributed Jenkins CI environment, so we stuck with manual signing, but the method of exporting IPA changed, the -exportOptionsPlist option must be used now.

Change the archiving command:

#
# Build the xcarchive - this will only be done once, will will then
# distribute it for Ad Hoc, App Store and Enterprise In House scenarios
#
xcodebuild \
    -workspace MyApp.xcworkspace \
    -scheme MyApp \
    -archivePath build/MyApp.xcarchive \
    archive 

The archive is signed with the iOS Developer certificate associated with the build account (so make sure it has one installed in the keychain). Now the archive can be exported to IPA format for Ad-hoc, Enterprise and App Store using the -exportOptionsPlist option to xcodebuild.

Create a file called exportAppStore.plist with the following contents and save it in your top level project directory.

<?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>app-store</string>
</dict>
</plist>

See the output xcodebuild -help for a complete list of keys available for the -exportOptionsPlist option.

Now modify the export archive command to use the new export options plist file

xcodebuild \
    -exportArchive \
    -archivePath build/MyApp.xcarchive \
    -exportOptionsPlist exportAppStore.plist \
    -exportPath MyAppForStore.ipa
like image 92
BitByteDog Avatar answered Sep 20 '22 21:09

BitByteDog