Last active
February 18, 2020 20:57
-
-
Save JakeSteam/0c68243f7e90da9bea70362461d59f34 to your computer and use it in GitHub Desktop.
Uploading app bundles to Google Play internal app sharing https://blog.jakelee.co.uk/uploading-an-app-bundle-to-google-play-internal-app-sharing-from-travis-ci/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Overall config | |
dist: xenial | |
language: android | |
# Android version config | |
android: | |
components: | |
- build-tools-28.0.3 | |
- android-28 | |
# Scripts | |
before_install: # Make sure build scripts can be executed & files are decrypted | |
- chmod 755 build-scripts/*.sh | |
- openssl aes-256-cbc -K $encrypted_defa05ba0abc_key -iv $encrypted_defa05ba0abc_iv -in scripts/serviceaccount.json.enc -out scripts/serviceaccount.json -d | |
script: # Runs build script | |
- build-scripts/travis_build.sh $TRAVIS_BRANCH $KEYSTORE_PASSWORD $KEYSTORE_KEY_PASSWORD |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
function uploadToInternalAppSharing { | |
if (($# != 2)); then echo "Please pass the bundle path and service account JSON location!"; exit 2; fi | |
BUNDLE_LOCATION="${1}app.aab" | |
AUTH_LOCATION="${2}" | |
# Parse service account JSON for authentication information | |
SERVICE_ACCOUNT_CONFIG=$(<${AUTH_LOCATION}) | |
AUTH_SERVER=$(echo ${SERVICE_ACCOUNT_CONFIG} | jq -r '.token_uri') | |
AUTH_EMAIL=$(echo ${SERVICE_ACCOUNT_CONFIG} | jq -r '.client_email') | |
AUTH_KEY=$(echo ${SERVICE_ACCOUNT_CONFIG} | jq -r '.private_key') | |
echo "Retrieved service account from JSON!" | |
# Generate JWT from authentication information | |
JWT=$(getJwt "$AUTH_SERVER" "$AUTH_EMAIL" "$AUTH_KEY") | |
echo "Generated JWT!" | |
# Use JWT to authenticate with Google and retrieve an access token | |
ACCESS_TOKEN=$(getAccessToken "$AUTH_SERVER" "$JWT") | |
echo "Generated access token!" | |
# Use access token to upload app bundle to Google Play Internal App Sharing | |
URL=$(uploadBundle "$ACCESS_TOKEN" "$BUNDLE_LOCATION") | |
echo "Uploaded app bundle to ${URL}" | |
} | |
function getJwt { | |
if (($# != 3)); then echo "Please pass the auth server, auth email, and auth key!"; exit 2; fi | |
AUTH_SERVER=$1 | |
AUTH_EMAIL=$2 | |
AUTH_KEY=$3 | |
# Create JWT header | |
JWT_HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e) | |
# Create JWT body | |
JWT_BODY=$(cat <<EOF | |
{ | |
"aud": "${AUTH_SERVER}", | |
"iss": "${AUTH_EMAIL}", | |
"scope": "https://www.googleapis.com/auth/androidpublisher", | |
"exp": $(($(date +%s)+300)), | |
"iat": $(date +%s) | |
} | |
EOF | |
) | |
JWT_BODY_CLEAN=$(echo -n "$JWT_BODY" | openssl base64 -e) | |
# Create complete payload | |
JWT_PAYLOAD=$(echo -n "$JWT_HEADER.$JWT_BODY_CLEAN" | tr -d '\n' | tr -d '=' | tr '/+' '_-') | |
# Create JWT signature | |
JWT_SIGNATURE=$(echo -n "$JWT_PAYLOAD" | openssl dgst -binary -sha256 -sign <(printf '%s\n' "$AUTH_KEY") | openssl base64 -e) | |
JWT_SIGNATURE_CLEAN=$(echo -n "$JWT_SIGNATURE" | tr -d '\n' | tr -d '=' | tr '/+' '_-') | |
# Combine JWT payload and signature | |
echo ${JWT_PAYLOAD}.${JWT_SIGNATURE_CLEAN} | |
} | |
function getAccessToken { | |
if (($# != 2)); then echo "Please pass the auth server and JWT!"; exit 2; fi | |
AUTH_SERVER=$1 | |
JWT=$2 | |
# Send JWT to auth server | |
HTTP_RESPONSE=$(curl --silent --write-out "HTTPSTATUS:%{http_code}" \ | |
--header "Content-type: application/x-www-form-urlencoded" \ | |
--request POST \ | |
--data "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=$JWT" \ | |
"$AUTH_SERVER") | |
# Parse auth server response for body and status | |
HTTP_BODY=$(echo ${HTTP_RESPONSE} | sed -e 's/HTTPSTATUS\:.*//g') | |
HTTP_STATUS=$(echo ${HTTP_RESPONSE} | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') | |
# Check response status for success, and retrieve access token if possible | |
if [[ ${HTTP_STATUS} != 200 ]]; then | |
echo -e "Create access token failed.\nStatus: $HTTP_STATUS\nBody: $HTTP_BODY\nExiting." | |
exit 1 | |
fi | |
echo $(echo ${HTTP_BODY} | jq -r '.access_token') | |
} | |
function uploadBundle { | |
if (($# != 2)); then echo "Please pass the access token and bundle location!"; exit 2; fi | |
ACCESS_TOKEN=$1 | |
BUNDLE_LOCATION=$2 | |
# Send app bundle and access token to internal app sharing | |
PACKAGE="uk.co.jakelee.examplepackage" | |
HTTP_RESPONSE=$(curl --write-out "HTTPSTATUS:%{http_code}" \ | |
--header "Authorization: Bearer $ACCESS_TOKEN" \ | |
--header "Content-Type: application/octet-stream" \ | |
--progress-bar \ | |
--request POST \ | |
--upload-file ${BUNDLE_LOCATION} \ | |
https://www.googleapis.com/upload/androidpublisher/v3/applications/internalappsharing/${PACKAGE}/artifacts/bundle?uploadType=media) | |
# Parse internal app sharing response for body and status | |
HTTP_BODY=$(echo ${HTTP_RESPONSE} | sed -e 's/HTTPSTATUS\:.*//g') | |
HTTP_STATUS=$(echo ${HTTP_RESPONSE} | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') | |
# Check response status for success, and retrieve download URL if possible | |
if [[ ${HTTP_STATUS} != 200 ]]; then | |
echo -e "Upload app bundle failed.\nStatus: $HTTP_STATUS\nBody: $HTTP_BODY\nExiting." | |
exit 1 | |
fi | |
echo $(echo ${HTTP_BODY} | jq -r '.downloadUrl') | |
} | |
RELEASE_BUNDLE_PATH=${TRAVIS_BUILD_DIR}/app/build/outputs/bundle/prodRelease/ | |
AUTH_LOCATION=${TRAVIS_BUILD_DIR}/scripts/serviceaccount.json | |
uploadToInternalAppSharing ${RELEASE_BUNDLE_PATH} ${AUTH_LOCATION}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment