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