Skip to content

πŸš€ Learn how to setup your first CI/CD pipeline with GitHub Actions and fastlane

Notifications You must be signed in to change notification settings

timdolenko/ci-introduction

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

How to do CI/CD for iOS?

This tutorial will teach you how to setup your own CI/CD based on the setup we currently have in Axel Springer in BILD team. BILD is a production app that currently has around 2M MAU.

Screenshot 2022-08-15 at 15 54 54

Our Goal

We want to:

  1. automatically run Unit Tests for each pull request
  2. deploy a new build to TestFlight whenever pull request is merged

Here's a quick diagram of how the process in the team might look like with this CI setup:

Workflow process diagramm

Clone this repo and checkout to the start branch.

It contains an empty SwiftUI app and 1 Unit Test.

Workflow initial setup

Now, let's go to Visual Studio Code and create our first workflow yaml file:

Create .github/workflows/pullRequest.yml file. I will just copy and paste the following to save time, but we'll go over everything:

name: Pull Request

on:
  pull_request:
    branches:
      - develop
  workflow_dispatch:

jobs:
  test:
    runs-on: macos-11
    steps:
      - uses: actions/checkout@v2

      - name: Cancel Previous Runs
        uses: styfle/[email protected]
        with:
          access_token: ${{ github.token }}

      - uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable

      - uses: ruby/setup-ruby@v1

      - name: Install Bundler
        run: gem install bundler

      - name: Install gems
        run: bundle install

      - name: Swift Packages Cache
        uses: actions/cache@v2
        id: cache
        with:
          path: |
            Build/SourcePackages
            Build/Build/Products
          key: ${{ runner.os }}-deps-v1-${{ hashFiles('BILDsolid.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
          restore-keys: ${{ runner.os }}-deps-v1-

I won't go into detail, because each platform, in our case it's Github Actions, has it's own syntax, it's more important to understand the principles and overall those config files are usually very easy to understand.

on: We describe when we want to run the workflow, or what will trigger it. In out case it's pull request against develop branch. workflow_dispatch means that we want to be able to trigger the workflow manually on Github.

jobs: Now we actually start to describe what we want to do. We have the first and the only job called test (can be named anything else). runs-on: macos-11 - Self-explanatory.

steps: Each job can have many of them.

actions/checkout@v2 - pull the repository code to the machine that was given us for this run. We start with a clean state.

cancel-workflow-action - basically if we already running the same workflow from the same branch, we want to first cancel the old one, and only then start the new execution. We discard the result of the previous workflow.

We can actually pass values or variables as a parameters to the step with with: keyword.

access_token: ${{ github.token }}

You see, github.token is actually a variable here. How do we now that it's available? - Documentation.

setup-xcode@v1 - install xcode

ruby/setup-ruby@v1 - install ruby - we will need it later.

Please add .ruby-version file to the root with the following contents: 2.7.2

gem install bundler and bundle install are needed to install Bundler, that's needed to manage ruby dependencies (kind of like SPM) and install all the gems (kind of like packages) that include fastlane. We will define the Gemfile later. But essentially all this installs fastlane.

actions/cache@v2 - we want to cache SPM modules to not refetch them again and again.

That's it. Now we can push and see how it works, but the most important part is missing - the unit tests.

Fastlane

To run them we will use fastlane. Let's setup a simple fastlane project:

First of all we need to install fastlane locally, for it let's create a Gemfile (no extension) in the root folder:

source 'https://rubygems.org'

gem 'fastlane'

Now please install ruby and Rubygems, there is a high chance that you already have them on your machine.

gem install bundler

Now run bundle install - it will read the Gemfile and install or update fastlane.

Okay let's create the first fastlane file: Fastlane/Fastfile

fastlane_version '2.157'
default_platform :ios

platform :ios do
    
end

And now finally add unit tests lane:

platform :ios do
    desc 'Builds project and executes unit tests'
    lane :unit_test do |options|
      scan(
        clean: options[:clean],
        skip_package_dependencies_resolution: options[:skip_package_dependencies_resolution]
      )
    end
end

Now, obviously it's not all it takes to run the test. We want to specify the project and the scheme among other things. Let's create Fastlane/Scanfile:

# For more information about this configuration visit
# https://github.com/fastlane/scan#scanfile

workspace "AnyApp.xcodeproj/project.xcworkspace"
scheme "AnyApp"
sdk "iphonesimulator"
device "iPhone 11"
code_coverage true
xcargs '-parallelizeTargets'
prelaunch_simulator true
derived_data_path "Build/"

Most of it is self explanatory, everything else - just read docs if you wanna understand it.

Please create .gitignore to avoid committing build artefacts and cache:

Build

# fastlane
Fastlane/report.xml
Fastlane/test_output

Okay, let's test how the fastlane works locally. In the terminal, from the root folder of the project, run:

fastlane unit_test

You'll see the simulator running our tests! How exciting! Now we just have to do the same on the CI - Super easy!

Let's go back to our workflow file pullRequest.yml and finally add this at the end of the file:

      - name: Run Tests
        run: bundle exec fastlane unit_test

Now we actually make use of the cache and check if it's there, here's how:

      - name: Run Tests (No Cache)
        if: steps.setup.outputs.cache-hit != 'true'
        run: bundle exec fastlane unit_test
      
      - name: Run Tests (Cache)
        if: steps.setup.outputs.cache-hit == 'true'
        run: bundle exec fastlane unit_test skip_package_dependencies_resolution:true

Now let's actually see if it works

Push the code!

If you'll raise a pull request from your branch

Screenshot 2022-07-06 at 14 54 58

Warning: if you create a project from scratch on your own and the latest Xcode available on Github Actions is still 13.2.1, you have to make sure your iOS Deployment target in the project is set to 15.2 and not higher.

After a few minutes you'll see beautiful green mark!

Congrats! πŸ₯³

About

πŸš€ Learn how to setup your first CI/CD pipeline with GitHub Actions and fastlane

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages