Github actions + Fastlane + iOS = ❤

TJ Gillis
TJ Gillis
Nov 7 · 4 min read

There is a lot to cover here so we will jump right in by looking at what our ideal build process would be:

  • xcodegen project¹
  • fastlane build/sign
  • upload to hockey app²

Straightforward, eh?

Let’s see what this looks like when we put this into an action workflow.

name: CI

on: [push]

jobs:
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Install and run xcodegen
run: |
brew install xcodegen
xcodegen generate
- name: Run fastlane setup
env:
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: |
fastlane setup --verbose
- name: Run fastlane build
env:
MATCH_REPO: ${{ secrets.MATCH_REPO }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASS }}
SIGNING_ID: ${{ secrets.SIGNING_ID }}
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: |
fastlane compile --verbose
- name: Run fastlane deploy
env:
HOCKEY_TOKEN: ${{ secrets.HOCKEY_TOKEN}}
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: fastlane deploy --verbose

That’s a clean build script. Let’s step through it and see what’s happening here.

name: CI
on: [push]

Here we have a name and an on field. Name is exactly what it sounds like. This is the name of your workflow. The next field is more interesting. This is an array of different webhooks you can listen to for when to run the job. For now, we’re just interested in push but here are the docs if your case requires others.

jobs:
build:
runs-on: macOS-latest

Following this, we have jobs and runs-on. Jobs are what they sound like. A unit of work. Here, our job is called build. Within that build job we can choose what OS we want to work on by setting runs-on. See here for the different platforms but in our case, we’re attempting to build for iOS so macOS it is.

steps:
- uses: actions/checkout@v1

Next, we have our steps. The first step is mandatory if you want to actually check out the code from your repo. You can read more about it here but essentially without it you aren’t able to perform any other tasks, which would make this exercise rather pointless.

- name: Install and run xcodegen
run: |
brew install xcodegen
xcodegen generate

Now we’re starting to see our code performing what we described at the top as our ideal process.

This here is a step named “Install and run xcodegen”. Luckily, the macOS image comes pre-installed with homebrew so we will use that to install xcodegen and generate a project with it.

- name: Run fastlane setup
env:
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: |
fastlane setup --verbose

Our next step, “Run fastlane setup”, is another element that is concisely named. But this is where things get tricky, at least for me. For match and fastlane to work how I wanted, I required access to the keychain. This is not something that we seem to have permissions to when using actions.

Thankfully, there is a fastlane action (this is not the same as a Github action) that will create a keychain and set it as the default, which makes up the content of our setup_project lane.

lane :setup do
setup_project
end

private_lane :setup_project do
create_keychain(
name: "actiontest_keychain",
password: "meow",
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: false
)
end

Above we use create_keychain to generate a new keychain that we’ve called actiontest_keychain, with a password of meow.

Now that we have our keychain made, let’s download our certs and build the app.

- name: Run fastlane build
env:
MATCH_REPO: ${{ secrets.MATCH_REPO }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASS }}
SIGNING_ID: ${{ secrets.SIGNING_ID }}
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: |
fastlane compile --verbose

Here you see an example of environment vars and secrets. Secrets are in your GitHub repo settings and allow for removing passwords and other sensitive info from source code. Very useful. But Fastlane does not understand how to access secrets, so we need to assign it to an environment var so we can ensure access.

private_lane :build do
match(
type: "appstore",
readonly: is_ci,
keychain_name: "actiontest_keychain",
keychain_password: "meow"
)

update_project_provisioning(
xcodeproj: ENV["XCODE_PROJ"],
profile: ENV["sigh_com.redspace.actionstest_appstore_profile-path"],
target_filter: "actionstest",
build_configuration: "Release",
code_signing_identity: "iPhone Distribution: REDspace Inc."
)

build_app(
scheme: "actionstest",
project: ENV["XCODE_PROJ"],
)

end

Our build lane is simple for how much trouble it gave me.

First, we run our fastlane match action. The special part of this match is that we are telling it what keychain to add the certs to. Another thing to keep in mind is at the moment it is difficult to check out repos on another host (such as Stash) with ssl. Your best bet would be to use http check out.

Since we’re not using automatic code signing, we must run the update_project_provisioning fastlane action with our profile, target, config, and signing identity. In terminal match will print out a list of environment variables that it has created and within them you’ll find an environment variable with your profile already set.

Finally, call build_app.

After that’s completed, we have one final step, which is to upload to hockey app.

- name: Run fastlane deploy
env:
HOCKEY_TOKEN: ${{ secrets.HOCKEY_TOKEN}}
APPLE_ACCOUNT: ${{ secrets.APPLE_ACCOUNT }}
TEAM_ID: ${{ secrets.TEAM_ID }}
run: fastlane deploy --verbose
content_copy
private_lane :hockey_upload do
hockey(
api_token: ENV["HOCKEY_TOKEN"],
create_status: "2",
ipa: "actionstest.ipa",
notes: ENV["RELEASENOTES"]
)
end

No secret sauce here.

Congratulations! You’ve created an action capable of generating, signing, building, and deploying your iOS project. Yay!

If you’d like to see this is as completed project we have made an example available on our GitHub.


Footnotes:

¹ XcodeGen is a command line tool written in Swift that generates your Xcode project using your folder structure and a project spec.

² HockeyApp is a service that allows developers to recruit and manage testers, distribute apps, and collect crash reports, among other things.

TJ Gillis

Written by

TJ Gillis

Games Technical Manager / Mobile Dev / Dad / Streamer / Beer lover

Well Red

Well Red

Exploring the tech industry one bit at a time. Brought to you by redspace.com.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade