-
-
Save jonah-williams/949831 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# https://gist.github.com/949831 | |
# http://blog.carbonfive.com/2011/05/04/automated-ad-hoc-builds-using-xcode-4/ | |
# command line OTA distribution references and examples | |
# http://nachbaur.com/blog/how-to-automate-your-iphone-app-builds-with-hudson | |
# http://nachbaur.com/blog/building-ios-apps-for-over-the-air-adhoc-distribution | |
# http://blog.octo.com/en/automating-over-the-air-deployment-for-iphone/ | |
# http://www.neat.io/posts/2010/10/27/automated-ota-ios-app-distribution.html | |
project_dir=`pwd` | |
# Configuration | |
environment_name="staging" | |
keychain="ci_keys" | |
keychain_password="super secret" | |
workspace="MyApp.xcworkspace" | |
scheme="Ad Hoc" | |
info_plist="$project_dir/MyApp-Info.plist" | |
environment_plist="$environment_name.plist" | |
environment_info_plist="$environment_name-Info.plist" | |
product_name="My App $environment_name" | |
mobileprovision="$project_dir/ad_hoc/MyAppStaging.mobileprovision" | |
provisioning_profile="iPhone Distribution: My Company, LLC" | |
build_number="%env.BUILD_NUMBER%" | |
artifacts_url="http://my_ci_server.example/artifacts/$build_number" | |
display_image_name="Icon-57.png" | |
full_size_image_name="Icon-512.png" | |
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 ] | |
then | |
tail -n20 xcodebuild_output | |
failed xcodebuild | |
fi | |
#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="$product_name.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 ] | |
then | |
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 | |
fi | |
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/$bundle_identifier.app | |
rm -rf $project_dir/$bundle_identifier.app.dSYM | |
mv -f "$project_dir/$project_app" "$project_dir/$bundle_identifier.app" | |
echo "$project_dir/$bundle_identifier.app" | |
mv -f "$project_dir/$project_app.dSYM" "$project_dir/$bundle_identifier.app.dSYM" | |
echo "$project_dir/$bundle_identifier.app.dSYM" | |
project_app=$bundle_identifier.app | |
#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" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>items</key> | |
<array> | |
<dict> | |
<key>assets</key> | |
<array> | |
<dict> | |
<key>kind</key> | |
<string>software-package</string> | |
<key>url</key> | |
<string>$artifacts_url/$project_app.ipa</string> | |
</dict> | |
<dict> | |
<key>kind</key> | |
<string>full-size-image</string> | |
<key>needs-shine</key> | |
<true/> | |
<key>url</key> | |
<string>$artifacts_url/$full_size_image_name</string> | |
</dict> | |
<dict> | |
<key>kind</key> | |
<string>display-image</string> | |
<key>needs-shine</key> | |
<true/> | |
<key>url</key> | |
<string>$artifacts_url/$display_image_name</string> | |
</dict> | |
</array> | |
<key>metadata</key> | |
<dict> | |
<key>bundle-identifier</key> | |
<string>$bundle_identifier</string> | |
<key>bundle-version</key> | |
<string>$short_version_string $build_number</string> | |
<key>kind</key> | |
<string>software</string> | |
<key>subtitle</key> | |
<string>$environment_name</string> | |
<key>title</key> | |
<string>$project_app</string> | |
</dict> | |
</dict> | |
</array> | |
</dict> | |
</plist> | |
EOF | |
} | |
echo "**** Validate Keychain" | |
validate_keychain | |
echo | |
echo "**** Describe SDKs" | |
describe_sdks | |
echo | |
echo "**** Describe Workspace" | |
describe_workspace | |
echo | |
echo "**** Set Environment" | |
set_environment | |
echo | |
echo "**** Increment Bundle Version" | |
increment_version | |
echo | |
echo "**** Build" | |
build_app | |
echo | |
echo "**** Package Application" | |
sign_app | |
echo | |
echo "**** Verify" | |
verify_app | |
echo | |
echo "**** Prepare OTA Distribution" | |
build_ota_plist | |
echo | |
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 SDKs: | |
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": | |
Schemes: | |
Ad Hoc | |
Development | |
**** 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: | |
24. | |
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 staging.app in /Users/tester/Library/Developer/Xcode/DerivedData/My_App-dnzbsdgcbuvddhcenlrdqhdfjjmh | |
Retrieving build products... | |
/Users/tester/TeamCity/build-agent-1/work/My_App_staging/com.MyCompany.My_App.staging.app | |
/Users/tester/TeamCity/build-agent-1/work/My_App_staging/com.MyCompany.My_App.staging.app.dSYM | |
**** 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/com.MyCompany.My_App.staging.app/My_App staging | |
Identifier=com.MyCompany.My_App.staging | |
Format=bundle with Mach-O universal (armv6 armv7) | |
CodeDirectory v=20100 size=3515 flags=0x0(none) hashes=167+5 location=embedded | |
CDHash=eb62e8fe529a4dbdaef163509ffff137 | |
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/com.MyCompany.My_App.staging.app/My_App staging | |
Sealed Resources rules=3 files=97 | |
/Users/tester/TeamCity/build-agent-1/work/My_App_staging/com.MyCompany.My_App.staging.app/_CodeSignature/CodeResources | |
Internal requirements count=1 size=180 | |
**** Prepare OTA Distribution | |
Generating com.MyCompany.My_App.staging.app.plist | |
**** Complete! |
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
}
have you try to change this to suit XCode-runner?
Thanks!
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...
Thank you, very useful script!
Hi jonah , Can You Please Exp-lance how to use this script
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.