Continuous Integration — GitLab — iOS — Part 1
By: Rakesh Chander
Tags: Continuous Integration, Best Practices, Pipeline, Gitlab, iOS

CI — Continuous Integration — is a must have now a days for the teams working on Shared Code Repositories viz GitHub / GitLab. Its time consuming for developers to validate all flows after each change. Here comes the CI to help us.

CI is a process which automates the process of build and test against each git push (defined steps). We can use CI to validate below-

Build

Unit Tests

UI Tests

Lint

Static Code Analysis (eg SONAR)

In this story I am going to explain how to achieve this for GitLab code repositories

Lets start with basic terminology — Job, Stage, Pipeline

Stage — Naming different stages to be which will be available as executable unit in that project’s scope

Job — Single Independent Unit which starts execution on Git Push as per rule defined against that job. Job must point to one of defined stage in that file. Job has script commands which will be executed once picked.

Pipeline — Against each push in git, list of jobs are filtered out as per rule set defined in them, and a sequence is prepared in the order in which

Note — Rule is basically the git branches against which one particular job should execute. If nothing is mentioned, then job will execute against all git push.

Here we have first and important step -

Gitlab will execute jobs on some runner machine and that runner machine needs to be configured for that project. Refer below link for setting up runners as per your need — https://docs.gitlab.com/ee/ci/runners/

Then create a file .gitlab.yml — Note that file name is having dot (.) to start with, it’ll be a hidden file — in your project root.

BUILD — Lets start with build stage

Whenever a code is pushed, we would like to validate whether there are any build issues from respective code changes.

To automate this, we have added stage named “build” under tag “stages”

Then we started defining a job and named that job as “build_core_all”

Under this job, as a standard practice, we removed all pods and re-installed (If you are using Carthage or SPM, you can change these script commands. In case you are not using any Dependency Manager, then you can skip these.)

Then we added standard “xcodebuild” command and specified workspace name and required scheme name

If you want that this job should execute on all push / merge, except tags, you can add “except” tag and mention “tags”. Similarly, you can mention and branch name like master, develop etc if you want to exclude those from this job.

Refer below snippet —

stages:

- build build_core_all:

stage: build

script:

- rm -rf Pods

- rm -rf Podfile.lock

- pod install

- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO -destination generic/platform=iOS

except:

- tags

TESTS — Execute Unit & UI Tests from Pipeline

We write both Unit & UI Tests to make sure that code remain bug free against any change. To automate the process of execution of Tests for each git push, we can use CI.

To automate this, we have added stage named “test” under tag “stages”

Then we started defining a job and named that job as “test_all”

Under this job, as a standard practice, we removed all pods and re-installed (If you are using Carthage or SPM, you can change these script commands. In case you are not using any Dependency Manager, then you can skip these.)

Then we added standard “xcodebuild” command for test and specified workspace name and required scheme name.

We can have different schemes for Unit & UI Tests or a different scheme for both. We can configure three different jobs targeting those three different schemes and define rules for branches on which those jobs will execute. In general we execute Unit Test Scheme for all branches other than master but we execute UI Tests only for develop branch as that takes some reasonable amount of time.

Refer below snippet —

stages:

- test

- build build_core_all:

stage: build

script:

- rm -rf Pods

- rm -rf Podfile.lock

- pod install

- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO -destination generic/platform=iOS

except:

- tags test_all:

stage: test

script:

- rm -rf Pods

- rm -rf Podfile.lock

- pod install

- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean test -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' -enableCodeCoverage YES

except:

- tags

- master

So, all set now to see this working now. Just push your code to git with this file.

Note — Make sure you have added active runner to that project

And as per your branch, in which you pushed code, and rules defined in yml file — gitlab CI pipeline will start executing jobs. At any stage, if any of job is failed, then whole pipeline aborts along with FAILURE.