-
Notifications
You must be signed in to change notification settings - Fork 4
Continuous Deployment for iOS Apps
We use the NOI Community App as an example throughout this tutorial, which has an App Name called NOI Community App
and a App ID it.bz.noi.community
...
- Go to https://appstoreconnect.apple.com
- Login with your Apple account (should be connected to an Admin role)
- Go to "My Apps" and click the
+
sign- The Bundle ID should be a reverse package name, for example:
it.bz.noi.community
- The Bundle ID should be a reverse package name, for example:
- Activate the internal testing track by adding some testers
- Fill in additional needed information for that inside "Test Information"
Create secrets under GitHub>Settings>Secrets (choose your repository):
APPLEID_PASSWORD
APPLEID_USERNAME
GOOGLE_SERVICE_INFO_PLIST
IOS_KEY_PASSPHRASE
Some information can be found on this blog.
OUTDATED see Simons Blog and [Github Guide](https://docs.github.com/en/actions/use-cases-and-
examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development)
When inside https://developer.apple.com/account/resources/certificates/list go to "Certificates" and click on + as seen in the Apple Developer Docs.
-
Create a Signing Request (CSR) (this tutorial assumes you do not have a Mac):
- Certificate Type:
iOS Distribution
- Certificate Name:
NOI SPA
Optionally, for more details on this topic, see some openSSL examples and this StackOverflow post to get an idea how to do it
- Certificate Type:
-
Create a private key and then generate a certificate request from it:
mkdir apple-keys cd apple-keys openssl req -newkey rsa:2048 -keyout it.bz.noi.community.ios.key.pem -out it.bz.noi.community.ios.req.pem
WARNING: Upload the
.req.pem
file not your private key.key.pem
! -
Download your
.cer
certificate by clicking on the generated certificate -
Go to profiles, create your App Store provisioning profile with the certificate above and download it
-
Encrypt both with your secret
IOS_KEY_PASSPHRASE
and move them to .github/secretsgpg --symmetric --cipher-algo AES256 ios_distribution.cer gpg --symmetric --cipher-algo AES256 itbznoicommunity.mobileprovision mkdir -p .github/secrets cp itbznoicommunity.mobileprovision.gpg .github/secrets/it.bz.noi.community.mobileprovisioning.gpg
- create a folder
.github/secrets
- create a file
decrypt_secrets.sh
with this content (adaptMOBILEPROV_NAME
andCERTIFICATE_NAME
):
#!/bin/bash
set -xeo pipefail
MOBILEPROV_NAME="it.bz.noi.community.mobileprovision"
CERTIFICATE_NAME="ios_distribution.p12"
SECRETS_PATH=".github/secrets"
# DECRYPT FILES
gpg --quiet --batch --yes --decrypt --passphrase="$IOS_KEY_PASSPHRASE" --output $SECRETS_PATH/$MOBILEPROV_NAME $SECRETS_PATH/$MOBILEPROV_NAME.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$IOS_KEY_PASSPHRASE" --output $SECRETS_PATH/$CERTIFICATE_NAME $SECRETS_PATH/$CERTIFICATE_NAME.gpg
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles/
cp $SECRETS_PATH/$MOBILEPROV_NAME ~/Library/MobileDevice/Provisioning\ Profiles/
security create-keychain -p "" build.keychain
security import $SECRETS_PATH/$CERTIFICATE_NAME -t agg -k ~/Library/Keychains/build.keychain -P "$IOS_KEY_PASSPHRASE" -A
security list-keychains -s ~/Library/Keychains/build.keychain
security default-keychain -s ~/Library/Keychains/build.keychain
security unlock-keychain -p "" ~/Library/Keychains/build.keychain
security set-key-partition-list -S apple-tool:,apple: -s -k "" ~/Library/Keychains/build.keychain
Take the following snippet and change to your teamID
and bundle ID:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>5V2Q9SWB7H</string>
<key>destination</key>
<string>export</string>
<key>uploadSymbols</key>
<true/>
<key>uploadBitcode</key>
<true/>
<key>provisioningProfiles</key>
<dict>
<key>it.bz.noi.community</key>
<string>it.bz.noi.community</string>
</dict>
<key>signingCertificate</key>
<string>Apple Distribution</string>
</dict>
</plist>
- Create a folder
.github
- add a file
main.yml
- fill it with the following content and adapt it to your folders, ID and profile:
name: CI
on: [ pull_request, push ]
jobs:
## Test on latest MacOS
test:
runs-on: macos-11
steps:
- name: Checkout the code
uses: actions/checkout@v2
# # List possible Xcode versions
# - name: Force Xcode versions
# run: sudo ls /Applications/*xcode*
# See https://xcodereleases.com/ for details (we choose the latest Release)
- name: Force XCode 13.0.0
run: sudo xcode-select -switch /Applications/Xcode_13.0.app
- name: Inject GoogleService-Info.plist
env:
GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST }}
run: echo "$GOOGLE_SERVICE_INFO_PLIST" > NOICommunity/GoogleService-Info.plist
- name: Resolve package dependencies
run: xcodebuild -resolvePackageDependencies
- name: List schemes (optional step to gather information)
run: xcodebuild -list -project NOICommunity.xcodeproj
- name: Test the app
run: |
set -eo pipefail
xcodebuild clean test \
-scheme NOICommunity \
-destination 'platform=iOS Simulator,OS=15.0,name=iPhone 13' \
IPHONEOS_DEPLOYMENT_TARGET='15.0' \
| xcpretty
## Deploy to TestFlight
deploy_testflight:
name: Deploy to Testflight
runs-on: macos-11
if: github.ref == 'refs/heads/development'
needs: [ test ]
steps:
- name: Checkout the code
uses: actions/checkout@v2
# See https://xcodereleases.com/ for details (we choose the latest Release, available on macos-latest)
- name: Force XCode 13.0.0
run: sudo xcode-select -switch /Applications/Xcode_13.0.app
- name: Resolve package dependencies
run: xcodebuild -resolvePackageDependencies
- name: Inject GoogleService-Info.plist
env:
GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST }}
run: echo "$GOOGLE_SERVICE_INFO_PLIST" > NOICommunity/GoogleService-Info.plist
- name: Install gpg (if absent)
run: gpg --version &>/dev/null || brew install gnupg
- name: Setup provisioning profile
env:
IOS_KEY_PASSPHRASE: ${{ secrets.IOS_KEY_PASSPHRASE }}
run: ./.github/secrets/decrypt_secrets.sh
- name: Increment build number
run: |
set -eo pipefail
BUILD_NUMBER=$(date "+%Y%m%d%H%M%S")
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" NOICommunity/Info.plist
- name: Archive the project
run: |
set -eo pipefail
xcodebuild clean archive \
-configuration Release \
-scheme NOICommunity \
-sdk iphoneos \
-archivePath "$PWD/build/NOICommunity.xcarchive" \
-destination "generic/platform=iOS,name=Any iOS Device" \
OTHER_CODE_SIGN_FLAGS="--keychain ~/Library/Keychains/build.keychain" \
CODE_SIGN_STYLE=Manual \
PROVISIONING_PROFILE='a5e85966-b44d-4f43-b01d-babf8ce192c3' \
CODE_SIGN_IDENTITY="Apple Distribution"
- name: Export .ipa
run: |
set -eo pipefail
xcodebuild -archivePath "$PWD/build/NOICommunity.xcarchive" \
-exportOptionsPlist exportOptions.plist \
-exportPath $PWD/build \
-allowProvisioningUpdates \
-exportArchive
- name: Publish the App on TestFlight
if: success()
env:
APPLEID_USERNAME: ${{ secrets.APPLEID_USERNAME }}
APPLEID_PASSWORD: ${{ secrets.APPLEID_PASSWORD }}
run: |
xcrun altool \
--upload-app \
-t ios \
-f $PWD/build/NOICommunity.ipa \
-u "$APPLEID_USERNAME" \
-p "$APPLEID_PASSWORD" \
--verbose
Hint: To get the UUID of your provisioning profile you can do:
grep UUID -A1 -a it.bz.noi.community.mobileprovision | grep -io "[-A-F0-9]\{36\}"
This wiki contains additional information about the Open Data Hub alongside the Open Data Hub - Official Documentation 🔗 .