Skip to content

Instantly share code, notes, and snippets.

@ouchadam
Last active February 20, 2023 15:02
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ouchadam/c74fa26c639a50d68bc35ee5749f868c to your computer and use it in GitHub Desktop.
Save ouchadam/c74fa26c639a50d68bc35ee5749f868c to your computer and use it in GitHub Desktop.
Upload APK to appcenter app=my-cool-app owner=owner-in-appcenter file=path/to/file token=1234-api-token destination_name=group-to-apply
#!/bin/bash
set -e
CONFIG=$@
for line in $CONFIG; do
eval "$line"
done
AUTH="X-API-Token: $token"
CONTENT_TYPE=application/vnd.android.package-archive
echo "Creating release (1/7)"
request_url="https://api.appcenter.ms/v0.1/apps/$owner/$app/uploads/releases"
upload_json=$(curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" "$request_url")
ID=$(echo $upload_json | jq -r '.id')
UPLOAD_DOMAIN=$(echo $upload_json | jq -r '.upload_domain')
PACKAGE_ASSET_ID=$(echo $upload_json | jq -r '.package_asset_id')
URL_ENCODED_TOKEN=$(echo $upload_json | jq -r '.url_encoded_token')
FILE_NAME=$(basename $file)
FILE_SIZE=$(stat --printf="%s" $file)
echo "Creating metadata (2/7)"
set_metadata_url="$UPLOAD_DOMAIN/upload/set_metadata/$PACKAGE_ASSET_ID?file_name=$FILE_NAME&file_size=$FILE_SIZE&token=$URL_ENCODED_TOKEN&content_type=$CONTENT_TYPE"
meta_response=$(curl -s -d POST -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" "$set_metadata_url")
chunk_size=$(echo $meta_response | jq -r '.chunk_size')
TMP_BUILD_DIR=build/appcenter-tmp
rm -rf $TMP_BUILD_DIR
mkdir $TMP_BUILD_DIR
split -b $chunk_size $file $TMP_BUILD_DIR/split
echo "Uploading chunked binary (3/7)"
binary_upload_url="$UPLOAD_DOMAIN/upload/upload_chunk/$PACKAGE_ASSET_ID?token=$URL_ENCODED_TOKEN"
block_number=1
for f in $TMP_BUILD_DIR/*
do
url="$binary_upload_url&block_number=$block_number"
size=$(stat --printf="%s" $f)
curl -X POST $url --data-binary "@$f" -H "Content-Length: $size" -H "Content-Type: $CONTENT_TYPE"
block_number=$(($block_number + 1))
printf "\n"
done
echo "Finalising upload (4/7)"
finish_url="$UPLOAD_DOMAIN/upload/finished/$PACKAGE_ASSET_ID?token=$URL_ENCODED_TOKEN"
curl -d POST -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" "$finish_url"
echo "Commit release (5/7)"
commit_url="https://api.appcenter.ms/v0.1/apps/$owner/$app/uploads/releases/$ID"
curl -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" \
--data '{"upload_status": "uploadFinished","id": "$ID"}' \
-X PATCH \
$commit_url
release_status_url="https://api.appcenter.ms/v0.1/apps/$owner/$app/uploads/releases/$ID"
release_id=null
counter=0
max_poll_attempts=15
echo "Polling for release id (6/7)"
while [[ $release_id == null && ($counter -lt $max_poll_attempts)]]
do
poll_result=$(curl -s -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" $release_status_url)
release_id=$(echo $poll_result | jq -r '.release_distinct_id')
echo $counter $release_id
counter=$((counter + 1))
sleep 3
done
if [[ $release_id == null ]];
then
echo "Failed to find release from appcenter"
exit 1
fi
echo "Applying destination to release (7/7)"
distribute_url="https://api.appcenter.ms/v0.1/apps/$owner/$app/releases/$release_id"
curl -H "Content-Type: application/json" -H "Accept: application/json" -H "X-API-Token: $token" \
--data '{"destinations": [{ "name": "$destination_name"}] }' \
-X PATCH \
$distribute_url
echo https://appcenter.ms/orgs/$owner/apps/$app/distribute/releases/$release_id
@m046418
Copy link

m046418 commented Dec 11, 2020

Hi Adam, just wanted to say thanks for the work you did - this helped me get our ruby scripts working with the new API.

One additional item you may want to add at the end is a loop to check on the "upload_status", basically using the same endpoint as the commit_url, but called with a -X GET, the status changes from "uploadFinished" to "readyToBePublished" when the backend is ready for any further steps, like adding distribution groups or release notes.
https://openapi.appcenter.ms/#/distribute/releases_getReleaseUploadStatus
Once status changes to "readyToBePublished ", the payload includes a "release_distinct_id" which is used as the "release_id" in subsequent calls to add app to groups or add release notes.

For our purposes (iOS), we also had to make the mime type in the block uploads = "Content-Type: application/octet-stream". I was unable to find a suitable mime type in the CLI source for iOS's "ipa" file types, maybe that just my lack of TypeScript skills.

Thanks again.

@ouchadam
Copy link
Author

@m046418 thanks for the heads up! I had a version which included those extra steps but forgot to update the gist, the latest revision includes polling for the release id and applying a final destination with destination_name

@King-of-Spades
Copy link

King-of-Spades commented Jan 6, 2021

I noticed some minor issues with this script when running on macOS:

Other than those issues, it was pretty smooth! Just sharing in case anyone else looking at this tripped up in similar points on macOS.

@hffmnn
Copy link

hffmnn commented Jan 6, 2021

@King-of-Spades Looks like (stat -f %z $file) is not cross-platform and only works on mac. I quickly run this in an ubuntu docker container and it gave me this:
stat: cannot read file system information for '%z': No such file or directory

I fixed this by replacing all occurrences of stat --printf="%s" ... with $(wc -c $file | awk '{print $1}') (line 23) and $(wc -c $f | awk '{print $1}') (line 43).

@King-of-Spades
Copy link

@hffmnn thanks for the tip! Your approach works for me too.

@niharamin86
Copy link

Thank you for the script.

@King-of-Spades
Copy link

King-of-Spades commented Jan 30, 2021

I added this script, but with a lot of modifications, to the App Center sample here: https://github.com/microsoft/appcenter-Xamarin.UITest-Demo/blob/main/ac-distribute.sh

I modified the script substantially to make it cross-platform & more configurable by initial variables. I also updated the doc to detail the new API approach.

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