Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Command line iOS project builds and over-the-air distribution
# command line OTA distribution references and examples
# Configuration
keychain_password="super secret"
scheme="Ad Hoc"
product_name="My App $environment_name"
provisioning_profile="iPhone Distribution: My Company, LLC"
function failed()
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
function validate_keychain()
# unlock the keychain containing the provisioning profile's private key and set it as the default keychain
security unlock-keychain -p "$keychain_password" "$HOME/Library/Keychains/$keychain.keychain"
security default-keychain -s "$HOME/Library/Keychains/$keychain.keychain"
#describe the available provisioning profiles
echo "Available provisioning profiles"
security find-identity -p codesigning -v
#verify that the requested provisioning profile can be found
(security find-certificate -a -c "$provisioning_profile" -Z | grep ^SHA-1) || failed provisioning_profile
function describe_sdks()
#list the installed sdks
echo "Available SDKs"
xcodebuild -showsdks
function describe_workspace()
#describe the project workspace
echo "Available schemes"
xcodebuild -list -workspace $workspace
function increment_version()
cd "MyApp"
agvtool -noscm new-version -all $build_number
cd ..
function set_environment()
#copy the info plist for the selected environment into place
cp -v "MyApp/$environment_info_plist" $info_plist || failed environment_plist
#copy the environment settings plist into place
cp -v "MyApp/$environment_plist" "MyApp/environment.plist" || failed environment
#extract settings from the Info.plist file
info_plist_domain=$(ls $info_plist | sed -e 's/\.plist//')
short_version_string=$(defaults read "$info_plist_domain" CFBundleShortVersionString)
bundle_identifier=$(defaults read "$info_plist_domain" CFBundleIdentifier)
echo "Environment set to $bundle_identifier at version $short_version_string"
function build_app()
local devired_data_path="$HOME/Library/Developer/Xcode/DerivedData"
#get the name of the workspace to be build, used as the prefix of the DerivedData directory for this build
local workspace_name=$(echo "$workspace" | sed -n 's/\([^\.]\{1,\}\)\.xcworkspace/\1/p')
#build the app
echo "Running xcodebuild > xcodebuild_output ..."
# disabled overriding PRODUCT_NAME, setting applies to all built targets in Xcode 4 which renames static library target dependencies and breaks linking
# xcodebuild -verbose -workspace "$workspace" -scheme "$scheme" -sdk iphoneos -configuration Release clean build PRODUCT_NAME="$product_name" >| xcodebuild_output
xcodebuild -verbose -workspace "$workspace" -scheme "$scheme" -sdk iphoneos -configuration Release clean build >| xcodebuild_output
if [ $? -ne 0 ]
tail -n20 xcodebuild_output
failed xcodebuild
#locate this project's DerivedData directory
local project_derived_data_directory=$(grep -oE "$workspace_name-([a-zA-Z0-9]+)[/]" xcodebuild_output | sed -n "s/\($workspace_name-[a-z]\{1,\}\)\//\1/p" | head -n1)
local project_derived_data_path="$devired_data_path/$project_derived_data_directory"
#locate the .app file
# infer app name since it cannot currently be set using the product name, see comment above
# project_app="$"
project_app=$(ls -1 "$project_derived_data_path/Build/Products/Release-iphoneos/" | grep ".*\.app$" | head -n1)
# if [ $(ls -1 "$project_derived_data_path/Build/Products/Release-iphoneos/$project_app" | wc -l) -ne 1 ]
if [ $(ls -1 "$project_derived_data_path/Build/Products/Release-iphoneos/" | grep ".*\.app$" | wc -l) -ne 1 ]
echo "Failed to find a single .app build product."
# echo "Failed to locate $project_derived_data_path/Build/Products/Release-iphoneos/$project_app"
failed locate_built_product
echo "Built $project_app in $project_derived_data_path"
#copy app and dSYM files to the working directory
cp -Rf "$project_derived_data_path/Build/Products/Release-iphoneos/$project_app" $project_dir
cp -Rf "$project_derived_data_path/Build/Products/Release-iphoneos/$project_app.dSYM" $project_dir
#rename app and dSYM so that multiple environments with the same product name are identifiable
echo "Retrieving build products..."
rm -rf $project_dir/$
rm -rf $project_dir/$
mv -f "$project_dir/$project_app" "$project_dir/$"
echo "$project_dir/$"
mv -f "$project_dir/$project_app.dSYM" "$project_dir/$"
echo "$project_dir/$"
#relink CodeResources, xcodebuild does not reliably construct the appropriate symlink
rm "$project_app/CodeResources"
ln -s "$project_app/_CodeSignature/CodeResources" "$project_app/CodeResources"
function sign_app()
echo "Codesign as \"$provisioning_profile\", embedding provisioning profile $mobileprovision"
#sign build for distribution and package as a .ipa
xcrun -sdk iphoneos PackageApplication "$project_dir/$project_app" -o "$project_dir/$project_app.ipa" --sign "$provisioning_profile" --embed "$mobileprovision" || failed codesign
function verify_app()
#verify the resulting app
codesign -d -vvv --file-list - "$project_dir/$project_app" || failed verification
function build_ota_plist()
echo "Generating $project_app.plist"
cat << EOF > $project_app.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">
<string>$short_version_string $build_number</string>
echo "**** Validate Keychain"
echo "**** Describe SDKs"
echo "**** Describe Workspace"
echo "**** Set Environment"
echo "**** Increment Bundle Version"
echo "**** Build"
echo "**** Package Application"
echo "**** Verify"
echo "**** Prepare OTA Distribution"
echo "**** Complete!"
**** Validate Keychain
Available provisioning profiles
1) 0b1a9aaa619c6022e2f0e91c46af5209 "iPhone Distribution: MyCompany, LLC"
1 valid identities found
SHA-1 hash: 0b1a9aaa619c6022e2f0e91c46af5209
**** Describe SDKs
Available SDKs
Mac OS X SDKs:
Mac OS X 10.6 -sdk macosx10.6
iOS 4.3 -sdk iphoneos4.3
iOS Simulator SDKs:
Simulator - iOS 3.2 -sdk iphonesimulator3.2
Simulator - iOS 4.0 -sdk iphonesimulator4.0
Simulator - iOS 4.1 -sdk iphonesimulator4.1
Simulator - iOS 4.2 -sdk iphonesimulator4.2
Simulator - iOS 4.3 -sdk iphonesimulator4.3
**** Describe Workspace
Available schemes
Information about workspace "My_App":
Ad Hoc
**** Set Environment
My_App/My_App/staging-Info.plist -> /Users/tester/TeamCity/build-agent-1/work/My_App_staging/My_App/My_App/My_App-Info.plist
My_App/My_App/staging.plist -> My_App/My_App/environment.plist
Environment set to com.MyCompany.My_App.staging at version 1.0
**** Increment Bundle Version
Setting version of project My_App to:
Also setting CFBundleVersion key (assuming it exists)
Updating CFBundleVersion in Info.plist(s)...
Updated CFBundleVersion in "My_App.xcodeproj/../My_App/My_App-Info.plist" to 24
Updated CFBundleVersion in "My_App.xcodeproj/../My_AppTests/My_AppTests-Info.plist" to 24
**** Build
Running xcodebuild > xcodebuild_output ...
Built My_App in /Users/tester/Library/Developer/Xcode/DerivedData/My_App-dnzbsdgcbuvddhcenlrdqhdfjjmh
Retrieving build products...
**** Package Application
Codesign as "iPhone Distribution: MyCompany, LLC", embedding provisioning profile /Users/tester/TeamCity/build-agent-1/work/My_App_staging/ad_hoc/My_App_Staging_Ad_Hoc.mobileprovision
**** Verify
Executable=/Users/tester/TeamCity/build-agent-1/work/My_App_staging/ staging
Format=bundle with Mach-O universal (armv6 armv7)
CodeDirectory v=20100 size=3515 flags=0x0(none) hashes=167+5 location=embedded
Signature size=4317
Authority=iPhone Distribution: MyCompany, LLC
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Jun 9, 2011 4:31:52 PM
Info.plist entries=33
/Users/tester/TeamCity/build-agent-1/work/My_App_staging/ staging
Sealed Resources rules=3 files=97
Internal requirements count=1 size=180
**** Prepare OTA Distribution
**** Complete!

This comment has been minimized.

Copy link
Owner Author

@jonah-williams jonah-williams commented Jun 9, 2011

Customize the variables in the "Configuration" section to match your app and build machine. You may also need to update the paths in "set_environment".

Once complete visit itms-services://?action=download-manifest&url=http://my_ci_server.example/path/to/project_app.plist to install ad hoc builds over the air. Requires that the installing device be added to your ad hoc provisioning profile and running iOS 4 or later.


This comment has been minimized.

Copy link

@e28eta e28eta commented Oct 28, 2011

As far as I can tell, setting the default keychain only affects which keychain new items are added to. I was having trouble because the keychain with my cert wasn't in the search list, and so wasn't picking up anything on it - despite setting the keychain to be the default.

I now have the following:

function backup_keychain_search_list() {
    KEYCHAIN_SEARCH_LIST=`security list-keychains | cut -f 2 -d"`

function use_and_validate_keychain {
    echo "Changing keychain search path to be ${full_keychain_path}"
    security list-keychain -s "${full_keychain_path}"
    echo "Changing default keychain to ${full_keychain_path}"
    security default-keychain -s "${full_keychain_path}"

function cleanup() {
    echo "Resetting the keychain search list"
    security list-keychains -s ${KEYCHAIN_SEARCH_LIST}

    echo "Resetting the default keychain to login"
    security default-keychain -s login.keychain

This comment has been minimized.

Copy link

@iAladdin iAladdin commented Apr 20, 2012

have you try to change this to suit XCode-runner?



This comment has been minimized.

Copy link

@rashmi1988 rashmi1988 commented Jul 18, 2013

Hi, I am trying to use this script for automating my iOS project. I have one concern here is, I am finding it tough to do it with a workspace based project. Because, I have only worked in project based templates in Xcode and thus finding it tough to find the paths.

Could you please tell me how can I make it work for a project based template. It will be great if I can do that...


This comment has been minimized.

Copy link

@ryanhanwu ryanhanwu commented Apr 17, 2014

Thank you, very useful script!


This comment has been minimized.

Copy link

@nsrinivas nsrinivas commented Oct 15, 2014

Hi jonah , Can You Please Exp-lance how to use this script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.