TestFairy is an android application beta testing and deployment platform. Read TestFairy platform features and detailed description here.

As you upload a build to TestFairy, it process on the uploaded build and do some dynamic code injection. During the process, it forget that the build was signed with a keystore. Before the build is made available for beta testers to download, TestFairy sign your build with a keystore key of its own. This will result your application not working properly for the services that like Facebook, Google Map, In-App Purchases, Google Cloud Messaging (GCM) etc.

Here is the workaround!! Instead of you just uploading your build to TestFairy, you can use the command line uploader script mentioning your keystore details. In this post we’ll see the configurations and how to execute testfairy command line uploader script. This will be helpful for integrating bamboo, Jenkins or other CI build automation tools.

1. Download TestFairy Command Line Upload Script

Let us download the TestFairy command line upload script from GitHub.  Once you have the testfairy-upload.sh file you can change the following parameters as per your project configurations.

# Put your TestFairy API_KEY here. Find it in your TestFairy account settings.
TESTFAIRY_API_KEY="Put your TestFairy API key here"

# Your Keystore, Storepass and Alias, the ones you use to sign your app.
KEYSTORE=build-dir/MyProject/keystore/myapp.keystore
STOREPASS=android
ALIAS=myapp

# Tester Groups that will be notified when the app is ready. Setup groups in your TestFairy account testers page.
# This parameter is optional, leave empty if not required
TESTER_GROUPS="Distribution Group Name"

# Comment text will be included in the email sent to testers
COMMENT="Put your update comment here"

# locations of various tools
CURL=/usr/bin/curl
ZIP=/usr/bin/zip
KEYTOOL=/usr/bin/keytool
ZIPALIGN=/Applications/adt-bundle-mac-x86_64-20130917/sdk/build-tools/19.1.0/zipalign
JARSIGNER=/usr/bin/jarsigner

2. Executing Command Line Upload Script

$ chmod a+x testfairy-upload.sh

$ ./testfairy-upload.sh bin/MyApp.apk

If the script execute successfully, you will see the output in the code

Uploading bin/MyApp.apk to TestFairy.. OK!
Downloading instrumented APK.. OK!
Re-signing APK file.. jar signed.

Warning:
No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2283-11-17) or after any future revocation date.
OK!
Uploading signed APK to TestFairy.. OK!

Build was successfully uploaded to TestFairy and is available at:
https://app.testfairy.com/projects/6521-myapp/builds/142823

The above code works fine and build getting uploaded to TestFairy. But there is one problem, if you are uploading build from CI tool, you would like see the comments from developers for each build. In such case you have to pass another command line parameter containing the release comment. This release comment will be send over invitation email.

#!/bin/sh

UPLOADER_VERSION=1.09

# Put your TestFairy API_KEY here. Find it in your TestFairy account settings.
TESTFAIRY_API_KEY="Put your TestFairy API key here"

# Your Keystore, Storepass and Alias, the ones you use to sign your app.
KEYSTORE=build-dir/MyProject/keystore/myapp.keystore
STOREPASS=android
ALIAS=myapp

# Tester Groups that will be notified when the app is ready. Setup groups in your TestFairy account testers page.
# This parameter is optional, leave empty if not required
TESTER_GROUPS="Distribution Group Name"

# Should email testers about neew version. Set to "off" to disable email notifications.
NOTIFY="on"

# If AUTO_UPDATE is "on" all users will be prompt to update to this build next time they run the app
AUTO_UPDATE="off"

# The maximum recording duration for every test. 
MAX_DURATION="10m"

# Is video recording enabled for this build 
VIDEO="on"

# Add a TestFairy watermark to the application icon?
ICON_WATERMARK="on"

# Comment text will be included in the email sent to testers
COMMENT="New Build"

# locations of various tools
CURL=/usr/bin/curl
ZIP=/usr/bin/zip
KEYTOOL=/usr/bin/keytool
ZIPALIGN=/Applications/adt-bundle-mac-x86_64-20130917/sdk/build-tools/19.1.0/zipalign
JARSIGNER=/usr/bin/jarsigner

SERVER_ENDPOINT=http://app.testfairy.com

usage() {
	echo "Usage: testfairy-upload.sh bin/TennisTV.apk"
	echo
}
	
verify_tools() {

	# Windows users: this script requires zip, curl and sed. If not installed please get from http://cygwin.com/
	
	# Check 'zip' tool
	${ZIP} -h >/dev/null
	if [ $? -ne 0 ]; then
		echo "Could not run zip tool, please check settings"
		exit 1
	fi
	
	# Check 'curl' tool
	${CURL} --help >/dev/null
	if [ $? -ne 0 ]; then
		echo "Could not run curl tool, please check settings"
		exit 1
	fi
	
	OUTPUT=$( ${JARSIGNER} -help 2>&1 | grep "verify" )
	if [ $? -ne 0 ]; then
		echo "Could not run jarsigner tool, please check settings"
		exit 1
	fi
	
	# Check 'zipalign' tool
	OUTPUT=$( ${ZIPALIGN} 2>&1 | grep -i "Zip alignment" )
	if [ $? -ne 0 ]; then
		echo "Could not run zipalign tool, please check settings"
		exit 1
	fi

	OUTPUT=$( ${KEYTOOL} -help 2>&1 | grep "keypasswd" )
	if [ $? -ne 0 ]; then
		echo "Could not run keytool tool, please check settings"
		exit 1
	fi
}

verify_settings() {
	if [ -z "${TESTFAIRY_API_KEY}" ]; then
		usage
		echo "Please update API_KEY with your private API key, as noted in the Settings page"
		exit 1
	fi

	if [ -z "${KEYSTORE}" -o -z "${STOREPASS}" -o -z "{$ALIAS}" ]; then
		usage
		echo "Please update KEYSTORE, STOREPASS and ALIAS with your jar signing credentials"
		exit 1
	fi

	# verify KEYSTORE, STOREPASS and ALIAS at once
	OUTPUT=$( ${KEYTOOL} -list -keystore "${KEYSTORE}" -storepass "${STOREPASS}" -alias "${ALIAS}" 2>&1 )
	if [ $? -ne 0 ]; then
		usage
		echo "Please check keystore credentials; keytool failed to verify storepass and alias"
		exit 1
	fi
}

if [ $# -ne 2 ]; then
	usage
	exit 1
fi

# before even going on, make sure all tools work
verify_tools
verify_settings

APK_FILENAME=$1
if [ ! -f "${APK_FILENAME}" ]; then
	usage
	echo "Can't find file: ${APK_FILENAME}"
	exit 2
fi

COMMENT=$2

# temporary file paths
DATE=`date`
TMP_FILENAME=.testfairy.upload.apk
ZIPALIGNED_FILENAME=.testfairy.zipalign.apk
rm -f "${TMP_FILENAME}" "${ZIPALIGNED_FILENAME}"

/bin/echo -n "Uploading ${APK_FILENAME} to TestFairy.. "
JSON=$( ${CURL} -s ${SERVER_ENDPOINT}/api/upload -F api_key=${TESTFAIRY_API_KEY} -F apk_file="@${APK_FILENAME}" -F icon-watermark="${ICON_WATERMARK}" -F video="${VIDEO}" -F max-duration="${MAX_DURATION}" -F comment="${COMMENT}" -A "TestFairy Command Line Uploader ${UPLOADER_VERSION}" )

URL=$( echo ${JSON} | sed 's/\\\//\//g' | sed -n 's/.*"instrumented_url"\s*:\s*"\([^"]*\)".*/\1/p' )
if [ -z "${URL}" ]; then
	echo "FAILED!"
	echo 
	echo "Upload failed, please check your settings"
	exit 1
fi

URL="${URL}?api_key=${TESTFAIRY_API_KEY}"

echo "OK!"
/bin/echo -n "Downloading instrumented APK.. "
${CURL} -L -o ${TMP_FILENAME} -s ${URL}

if [ ! -f "${TMP_FILENAME}" ]; then
	echo "FAILED!"
	echo
	echo "Could not download APK back from server, please contact support@testfairy.com"
	exit 1
fi

echo "OK!"

/bin/echo -n "Re-signing APK file.. "
${ZIP} -qd ${TMP_FILENAME} 'META-INF/*'
${JARSIGNER} -keystore "${KEYSTORE}" -storepass "${STOREPASS}" -digestalg SHA1 -sigalg MD5withRSA ${TMP_FILENAME} "${ALIAS}"
${JARSIGNER} -verify ${TMP_FILENAME} >/dev/null
if [ $? -ne 0 ]; then
	echo "FAILED!"
	echo
	echo "Jarsigner failed to verify, please check parameters and try again"
	exit 1
fi

${ZIPALIGN} -f 4 ${TMP_FILENAME} ${ZIPALIGNED_FILENAME}
rm -f ${TMP_FILENAME}
echo "OK!"

/bin/echo -n "Uploading signed APK to TestFairy.. "
JSON=$( ${CURL} -s ${SERVER_ENDPOINT}/api/upload-signed -F api_key=${TESTFAIRY_API_KEY} -F apk_file=@${ZIPALIGNED_FILENAME} -F testers-groups="${TESTER_GROUPS}" -F auto-update="${AUTO_UPDATE}" -F notify="${NOTIFY}")
rm -f ${ZIPALIGNED_FILENAME}

URL=$( echo ${JSON} | sed 's/\\\//\//g' | sed -n 's/.*"build_url"\s*:\s*"\([^"]*\)".*/\1/p' )
if [ -z "$URL" ]; then
	echo "FAILED!"
	echo
	echo "Build uploaded, but no reply from server. Please contact support@testfairy.com"
	exit 1
fi

echo "OK!"
echo
echo "Build was successfully uploaded to TestFairy and is available at:"
echo ${URL}

Now, we can run the above script by passing two arguments. One for the apk path and other is for comment for your build. This comment will be sent to tester over email.

$ chmod a+x testfairy-upload.sh

$ ./testfairy-upload.sh bin/MyApp.apk "$@Your build update comment here"

Nilanchala

A blogger, a bit of tech freak and a software developer. He is a thought leader in the fusion of design and mobile technologies. He is the author of Xamarin Mobile Application Development for Android Book (goo.gl/qUZ0XV3), DZone MVB and founder of stacktips.com.

Related Articles

Join The Discussion

Please note: We reserve the right to delete comments that contains snarky remarks, offensive or off-topic. To know more read our comments policy.
  • Samanta Cicilia

    i have a problem with Downloading instrumented APK because i’m behind a proxy server. How i can configure this?

  • Chris Wagner

    Thank you for this!

  • Albert Martorell Garcia

    A really useful post.
    To me, this article fits the bill to understand the process of uploading the apk through command line uploader.
    Thank you.