Automated Xcode 6 Bot TestFlight Uploads

Once upon a time, there was a script for uploading Xcode Bot builds to TestFlight automagically. You can find it here but sadly it doesnt work with Xcode 6, like everything else.

I’ve created a new script that will do the same job. It’s been tested with Xcode 6.1.1, Xcode Server 4.0 and Yosemite 10.10.1.


  1. Set up a new shared scheme and build config that properly signs your archive for TestFlight distribution. I recommend duplicating your Debug build config for this.
  2. In this scheme, add a ‚ÄĚPost-actions” script in the Archive action. Make sure you do this in the scheme “Post-actions”, not as a script in the “Build phases”. Find the script at the end of this post.
  3. Update your tokens and distribution list in the script
  4. Create an Xcode bot that runs this scheme.
  5. Profit


The script won’t codesign your ipa file any more as the previous one did. The reason is that ipa files may now contain app excensions which need to be codesigned separately. I think this is easier to set up in a scheme in Xcode than in a script.


API_TOKEN="your api token"
TEAM_TOKEN="your team token"
DISTRIBUTION_LISTS="your distribution list"

upload() {
    # Remove old files 'n folders
    rm /tmp/tflog
    rm /tmp/tfupload
    rm /tmp/tfuploadout
    rm -rf /tmp/Archive.xcarchive
    rm "${DSYM_ZIP}"
    until [ -e "${IPA}" ]; do
    echo "Waiting for ${IPA} to exist" >> /tmp/tflog
    if [ $TIMEOUT -eq 0 ]
        echo "Giving up on ${IPA} existence" >> /tmp/tflog
        TIMEOUT=`expr $TIMEOUT - 1`
    sleep 1
    # Unzip Archive to get at the juicy dSYM files
    echo "Unpacking ${ARCHIVE} to /tmp/Archive.xcarchive" >> /tmp/tflog
    unzip "${ARCHIVE}" -d /tmp >> /tmp/tflog
    # Zip the aforementioned juicy dSYM files up for TestFlight
    echo "Packing dSYM(s) as ${DSYM_ZIP}" >> /tmp/tflog
    pushd /tmp/Archive.xcarchive/dSYMs
    # Multiple dSYM files are not supported by TestFlight at this time. Just uploading the main one for now.
    #zip -r "${DSYM_ZIP}" * >> /tmp/tflog
    zip -r "${DSYM_ZIP}" "${DWARF_DSYM_FILE_NAME}"
    # Get the SCM log
    SCM_LOG=$(grep "\s\s\s\s\s\s\s" ${BASE_PATH}${XCS_BOT_ID}-${XCS_BOT_NAME}/${XCS_INTEGRATION_NUMBER}/sourceControl.log | sed 's/          //g')
    # Upload to TestFlight
    echo "Uploading to TestFlight" >> /tmp/tflog
    /usr/bin/curl -vs "" \
    -F file=@"${IPA}" \
    -F dsym=@"${DSYM_ZIP}" \
    -F api_token="${API_TOKEN}" \
    -F team_token="${TEAM_TOKEN}" \
    -F distribution_lists="${DISTRIBUTION_LISTS}" \
    -F notes="${SCM_LOG}\n\nBuild uploaded automatically from Xcode Server Bot."  --stderr /tmp/tfupload -o /tmp/tfuploadout
    # Clean up after ourselves before we leave
    rm -rf /tmp/Archive.xcarchive
    rm "${DSYM_ZIP}"

echo "Starting TestFlight Upload, check /tmp/tflog for status";

upload &

11 thoughts on “Automated Xcode 6 Bot TestFlight Uploads

  1. Came across your post while wrestling with Xcode 6 CI and it’s very helpful. Do you happen to know at what point in the build/integration phase the ipa file gets created? Is it after Archive?

    • So I tried this both in Archive post-action and in post-integration trigger, and both failed to find the ipa file. Yet after integration finishes I can see it there in the folder, except the creation timestamp is the same as the post-integration trigger log. How were you able to get this to work? I must be missing something.

      • Hi, as you see in the script there is an upload() function that is launched as a new process. It waits for up to two minutes for the ipa file to be created. Maybe you need to increase this timer?

        • So I’m actually trying to use Beta by Crashlytics, not Testflight, and was trying to use this method of pushing the ipa because their recommended method wasn’t working. I managed to get that to work last night, so giving up on this for now, but I would like to figure out why it’s not working for me. I can actually see the post-trigger step getting stuck for two minutes running the script.

    • Sort of, but you still have to manually mark the uploaded build for testing. I use this script that i invoke manually at the time. Requires Shenzhen

      ipa build –workspace –scheme “Your Scheme” –clean –configuration “Your Config”

      ipa distribute:crashlytics -c Crashlytics.framework -f -a key -s key etc

      You can replace crashlytics with whatever is supported by Shenzhen (including Apple TestFlight but it’s called iTunes Connect i think)

  2. what is “your api token”? I have an apple id, I have a provisioning profile, I have a app id. but nothing like “api token” can you give some guidance?

    what is “your team token”? the apple id?

    I get “your distribution list” but please provide an example…

    API_TOKEN=”your api token”
    TEAM_TOKEN=”your team token”
    DISTRIBUTION_LISTS=”your distribution list”

Leave a Reply

Your email address will not be published. Required fields are marked *