Skip to content

Instantly share code, notes, and snippets.

@hlung
Last active October 13, 2016 07:30
Show Gist options
  • Save hlung/7818585 to your computer and use it in GitHub Desktop.
Save hlung/7818585 to your computer and use it in GitHub Desktop.
A bash script to build on top of Shenzhen (https://github.com/nomad/shenzhen). Use specified targets of an Xcode iOS project.xcworkspace file, then upload to TestFlight.
#!/bin/bash
# TestFlight-upload.sh
# https://gist.github.com/hlung/7818585
#
# By Thongchai Kolyutsakul @hlungx
# Version: v1.0.3 - 27 Oct 2014
#
# Requirement: Shenzhen (https://github.com/nomad/shenzhen)
#
# A bash script that calls Shenzhen to build and upload to TestFlight.
# It builds schemes inside Xcode project.xcworkspace file.
# It also shows a summary of the number of succeeded/failed at the end of build and upload steps.
#============================================
# INPUT PARAMETERS - change to fit your needs
#============================================
# --- Feature options ---
# Example:
# - If you only want to build and not upload, set "DEBUG_TF_SKIP_UPLOAD=1"
# - If you want to only upload the already built ipa files, set "DEBUG_SKIP_BUILD=1"
DEBUG_SKIP_BUILD_STEP=0 # set to 1 if you don't want to clean and rebuild ipa files
DEBUG_SKIP_TESTFLIGHT_STEP=0 # set to 1 if you don't want the built ipa files to be uploaded
DEBUG_TF_SKIP_NOTIFY_USERS=1 # set to 1 to add --notify option, send notification emails to users after upload (optional)
DEBUG_TF_REPLACE_BUILD=0 # set to 1 to add --replace option, new build will appear with the same build version and same date (optional)
# Setup paths, relative to this script's position
PATH_XCODE_WORKSPACE="../Fingi.xcworkspace"
PATH_OUTPUT="../build"
# --- Build options ---
SDK='iphoneos' # SDK version to build with. 'iphoneos' uses latest SDK.
SCHEMES=('Put your app build scheme here') # all schemes to build, e.g. SCHEMES=('MyProj Staging' 'MyProj Production')
# --- TestFlight options --- https://testflightapp.com/api/doc/
source ~/.bash_profile # load .bash_profile to get exported environment varables
TF_API_TOKEN='YOUR_TESTFLIGHT_API_TOKEN'
TF_TEAM_TOKEN='YOUR_TESTFLIGHT_TEAM_TOKEN'
# TF_DISTRIBUTION_LISTS is required, comma separated.
# Too bad there's no 'all' option. We can just put all lists here because permitted devices are already specified in the provisioning profile.
TF_DISTRIBUTION_LISTS='List 1, List 2'
TF_NOTE='build 1
- Your release notes here
'
#============================================
cd "$PWD" # cd to this script's directory
SCHEMES_COUNT=(${#SCHEMES[*]})
declare -a FAILED_BUILDS=()
declare -a FAILED_UPLOADS=()
#============
# Build
#============
if [ $DEBUG_SKIP_BUILD_STEP -eq 0 ]; then
# Clean output files
echo "*** Remove output files in directory: $PATH_OUTPUT"
# do dangerous stuff
mkdir -p $PATH_OUTPUT # create directory if not exists
pushd "$PATH_OUTPUT" # use double quotes so path with space works
rm *.ipa
rm *.app.dSYM.zip
popd
# build each scheme, creating ipa and .dSYM.zip files
echo
echo "*** Start building ..."
echo "Project dir: $PATH_XCODE_WORKSPACE"
printf "All schemes ($SCHEMES_COUNT):"
printf " \"%s\"" "${SCHEMES[@]}"
echo
i=0
number_of_successful_builds=0
for SCHEME in "${SCHEMES[@]}"; do
i=`expr $i + 1` # increment
echo
echo Building scheme "($i/$SCHEMES_COUNT)": $SCHEME
echo " running (quotes omitted) > "ipa build -w "$PATH_XCODE_WORKSPACE" -s "$SCHEME" -c release --sdk "$SDK" -d "$PATH_OUTPUT" --no-archive
# Note: `ipa build` will clean and archive by default
# Hardcoded arguments: -c release, --no-archive
OUTPUT=$(ipa build -w "$PATH_XCODE_WORKSPACE" -s "$SCHEME" -c release --sdk "$SDK" -d "$PATH_OUTPUT" --no-archive)
echo
echo $OUTPUT
echo
success=$(grep -o "successfully built" <<< "$OUTPUT" | wc -l)
if [ $success -eq 1 ]; then
number_of_successful_builds=`expr $number_of_successful_builds + 1` # increment
else
FAILED_BUILDS+=("$SCHEME")
fi
done
echo "** $number_of_successful_builds of $SCHEMES_COUNT schemes successfully built **"
echo "${FAILED_UPLOADS[@]}"
else
echo "*** SKIPPING build step ..."
fi
#============
# TesteFlight
#============
if [ $DEBUG_SKIP_TESTFLIGHT_STEP -eq 0 ]; then
# list all ipa files
cd $PATH_OUTPUT
IPA_FILES=()
for f in *.ipa; do
mod=${f//.ipa/}
IPA_FILES+=("$mod")
done
IPA_FILES_COUNT=${#IPA_FILES[*]}
i=0
number_of_successful_uploads=0
# Optional arguments
# DO NOT wrap flags in double quotes when in execution line
if [ $DEBUG_TF_REPLACE_BUILD -eq 0 ]; then
TF_ARG_REPLACE=""
else
TF_ARG_REPLACE=" --replace"
fi
if [ $DEBUG_TF_SKIP_NOTIFY_USERS -eq 0 ]; then
TF_ARG_NOTIFY=" --notify"
else
TF_ARG_NOTIFY=""
fi
echo
echo "*** Start uploading ..."
for f in "${IPA_FILES[@]}"; do
if [ "$f" == "*" ]; then
echo "No ipa file found."
break
fi
i=`expr $i + 1` # increment
ipa=$f".ipa"
dsym=$f".app.dSYM.zip"
echo -e Uploading "($i/$IPA_FILES_COUNT)": \"$ipa\" with \"$dsym\"
echo " running (quotes omitted) > "ipa distribute:testflight -f "$ipa" -d "$dsym" -l "$TF_DISTRIBUTION_LISTS" --notes "$TF_NOTE" -a $TF_API_TOKEN -T $TF_TEAM_TOKEN$TF_ARG_NOTIFY$TF_ARG_REPLACE
echo
OUTPUT=$(ipa distribute:testflight -f "$ipa" -d "$dsym" -l "$TF_DISTRIBUTION_LISTS" --notes "$TF_NOTE" -a $TF_API_TOKEN -T $TF_TEAM_TOKEN$TF_ARG_NOTIFY$TF_ARG_REPLACE)
success=$(grep -o "successfully uploaded" <<< "$OUTPUT" | wc -l)
if [ $success -eq 1 ]; then
number_of_successful_uploads=`expr $number_of_successful_uploads + 1` # increment
else
FAILED_UPLOADS+=("$ipa")
fi
done
echo
echo "** $number_of_successful_uploads of $IPA_FILES_COUNT ipa files successfully uploaded **"
fi
echo
echo "[ SUMMARY ]"
if [ $DEBUG_SKIP_BUILD_STEP -eq 0 ]; then
if [ ${#FAILED_BUILDS[@]} -eq 0 ]; then
echo " ✓ All schemes are built."
else
printf " ✗ WARNING: failed to build schemes (${#FAILED_BUILDS[@]}):"
echo "${FAILED_BUILDS[@]}"
echo "Common cause: - try checking scheme name in project, it's not saved inside project file."
echo " - the *-Prefix.pch file was modified, try build with device."
fi
fi
if [ $DEBUG_SKIP_TESTFLIGHT_STEP -eq 0 ]; then
if [ ${#FAILED_UPLOADS[@]} -eq 0 ]; then
echo " ✓ All ipa files are uploaded."
else
printf " ✗ WARNING: failed to uploads ipa files (${#FAILED_UPLOADS[@]}):"
echo "${FAILED_UPLOADS[@]}"
echo "Common cause: - outdated shenzhen gem"
echo " - wrong provisioning profile"
fi
fi
@alex-schwartzman
Copy link

How did you manage to make Shenzhen to support distribute:testflight command?

ipa help
  ipa

  Build and distribute iOS apps (.ipa files)

  Commands:
    build                    Create a new .ipa file for your app
    distribute:crashlytics   Distribute an .ipa file over Crashlytics
    distribute:deploygate    Distribute an .ipa file over deploygate
    distribute:fir           Distribute an .ipa file over fir.im
    distribute:ftp           Distribute an .ipa file over FTP
    distribute:hockeyapp     Distribute an .ipa file over HockeyApp
    distribute:itunesconnect Upload an .ipa file to iTunes Connect
    distribute:pgyer         Distribute an .ipa file over Pgyer
    distribute:rivierabuild  Distribute an .ipa file over RivieraBuild
    distribute:s3            Distribute an .ipa file over Amazon S3
    distribute:testfairy     Distribute an .ipa file over TestFairy
    help                     Display global or [command] help documentation
    info                     Show mobile provisioning information about an .ipa file

  Aliases:
    distribute:sftp distribute:ftp --protocol sftp

  Global Options:
    --verbose             
    -h, --help           Display help documentation 
    -v, --version        Display version information 
    -t, --trace          Display backtrace when an error occurs 

  Author:
    Mattt Thompson <m@mattt.me>

  Website:
    http://mattt.me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment