When building an iOS project, you probably want to have some form of continuous integration working on your codebase.
Continuous integration
Continuous integration (CI) is a process where each code change results in certain checks to be performed. It is best to automate this.
Continuous develivery/deployment
On top of integration, you most likely want to make sure your code changes become available to your users as well. To help in this process it is good to perform Continuous delivery (CD). CD is the process o automatically deploying your code changes when all checks have passed.
You need to run your CI/CD somewhere
In 2019 Github made GitHub Actions available. Recently I worked on getting CI/CD working on several codebases. The lessons learned I applied to the CocoaHeadsNL app. You can check the CocoaHeadsNL app’s entire codebase on Github, including it’s CI/CD process.
What are Github Actions
GitHub Actions allows us to run a workflow on any GitHub event on Linux, macOS, Windows, ARM, and Docker containers. You get live logs. It has a built in secret store. It is free (or cheap) depending on I you are an open Github repository or not.
In our case, it is good to know hosted runners can be a full macOS environment, which is required for xcodebuild.
To define a workflow you add a Yaml file at a well defined location within your repository: .github/workflows/[filename].yml
name: Deploy
on:
# Trigger the workflow on push
# but only for the master branch
push:
branches:
- master
jobs:
Build:
runs-on: macOS-latest
steps:
- name: Dump file hierarchy
run: ls -R
Steps involved
Your building blocks are called steps. And we need to:
- Checkout the code
- Compile, archive and codesign it
- Upload to Apple
Checkout the code
- uses: actions/checkout@v1
Compile, archive and codesign
- name: Select Xcode
run: sudo xcode-select -switch /Applications/Xcode_11.3.app
- name: Xcode version
run: /usr/bin/xcodebuild -version
- name: Build archive
run: |
xcodebuild -sdk iphoneos -project CocoaHeadsNL/CocoaHeadsNL.xcodeproj \
-configuration Release -scheme CocoaHeadsNL \
-derivedDataPath DerivedData \
-archivePath DerivedData/Archive/CocoaHeadsNL archive
- name: Export Archive
run: |
xcodebuild -exportArchive \
-archivePath DerivedData/Archive/CocoaHeadsNL.xcarchive \
-exportOptionsPlist provisioning/App-Store.plist \
-exportPath DerivedData/ipa
Upload to Apple
- name: Deploy App to Apple
run: |
xcrun altool --upload-app --type ios \
--file DerivedData/ipa/CocoaHeadsNL.ipa \
--username "${{ secrets.appstore_connect_username }}" \
--password "${{ secrets.appstore_connect_password }}" --verbose
We need to do something with our “secrets”
More info on Github Actions’s Secret Store: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets.
- Allows storing of string based data of max 64kb
- Stored encrypted with a key stored with Github.
- So don’t store your launch codes here people.
- I chose to trust Github to some degree.
So what we need to do is:
- Encrypt files with GPG
- Add files to repo
- Decrypt files in your workflow
- Password is stored as Github secret
Currently gpg is missing on macOS Hosted Runners
Fix it with this step:
- name: Install GPG
run: brew install gnup
Decrypting files
#!/bin/sh
# Decrypt the files
# --batch to prevent interactive command --yes to assume "yes" for questions
gpg --quiet --batch --yes --decrypt --passphrase="$PROVISIONING_PASSWORD" \
--output provisioning/AppStoreCertificates.p12 provisioning/AppStoreCertificates.p12.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$PROVISIONING_PASSWORD" \
--output provisioning/CocoaHeadsNL-AppStore-General-Notification.mobileprovision \
provisioning/CocoaHeadsNL-AppStore-General-Notification.mobileprovision.gpg
# Three more lines decrypting a provisioning profiles ommited
# Install the provisioning profiles
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
echo "List profiles"
ls ~/Library/MobileDevice/Provisioning\ Profiles/
echo "Move profiles"
cp provisioning/*.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
echo "List profiles"
ls ~/Library/MobileDevice/Provisioning\ Profiles/
security create-keychain -p "" build.keychain
security import provisioning/AppStoreCertificates.p12 -t agg \
-k ~/Library/Keychains/build.keychain -P "$PROVISIONING_PASSWORD" -A
# install distribution cert and key
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
I will extend this post based on any questions I receive.
I also presented on GitHub Actions during the January 2020 CocoaHeadsNL meetup, check the video on Youtube: Building iOS apps with Github Actions, Jeroen Leenarts (English) during CocoaHeadsNL meetup January 2020