Created
June 20, 2020 13:39
-
-
Save Preecington/c1c2b30582a0ecf858da0a100ba621b4 to your computer and use it in GitHub Desktop.
TestFlight Deploy GitHub Action (Xamarin.iOS)
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
name: Testflight Deploy | |
on: | |
push: | |
branches: [ master ] | |
jobs: | |
build: | |
runs-on: macos-latest | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
fetch-depth: 0 | |
- name: Import Codesign Certificate | |
id: codesign-cert | |
uses: Apple-Actions/import-codesign-certs@v1 | |
with: | |
p12-file-base64: ${{ secrets.DIST_CERT_BASE64 }} | |
p12-password: ${{ secrets.DIST_CERT_P12_PASSWORD }} | |
- name: Generate App Store Connect API Token | |
id: app-store-api-token | |
env: | |
ISSUER_ID: ${{ secrets.APPCONNECT_API_ISSUER }} | |
KEY_ID: ${{ secrets.APPCONNECT_API_KEY_ID }} | |
AUTH_KEY: ${{ secrets.APPCONNECT_API_KEY_PRIVATE }} | |
run: | | |
pip3 install cryptography | |
pip3 install pyjwt | |
exp=$(date -v +20M +%s) | |
iss=$(date -v -1M +%s) | |
jwt=$(python3 -c "import jwt; print(jwt.encode({'aud':'appstoreconnect-v1','iss':'$ISSUER_ID','exp':$exp,'iat':$iss},'''$AUTH_KEY''',algorithm='ES256', headers={'kid': '$KEY_ID'}).decode('utf-8'))") | |
echo "::add-mask::$jwt" | |
echo "::set-output name=appStoreToken::$jwt" | |
- name: Prime App Store Connect API | |
env: | |
JWT: ${{ steps.app-store-api-token.outputs.appStoreToken }} | |
uses: nick-invision/[email protected] | |
with: | |
timeout_minutes: 1 | |
max_attempts: 30 | |
command: 'curl --header "Authorization: Bearer $JWT" -s -o /dev/null -w "%{http_code}" --fail https://api.appstoreconnect.apple.com/v1/bundleIds' | |
- name: Import Healthshare Provisioning Profile | |
id: prov-profile | |
uses: Apple-Actions/download-provisioning-profiles@v1 | |
with: | |
bundle-id: YOUR_BUNDLE_ID_HERE | |
issuer-id: ${{ secrets.APPCONNECT_API_ISSUER }} | |
api-key-id: ${{ secrets.APPCONNECT_API_KEY_ID }} | |
api-private-key: ${{ secrets.APPCONNECT_API_KEY_PRIVATE }} | |
profile-type: IOS_APP_STORE | |
- name: Bump Build Version | |
run: | | |
for f in */Info.plist | |
do | |
/usr/libexec/PlistBuddy $f -c "Set :CFBundleVersion $GITHUB_RUN_NUMBER" | |
done | |
sed -i '' -e 's/\(\([0-9]\{1,\}\.\)\{1,\}\)\([0-9]\)/\1'"$GITHUB_RUN_NUMBER"'/g' */Properties/AssemblyInfo.cs | |
- name: Restore Dependencies & Build IPA | |
run: | | |
nuget restore | |
msbuild /p:Configuration=Release /p:Platform=iPhone /p:BuildIpa=true /p:IpaPackageDir=$GITHUB_WORKSPACE/build "/p:Codesignkey=Apple Distribution" | |
- name: Upload a Build Artifact | |
uses: actions/upload-artifact@v2 | |
with: | |
path: ${{ github.workspace }}/build/*ipa | |
- name: Preparing Release Notes from PR History | |
id: release-notes | |
run: | | |
rel_notes=`git log -n 1 --merges --pretty=format:'%b'` | |
echo "Release Notes for $GITHUB_RUN_NUMBER: $rel_notes" | |
echo "::set-output name=relNotes::$rel_notes" | |
- name: Upload to TestFlight | |
env: | |
FASTLANE_USERNAME: ${{ secrets.FASTLANE_USERNAME }} | |
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APP_PASSWORD }} | |
APPLE_ID: ${{ secrets.APPLE_ID }} | |
run: fastlane pilot upload -u $FASTLANE_USERNAME -p $APPLE_ID -i $GITHUB_WORKSPACE/build/*.ipa -a preecington.HealthShare --skip_waiting_for_build_processing true --verbose | |
- name: Publish Testflight Build | |
env: | |
GROUP_NAME: YOUR_GROUP_HERE | |
JWT: ${{ steps.app-store-api-token.outputs.appStoreToken }} | |
API_BASE: https://api.appstoreconnect.apple.com/v1 | |
REL_NOTES: ${{ steps.release-notes.outputs.relNotes }} | |
run: | | |
set -o pipefail | |
output="" | |
callWithRetry() { | |
attempt_counter=0 | |
output="" | |
max_attempts=${5:-30} | |
until output=$(curl --header "Authorization: Bearer $JWT" -s -f -X ${3:-GET} -H "Content-Type: application/json" -d "${4:-}" "$1" | jq -r -e "${2:-.}" ) ; do | |
if [ ${attempt_counter} -eq ${max_attempts} ];then | |
echo "Max attempts reached" | |
exit -1 | |
fi | |
printf "${7:-.}\n" | |
attempt_counter=$(($attempt_counter+1)) | |
sleep ${6:-1} | |
done | |
} | |
api_url="$API_BASE/builds?filter[version]=$GITHUB_RUN_NUMBER&include=buildBetaDetail,betaBuildLocalizations&fields[buildBetaDetails]=externalBuildState&fields[betaBuildLocalizations]=whatsNew" | |
query='. as $parent | .included[] | select(.type == "buildBetaDetails") | select(.attributes.externalBuildState != "PROCESSING") | $parent' | |
# Wait until Build is ready | |
echo "Waiting for App Store Connect to process build $GITHUB_RUN_NUMBER" | |
callWithRetry "$api_url" "$query" "GET" "" 100 10 "Waiting for build processing..." | |
# Get ids | |
build_id=$(echo $output | jq '.data[].id' -r) | |
build_localization_id=$(echo $output | jq '.included[] | select(.type == "betaBuildLocalizations") | .id' -r) | |
# Set compliance | |
echo "Updating export compliance" | |
data='{"data":{"attributes":{"usesNonExemptEncryption":false},"id":"'$build_id'","type":"builds"}}' | |
callWithRetry "$API_BASE/builds/$build_id" "." "PATCH" "$data" | |
# Get Group Id | |
callWithRetry "$API_BASE/betaGroups?filter[name]=$GROUP_NAME" ".data[].id" | |
group_id=$output | |
# Set group | |
echo "Assigning external testers" | |
data='{"data":[{"id":"'$group_id'","type":"betaGroups"}]}' | |
callWithRetry "$API_BASE/builds/$build_id/relationships/betaGroups" "." "POST" "$data" | |
# Set release notes | |
echo "Assigning release notes" | |
data='{"data":{"id":"'$build_localization_id'","attributes":{"whatsNew":"'$REL_NOTES'"},"type":"betaBuildLocalizations"}}' | |
callWithRetry "$API_BASE/betaBuildLocalizations/$build_localization_id" "." "PATCH" "$data" | |
# Submit for review | |
echo "Submitting for external review" | |
data='{"data":{"relationships":{"build":{"data":{"id":"'$build_id'","type":"builds"}}},"type":"betaAppReviewSubmissions"}}' | |
callWithRetry "$API_BASE/betaAppReviewSubmissions" "." "POST" "$data" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment