Skip to content

Instantly share code, notes, and snippets.

@jcayzac
Last active August 29, 2015 14:08
Show Gist options
  • Save jcayzac/f3c12499dff8431af209 to your computer and use it in GitHub Desktop.
Save jcayzac/f3c12499dff8431af209 to your computer and use it in GitHub Desktop.
CI build script for Xcode6.1/iOS8
#!/usr/bin/env bash -e -u -o pipefail
export LANG="en_US.UTF-8"
usage() {
cat <<EOT
NAME
${0##*/} -- Build iOS stuff.
SYNOPSIS
cd /some/git/repository
${0##*/} [options]
DESCRIPTION
${0##*/} builds iOS stuff defined in a '.ci' file at the root of the git repository working copy the current directory is part of. The format of this file is as follow:
--8<------8<------8<------8<------8<------8<------8<--
# This is a comment. Blank lines are also ok, as shown below:
# Each line represents a buildable.
# Fields below are tab-separated. Using space as a separator is not supported, as spaces can appear inside a field.
# Workspace Scheme Options
Subfolder/Foo.xcworkspace MyAppScheme1 profile=deadbabe-f000-1337-1001-dead13370473 arg2=.. arg3=..
Subfolder/Foo.xcworkspace MyAppScheme2 profile=deadbabe-f000-1337-1001-dead13370473 arg2=.. arg3=..
Subfolder/Foo.xcworkspace MyLibScheme
# vim:noet:ts=4:sw=4:ff=unix:
--8<------8<------8<------8<------8<------8<------8<--
The following options are available:
profile=<...> The identifier of the provisioning profile to use when signing apps. REQUIRED if the scheme is that of an app.
identity=<...> The identity to use when signing apps. Optional (Xcode will try to guess).
notest Skip unit tests for that buildable.
IMPORTANT
${0##*/} is not meant to be run concurrently. Running multiple insteances at the same time will spread havoc.
BUILD PHASES
When building stuff, the following happens:
1: SIMULATOR ENVIRONMENT GATHERING
Information about the available simulator runtimes and device types is gathered.
2: PRISTINE GIT ENFORCEMENT
If both GIT_URL and GIT_BRANCH (or GERRIT_REFSPEC) are defined, this phases turn the current working copy pristine and, if git submodules are being used, re-modularize it.
3: RELEASE NOTES GENERATION
This phase builds a unique version number using the current datetime, the build number (if the BUILD_NUMBER environment variable is defined) and the current commit's shortened SHA-1 identifier. It then gathers the changelog from the git history and creates release notes for the build.
4: PROCESSING OF THE DOTCI FILE
The project's '.ci' file gets processed, and each declared artifact is in turn built, analyzed, tested, uploaded to SauceLabs and/or TestFlight (optional) and archived.
4.1: BUILDABLE SETTINGS GATHERING
Information about the current buildable is gathered.
4.2: VERSION INJECTION
The version info generated in the RELEASE NOTES GENERATION phase above is injected into the buildable's Info.plist file.
4.3: BUILD FOR DEVICE
Binary for iOS devices is created. If the buildable is an app, it is also signed and archived. Note this phase is skipped if the buildable is a unit test bundle.
4.4: BUILD FOR SIMULATOR
Binary for the iOS simulator is created. If the buildable is an app, it gets archived.
4.5: UNIT TESTS
Tests are run for every available simulator runtime and device type combination, using temporary simulators that are destroyed after each has run the tests, unless the 'notest' option was specified.
4.6: UPLOAD TO SAUCELABS
[Optional] If the buildable is an app, and if SAUCELABS_CREDENTIALS is defined, the application is uploaded to SauceLabs.
4.7: UPLOAD TO TESTFLIGHT
[Optional] If the buildable is an app, and if TESTFLIGHT_API_TOKEN, TESTFLIGHT_DISTRIBUTION and TESTFLIGHT_TEAM_TOKEN are defined, the application is uploaded to TestFlight.
OPTIONS
Supported options:
--help, -h Print this message and exit.
--verbose, -v Be more verbose.
ENVIRONMENT
Supported environment variables.
BUILD_NUMBER If specified, will be included in the product's version number. Normally set by the CI environment.
GERRIT_REFSPEC If specified, will be used instead of the GIT_BRANCH. Normally set by JIRA's Gerrit Trigger plugin.
GIT_BRANCH If specified, the working copy will be made pristine before the build is started.
GIT_URL If specified, used when making the working copy pristine. Otherwise the remote currently tracking the specified branch is used.
PROXY_CREDENTIALS If specified, will be passed to 'curl' as the value of --proxy-user when trying to upload apps to SauceLabs or TestFlight.
SAUCELABS_BUCKET The bucket name to use for uploading to SauceLabs. The URL used for uploading will become https://saucelabs.com/rest/v1/storage/<SAUCELABS_BUCKET>/. If not specified the bucket will default to the username part of SAUCELABS_CREDENTIALS.
SAUCELABS_CREDENTIALS SauceLabs' credentials. Should be 'username:key'. If defined, apps will get uploaded to SauceLabs automatically.
TESTFLIGHT_API_TOKEN TestFlight API token. If specified along with TESTFLIGHT_TEAM_TOKEN and TESTFLIGHT_DISTRIBUTION, signed apps will be uploaded to TestFlight automatically.
TESTFLIGHT_DISTRIBUTION TestFlight distribution lists (comma-separated). If specified along with TESTFLIGHT_API_TOKEN and TESTFLIGHT_TEAM_TOKEN, signed apps will be uploaded to TestFlight automatically.
TESTFLIGHT_TEAM_TOKEN TestFlight team token. If specified along with TESTFLIGHT_API_TOKEN and TESTFLIGHT_DISTRIBUTION, signed apps will be uploaded to TestFlight automatically.
EOT
}
usage_and_exit() {
for sig in INT TERM EXIT; do trap $sig; done
usage
exit ${1:-}
}
while (($#))
do
case "$1" in
--verbose|-v)
VERBOSE=1
;;
--help|-h)
usage_and_exit
;;
esac
done
# Signal handler that reports failures and do bits of cleanup
sig_handler() {
declare RC=$? SIG=$1 CMD="$BASH_COMMAND"
# Don't fail anymore
set +e +u +o pipefail
if [ "$SIG" == "EXIT" ]
then
if (($RC))
then
# Command failed!
declare START=1 END=${#BASH_SOURCE[@]} I=0 FN=
printf "*** ERROR: [%s] exited with return code [%i]!\nStack:\n" "$CMD" $RC
for ((I=$START; I < $END; I++))
do
printf "%s[%i] %s()\n" "${BASH_SOURCE[$I]}" ${BASH_LINENO[$((I-1))]} "${FUNCNAME[$I]}"
done
# If the script has a "onfailure" function, call it
[ "$(type onfailure 2>&-)" != "onfailure is a function" ] || onfailure "$CMD" "$RC"
else
printf '\n%s\n' "𝕭𝖚𝖎𝖑𝖉 𝖘𝖚𝖈𝖈𝖊𝖊𝖉𝖊𝖙𝖍. 𝕿𝖍𝖔𝖚 𝖉𝖎𝖉𝖘𝖙 𝖜𝖊𝖑𝖑."
fi
fi
# Treat INT (i.e. Ctrl-C) as KILL and send to the whole process group
kill -s ${SIG/INT/KILL} 0
}
for sig in INT TERM EXIT
do
trap "sig_handler $sig" $sig
done
if ! git rev-parse --show-toplevel >&- 2>&-
then
printf '*** ERROR: Not a GIT working copy: "%s"\n\n' "$PWD"
usage_and_exit 1
fi
cd "$(git rev-parse --show-toplevel)"
ROOT_DIR="$PWD"
BUILD_DIR="$ROOT_DIR/build"
export TMPDIR="$BUILD_DIR/tmp"
fuckingkillthesimulatornow() {
killall iOS\ Simulator launchd_sim 2>&- || true
(launchctl list | grep CoreSimulatorService | cut -f 3 | xargs -n 1 launchctl remove) || true
sleep 5
}
# Collect available runtimes and devices
fuckingkillthesimulatornow
set +u
declare -a RUNTIMES=()
declare -a RUNTIME_VERSIONS=()
while IFS= read -r LINE
do
[[ ! "$LINE" =~ unavailable && "$LINE" =~ ^iOS\ [0-9\.]*\ \(([0-9\.]*).*\((com\.apple\.CoreSimulator\.[^\)]*) ]] || continue
RUNTIME_VERSIONS=("${RUNTIME_VERSIONS[@]}" "${BASH_REMATCH[1]}")
RUNTIMES=("${RUNTIMES[@]}" "${BASH_REMATCH[2]}")
done < <(xcrun simctl list runtimes)
printf "Found active runtime: [iOS %s]\n" "${RUNTIME_VERSIONS[@]}"
RUNTIME_COUNT=${#RUNTIMES[@]}
declare -a DEVICES=()
declare -a DEVICE_TYPES=()
while IFS= read -r LINE
do
[[ ! "$LINE" =~ unavailable && "$LINE" =~ ^(.*)\ \((com\.apple\.CoreSimulator\.[^\)]*) ]] || continue
DEVICES=("${DEVICES[@]}" "${BASH_REMATCH[1]}")
DEVICE_TYPES=("${DEVICE_TYPES[@]}" "${BASH_REMATCH[2]}")
done < <(xcrun simctl list devicetypes)
printf "Found active device type: [%s]\n" "${DEVICES[@]}"
DEVICE_COUNT=${#DEVICES[@]}
set -u
clean() {
rm -rf "$BUILD_DIR"
}
PRETTY=/bin/cat
if which xcpretty >&- 2>&-
then
PRETTY='xcpretty --no-utf -r junit'
fi
NCPU=$(sysctl -n hw.ncpu)
xcbare() {
declare WORKSPACE="$1"; shift
declare SCHEME="$1"; shift
[[ "$*" =~ .*-jobs\ *([0-9]+) ]] || declare JOBS="-jobs $NCPU"
rm -rf "$TMPDIR"
nice -n 19 xcrun xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-derivedDataPath "$BUILD_DIR/derived" \
${JOBS:-} \
CACHE_ROOT="$TMPDIR/cache" \
${1+"$@"}
}
xcnice() {
xcbare ${1+"$@"} | $PRETTY
return ${PIPESTATUS[0]}
}
#set -x
if [[ -n "${GERRIT_REFSPEC:-}" ]]
then
GIT_BRANCH="$GERRIT_REFSPEC"
fi
if [[ -n "${GIT_BRANCH:-}" ]]
then
# Makes the workspace pristine
git fetch --all
git reset --hard
git clean -fdx
git submodule foreach --recursive git reset --hard
git submodule foreach --recursive git clean -fdx
if [[ -n "${GIT_URL:-}" ]]
then
printf 'Fetching branch [%s] from remote [%s]:\n' "$GIT_BRANCH" "$GIT_URL"
git fetch "$GIT_URL" "$GIT_BRANCH"
git checkout FETCH_HEAD
else
printf 'Checking out branch [%s]:\n' "$GIT_BRANCH"
git checkout "$GIT_BRANCH"
fi
git submodule update --init --recursive --force
fi
if [ ! -f "$ROOT_DIR/.ci" ]
then
printf '*** ERROR: File not found: "%s"\n\n' "$ROOT_DIR/.ci"
usage_and_exit 1
fi
VERSION="$(/bin/date -u +'%Y%m%d.%H%M%S')-build${BUILD_NUMBER:--is-local}-commit$(git rev-parse --short HEAD)"
CHANGELOG="$(git log -n 50 --date=short --pretty=tformat:"%ad %w(0,0,4)%B" --no-merges | /usr/bin/sed -E 's/[[:space:]\n\r]*Change-Id: [[:alnum:]]*[[:space:]\r\n]*//g' | /usr/bin/grep -vE '^[[:space:]]*$')"
RELEASE_NOTES="$(printf 'Build version: %s\n%s \n\nChangelog (truncated):\n\n%s\n' "$VERSION" "CI: ${JOB_URL+${JOB_URL}${BUILD_NUMBER:-}}" "$CHANGELOG")"
printf '\n\n+-----------------+\n| RELEASE NOTES |\n+-----------------+\n\n%s\n' "$RELEASE_NOTES"
log() {
echo "$(date +'%b %d %H:%M:%S') $HOSTNAME cibuild[$!] <Debug>: [Line ${BASH_LINENO[1]}] $1" >>~/Library/Logs/CoreSimulator/CoreSimulator.log
}
process_buildable() {
declare BUILDABLE_WORKSPACE="$1"; shift
declare BUILDABLE_SCHEME="$1"; shift
declare BUILDABLE_PROFILE=
declare BUILDABLE_IDENTITY=
declare BUILDABLE_SHOULD_TEST=1
printf '\n\n\nPROCESSING %s…\n==============================================\n\n' "$BUILDABLE_WORKSPACE:$BUILDABLE_SCHEME"
while (($#))
do
declare BUILDABLE_OPT_VALUE="${1#*=}"
declare BUILDABLE_OPT="${1%%=*}"
case "$BUILDABLE_OPT" in
profile)
BUILDABLE_PROFILE="$BUILDABLE_OPT_VALUE"
;;
identity)
BUILDABLE_IDENTITY="$BUILDABLE_OPT_VALUE"
;;
notest)
BUILDABLE_SHOULD_TEST=0
;;
*)
printf '*** ERROR: Unknown option: %s\n' "$BUILDABLE_OPT"
usage_and_exit 1
;;
esac
shift
done
if [ ! -d "$ROOT_DIR/$BUILDABLE_WORKSPACE" ]
then
printf 'ERROR: could not find workspace at "%s".\n' "$ROOT_DIR/$BUILDABLE_WORKSPACE"
exit 1
fi
# Gather project settings from Xcode
clean
BUILDABLE_SETTINGS="$(xcbare "$BUILDABLE_WORKSPACE" "$BUILDABLE_SCHEME" -sdk iphoneos -configuration Release -showBuildSettings \
ONLY_ACTIVE_ARCH=YES \
ARCHS=arm64 \
"${BUILDABLE_IDENTITY+CODE_SIGN_IDENTITY=$BUILDABLE_IDENTITY}" \
"${BUILDABLE_PROFILE+PROVISIONING_PROFILE=$BUILDABLE_PROFILE}" \
| /usr/bin/sed "1,/Build settings for action build and target ${BUILDABLE_SCHEME}/d; /^\$/,\$d"
)"
[ -z "${VERBOSE:-}" ] || printf '\n\nPROJECT SETTINGS FOR %s:\n\n%s\n\n' "$BUILDABLE_WORKSPACE/$BUILDABLE_SCHEME" "$BUILDABLE_SETTINGS"
read BUILDABLE_INFO_PLIST < <(/usr/bin/awk "/ PRODUCT_SETTINGS_PATH"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
read BUILDABLE_NAME < <(/usr/bin/awk "/ PRODUCT_NAME"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
read BUILDABLE_FULLNAME < <(/usr/bin/awk "/ FULL_PRODUCT_NAME"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
read BUILDABLE_PRODUCTS < <(/usr/bin/awk "/ BUILT_PRODUCTS_DIR"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
read BUILDABLE_SYM < <(/usr/bin/awk "/ DWARF_DSYM_FILE_NAME"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
read BUILDABLE_TYPE < <(/usr/bin/awk "/ PRODUCT_TYPE"' =/ {print $3}' <<<"$BUILDABLE_SETTINGS")
BUILDABLE_TYPE=${BUILDABLE_TYPE##com\.apple\.product-type\.}
unset IS_APP
if [[ "$BUILDABLE_TYPE" == "application" ]]
then
IS_APP=1
fi
printf '\nName: %s' "$BUILDABLE_FULLNAME"
printf '\nType: %s %s' "$BUILDABLE_TYPE" "${IS_APP+[will be archived]}"
printf '\n'
# Inject version number into artifact's Info.plist
if [[ -n "$BUILDABLE_INFO_PLIST" && -f "$BUILDABLE_INFO_PLIST" ]]
then
defaults write "$BUILDABLE_INFO_PLIST" CFBundleVersion "$VERSION"
fi
declare BUILDABLE_ARTIFACTS_ROOT="$ROOT_DIR/artifacts/$BUILDABLE_NAME"
mkdir -p "$BUILDABLE_ARTIFACTS_ROOT"/{device,simulator}
if [[ "$BUILDABLE_TYPE" != "bundle.unit-test" ]]
then
# Build for device (release)
printf '\n# Building %s for device:\n' "$BUILDABLE_NAME"
clean
xcnice "$BUILDABLE_WORKSPACE" "$BUILDABLE_SCHEME" -sdk iphoneos -configuration Release \
ONLY_ACTIVE_ARCH=NO ARCHS='${ARCHS_STANDARD}' \
${IS_APP+${BUILDABLE_IDENTITY+CODE_SIGN_IDENTITY="$BUILDABLE_IDENTITY"}} \
${IS_APP+${BUILDABLE_PROFILE+PROVISIONING_PROFILE="$BUILDABLE_PROFILE"}} \
clean build analyze ${IS_APP+archive -archivePath "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive"}
if [[ "$BUILDABLE_TYPE" == "application" ]]
then
declare BUILDABLE_PROFILE_PATH="$(/usr/libexec/PlistBuddy -c "print Name" /dev/stdin <<< $(security cms -D -i "$HOME/Library/MobileDevice/Provisioning Profiles/${BUILDABLE_PROFILE}.mobileprovision"))"
nice -n 19 /usr/bin/ditto -c -k --zlibCompressionLevel 9 --sequesterRsrc --keepParent "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive/dSYMs/${BUILDABLE_SYM}" "$BUILDABLE_ARTIFACTS_ROOT/device/${BUILDABLE_SYM}.zip"
nice -n 19 xcrun xcodebuild -exportArchive -exportFormat ipa -archivePath "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive" -exportPath "$BUILDABLE_ARTIFACTS_ROOT/device/${BUILDABLE_NAME}.ipa" -exportProvisioningProfile "$BUILDABLE_PROFILE_PATH"
nice -n 19 /usr/bin/ditto -c -k --zlibCompressionLevel 9 --sequesterRsrc --keepParent "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive" "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive.zip"
rm -rf "$BUILDABLE_ARTIFACTS_ROOT/device/archive.xcarchive"
fi
fi
# Build for simulator (debug)
if [[ "$BUILDABLE_TYPE" == "application" ]] || ((BUILDABLE_SHOULD_TEST))
then
printf '\n# Building %s for simulator:\n' "$BUILDABLE_NAME"
clean
xcnice "$BUILDABLE_WORKSPACE" "$BUILDABLE_SCHEME" -sdk iphonesimulator -configuration Debug \
ONLY_ACTIVE_ARCH=NO ARCHS='${ARCHS_STANDARD}' \
clean build
if [[ "$BUILDABLE_TYPE" == "application" ]]
then
BUILDABLE_PRODUCTS="${BUILDABLE_PRODUCTS//Release/Debug}"
BUILDABLE_PRODUCTS="${BUILDABLE_PRODUCTS//iphoneos/iphonesimulator}"
[ ! -d "$BUILDABLE_PRODUCTS/$BUILDABLE_FULLNAME" ] || \
nice -n 19 /usr/bin/ditto -c -k --zlibCompressionLevel 9 --sequesterRsrc --keepParent "$BUILDABLE_PRODUCTS/$BUILDABLE_FULLNAME" "$BUILDABLE_ARTIFACTS_ROOT/simulator/${BUILDABLE_FULLNAME}.zip"
[ ! -d "$BUILDABLE_PRODUCTS/$BUILDABLE_SYM" ] || \
nice -n 19 /usr/bin/ditto -c -k --zlibCompressionLevel 9 --sequesterRsrc --keepParent "$BUILDABLE_PRODUCTS/$BUILDABLE_SYM" "$BUILDABLE_ARTIFACTS_ROOT/simulator/${BUILDABLE_SYM}.zip"
fi
fi
# Test on simulators
if ((BUILDABLE_SHOULD_TEST))
then
TEST_REPORTS_ROOT="$BUILDABLE_ARTIFACTS_ROOT/simulator/unit-tests"
mkdir -p "$TEST_REPORTS_ROOT"
for ((DEVICE_IDX=0; DEVICE_IDX<$DEVICE_COUNT; ++DEVICE_IDX))
do
DEVICE="${DEVICES[DEVICE_IDX]}"
DEVICE_TYPE="${DEVICE_TYPES[DEVICE_IDX]}"
for ((RUNTIME_IDX=0; RUNTIME_IDX<RUNTIME_COUNT; ++RUNTIME_IDX))
do
RUNTIME="${RUNTIMES[RUNTIME_IDX]}"
RUNTIME_VERSION="${RUNTIME_VERSIONS[RUNTIME_IDX]}"
printf '\n\n\nUnit tests will now run for [name=%s,OS=%s]\n' "${DEVICE}" "${RUNTIME_VERSION}"
printf 'Killing whatever iOS simulator might be running…'
fuckingkillthesimulatornow
printf ' Done.\n'
printf 'Creating temporary simulator for [name=%s,OS=%s]…' "${DEVICE}" "${RUNTIME_VERSION}"
unset SIMULATOR_ID
read SIMULATOR_ID < <(xcrun simctl create "$DEVICE" "$DEVICE_TYPE" "$RUNTIME" 2>&- && sleep 3 || printf 'error=%s' $?) || true
if [[ "$SIMULATOR_ID" == 'error=144' ]]
then
printf ' Unsupported (skipping).\n'
continue
elif [[ "$SIMULATOR_ID" =~ ^error=([0-9]*)$ ]]
then
printf ' ERROR (%s).\n' "${BASH_REMATCH[1]}"
exit 1
else
printf ' Done. UDID is %s.\n' "$SIMULATOR_ID"
printf 'Launching frontend simulator application for [name=%s, OS=%s, id=%s]…' "${DEVICE}" "${RUNTIME_VERSION}" "${SIMULATOR_ID}"
[ ! -d "/tmp/tmp$!.trace" ] || rm -rf "/tmp/tmp$!.trace"
xcrun instruments -l 5 -t Blank -D "/tmp/tmp$!" -w "$SIMULATOR_ID" >&- 2>&-
rm -rf "/tmp/tmp$!.trace"
printf ' Done.\n'
declare TEST_RUN_EXIT_STATUS=0
set +e +o pipefail
xcnice "$BUILDABLE_WORKSPACE" "$BUILDABLE_SCHEME" -jobs 1 -sdk iphonesimulator -configuration Debug \
ONLY_ACTIVE_ARCH=NO ARCHS='${ARCHS_STANDARD}' \
test -destination "platform=iOS Simulator,id=$SIMULATOR_ID" -destination-timeout 10
TEST_RUN_EXIT_STATUS=$?
set -e -o pipefail
printf 'Killing simulator [name=%s, OS=%s, id=%s]…' "${DEVICE}" "${RUNTIME_VERSION}" "${SIMULATOR_ID}"
fuckingkillthesimulatornow
printf ' Done.\n'
printf 'Deleting simulator [name=%s, OS=%s, id=%s]…' "${DEVICE}" "${RUNTIME_VERSION}" "${SIMULATOR_ID}"
xcrun simctl delete "$SIMULATOR_ID"
printf ' Done.\n'
# sysexits(3): 66 is EX_NOINPUT
if ((TEST_RUN_EXIT_STATUS == 66))
then
printf 'No test specified for scheme %s! Aborting test runs now.\n' "$BUILDABLE_SCHEME"
break 2
fi
printf 'Testing ended with exit code=%i.\n' $TEST_RUN_EXIT_STATUS
[ ! -f build/reports/junit.xml ] || mv build/reports/junit.xml "$TEST_REPORTS_ROOT/${SIMULATOR_ID}.xml"
((TEST_RUN_EXIT_STATUS)) && exit $TEST_RUN_EXIT_STATUS
fi
done
done
fi
# Optional: upload to saucelabs
if [[ -n "${SAUCELABS_CREDENTIALS:-}" && "$BUILDABLE_TYPE" == 'application' ]]
then
printf 'Uploading "%s" to SauceLabs bucket "%s"…\n' "${BUILDABLE_NAME}-${VERSION}" "${SAUCELABS_BUCKET:-${SAUCELABS_CREDENTIALS%%:*}}"
/usr/bin/curl -s -S 2>&1 \
${PROXY_CREDENTIALS+--proxy-user "$PROXY_CREDENTIALS"} \
-u "$SAUCELABS_CREDENTIALS" \
-X POST \
-H "application/octet-stream" \
"https://saucelabs.com/rest/v1/storage/${SAUCELABS_BUCKET:-${SAUCELABS_CREDENTIALS%%:*}}/${BUILDABLE_NAME}-${VERSION}.zip" \
--data-binary "@$BUILDABLE_ARTIFACTS_ROOT/simulator/${BUILDABLE_FULLNAME}.zip"
fi
# Optional: upload to testflight
if [[ -n "${TESTFLIGHT_DISTRIBUTION:-}" && -n "${TESTFLIGHT_API_TOKEN:-}" && -n "${TESTFLIGHT_TEAM_TOKEN:-}" && "$BUILDABLE_TYPE" == 'application' ]]
then
printf 'Uploading "%s" to TestFlight…\n' "${BUILDABLE_NAME}-${VERSION}"
/usr/bin/curl -s -S 2>&1 \
${PROXY_CREDENTIALS+--proxy-user "$PROXY_CREDENTIALS"} \
"http://testflightapp.com/api/builds.json" \
-F "file=@$BUILDABLE_ARTIFACTS_ROOT/device/${BUILDABLE_NAME}.ipa" \
-F "dsym=@$BUILDABLE_ARTIFACTS_ROOT/device/${BUILDABLE_SYM}.zip" \
-F "api_token=$TESTFLIGHT_API_TOKEN" \
-F "team_token=$TESTFLIGHT_TEAM_TOKEN" \
-F "notes=$RELEASE_NOTES" \
-F "notify=True" \
-F "replace=True" \
-F "distribution_lists=$TESTFLIGHT_DISTRIBUTION"
fi
printf '\n\n\n\n\n'
}
[ ! -d "$ROOT_DIR/artifacts" ] || rm -rf "$ROOT_DIR/artifacts"
while IFS=$'\t' read -ra BUILDABLE
do
process_buildable "${BUILDABLE[@]}"
done < <(grep -vE '^\s*(#.*)?$' "$ROOT_DIR/.ci")
# vim:nosi:noet:ts=4:sw=4:ff=unix:
#!/usr/bin/env bash -l -e -o pipefail
# Example usage
export LANG="en_US.UTF-8"
cd "$WORKSPACE"
# Get the Ruby environment right
[[ "$(type rvm 2>&-)" =~ "rvm is a function" ]]
RES=$?
(($RES)) || rvm use "ruby-2.0.0-p481"
gem update -N --minimal-deps --conservative "cocoapods" "xcpretty"
# Unlock the keychain
security unlock-keychain -p "$CI_USER_PASSWORD" "$HOME/Library/Keychains/login.keychain"
security set-keychain-settings -t 3600 -l "$HOME/Library/Keychains/login.keychain"
export \
TESTFLIGHT_API_TOKEN="$CI_TF_API_TOKEN" \
TESTFLIGHT_TEAM_TOKEN="$CI_TF_TEAM_TOKEN" \
TESTFLIGHT_DISTRIBUTION="Developers" \
PROXY_CREDENTIALS=$USER:$CI_USER_PASSWORD \
http_proxy=http://proxy
# Build!
"$HOME/cibuild.sh"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment