The purpose of this document is to be a step by step guide on how to automate app distribution (via Testflight) using Fastlane and Github Actions
The following links will be useful:
- Using App Store Connect API with fastlane
- Automate certificate and profile creation using fastlane
- App upload via GitHub Actions
- The first thing we will need is a private key (in base64), the key id and the issuer id.
- We can create it on the AppStoreConnect. Selecting role
App Manager
is enough. - Download the
p8
key to your desktop and name itkey.p8
. - Run
cat key.p8 | base64
on your terminal to get thebase64
representation. - The key id and issuer id are on available AppStoreConnect.
- We can create it on the AppStoreConnect. Selecting role
- Store the values on a new file called
.env
inside the Fastlane folder.- Remember to add
fastlane/.env
to your gitignore - APP_STORE_CONNECT_API_KEY_KEY_ID="key id value"
APP_STORE_CONNECT_API_KEY_ISSUER_ID="issuer id value"
APP_STORE_CONNECT_API_KEY_KEY="key in base64 (output of the point1.3
)" - Also add those key/value pairs to Github Secrets (for later usage in Github Action)
- Remember to add
- Now you can add a new line to your
Fastfile
to upload a build to Testflight:
lane :upload do
app_store_connect_api_key(
is_key_content_base64: true
)
build_app
upload_to_testflight
end
Given we already have App Store Connect API keys generated and working, we need to do 2 more things to automate certificate and profile creation:
- Setup private repository which will store encrypted certificates and profiles
- Setup fastlane to sync certificates and profiles between repository and development machine
- First, create a private repository and create an access token which will enable to read/write certificates to the repository without user credentials.
- We will use this private repository to store and retrieve the certificates and profiles
- Right after that, run
fastlane match init
in the project folder (the one which is going to use certificates). - Now we can generate certificates using command
fastlane match appstore/fastlane match development
- This will require a password for encryption/decryption. Use a strong password and save it somewhere safe.
desc "Sync certificates from private repo"
lane :sync_certs do
match(
git_branch: "main",
type: "appstore",
profile_name: "SwiftyPick AppStore",
app_identifier: "mdb.SwiftyPick"
)
match(
git_branch: "main",
type: "development",
profile_name: "SwiftyPick Development",
app_identifier: "mdb.SwiftyPick"
)
end
- Add these values to your
.env
file and to Github Secrets:- MATCH_GIT_BASIC_AUTHORIZATION="the access token generated in point
1
" - MATCH_PASSWORD="the encryption/decryption password used in point
3.1
"
- MATCH_GIT_BASIC_AUTHORIZATION="the access token generated in point
Now we just need to:
- Create fastlane action with release flow
- Create GitHub workflow based on preferred events
- Add a new lane in your Fastfile:
desc "Upload to testflight"
lane :distribute_to_testflight do
app_store_connect_api_key(
is_key_content_base64: true
)
if is_ci
setup_ci
end
sync_certs
bundle_install
cocoapods
increment_build_number(
build_number: latest_testflight_build_number(
username: ENV['APPLE_ID_USERNAME'],
app_identifier: "mdb.SwiftyPick"
) + 1
) # This is to prevent being rejected by AppStoreConnect for submitting multiple builds with same number
# You have to add your `APPLE_ID_USERNAME` to Github Secrets to make this worj
build_app(
export_options: {
provisioningProfiles: {
"mdb.SwiftyPick" => "SwiftyPick AppStore"
}
}
)
upload_to_testflight(
skip_waiting_for_build_processing: true,
changelog: changelog_from_git_commits(
commits_count: 5,
pretty: "- %s",
merge_commit_filtering: "exclude_merges"
) # This is to set the `What to test?` notes to the latest 5 commit messages
)
end
- Create a new github action to upload the app to testflight:
name: Upload build to Testflight
on:
push:
branches: main
jobs:
upload-build:
runs-on: macos-latest
env:
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: '5' # This is to allow the CI to fetch the latest 5 commits
- name: Upload to Testflight
run: fastlane distribute_to_testflight
That's it! If you follow the steps above you should be able to start distributing your app to testflight automatically on every push to the main
branch using Github Actions and Fastlane 🤖 🚀