CI/CD Automation for Mobile Development from Scratch (Part 1)
Tutorial on how to implement a Bitrise pipeline in less than one hour
This story guide you to implement the CI/CD cloud service in less than one hour. CI/CD is one of the best practices for mobile development teams to implement:
It is also an agile methodology best practice, as it enables mobile development team to focus on meeting business requirements, code quality, and security because other tasks are automated.
In this tutorial, I would like to introduce Bitrise. Bitrise is a leading CI/CD cloud platform for mobile. The setup of workflows using in this platform is easy and intuitive. As a mobile developer, we can setup some basic workflows in just a few hours. In this story, you will be presented how to setup a typical workflow: merge check for a Pull Request. The main objective of this workflow is to validate the pull request created by a developer in our team, before merging it in development branch. This example workflow will run all tests (UI tests, unit tests, …) enabled in the project’s test target.
Prerequisites: in this tutorial, I assume that you already had a Bitrise’s account and was given all necessary permissions to create and modify a Bitrise’s app. If you don’t have an account, you can create one for free. A Bitrise’s app can be quickly created from Dashboard -> Add new app -> then follow the guide to connect your repo to the app. Though this tutorial’s objective is to merge check an iOS build, it’s still relevant for Android developers to look at and learn the set up process.
Setup Bitrise app
To create this merge-check workflow, firstly go to the app dashboard, then click on workflow tab to open Workflow manager.
You will see workflow manager displayed like below:
On the first row, it displays all configurations needed for an iOS build:
- Workflows: this is where we can see and manage all workflows. On the second line, there are options we can use to switch, create, add existing, and rearrange workflows. When we created this app, by default, Bitrise provide us a “primary” workflow, which is used to archive iOS apps. Now, click on “+ Workflow” and give it a name (in this tutorial I will use “DemoWorkflow”) by cloning the “primary”. We now have our first baby workflow. For the sake of our tutorial, we remove all preset steps, so we can created them from scratch.
- Code Signing: this is where we can safely keep certificates and provisioning profiles. At the top of this tab, we will see the guide about using a script to export our local certificates and provisioning profiles on Bitrise. In this tutorial, we don’t need to setup them, since the tests will run only on simulator. Later, when we need to archive the build to export it to Beta tools or App Store, we definitely need to setup this.
- Secrets and Env Vars: basically theses are stored parameters we need to use in the workflows. “Secrets”, well, mean confidential ones. Parameters saved here will be encrypted and will never be exposed in the logs. Info stored will be displayed as ***** in this section and in the logs. This is typical place to save passwords, token…etc. On the other hand, “Env Vars”, stored regular variables. This is where our stored parameters like: scheme name, project path, app version, …etc. Info stored here are exposed to all of our team members.
- Triggers: this is where we create rules on how to trigger a workflow. Bitrise provide 3 ways of triggering: Pull request, Push and Tagging. In this tutorial, we want to tests a pull request, so check the Pull Request tab, click on “+ Add Trigger” and fill the source, destination and workflow, we will get something like this:
This config mean when there’s a pull request from any branch, toward branch ‘develop’, it will trigger the workflow ‘DemoWorkflow’. Do ‘Save’ (Cmd + S) now. In addition of this triggers, we can also schedule a workflow at specified time, but it will be presented a bit later.
5. Stack: this is where we specify Xcode and Mac OS version to run our workflow. Select the correct one for your project.
6. bitrise.yml: this is the configuration file that sum up our whole setup in this Bitrise’s app. We may need to backup this file from time to time, just in case we need to rollback.
Setup Pre-Merge workflow
Now, it’s time for real work …:] Actually, there’s not much left to do here, we will finish it in 5 minutes. So, let’s switch back to Workflow tab. We will design the flow like this: get the repo, install pod or Carthage if needed, run tests, and that’s it.
- Cloning the git repo on the pull request’s branch: if your repo is private, add the step SSH Key (click on the “+” sign, and search for keyword ‘SSH’). This will activate the SSH key you generate when creating this Bitrise’s app. Then we add the step Git Clone repository. Normally, this’s all correct already, since Bitrise is smart enough to pre-fill all the information needed like Git URL, branch name (which is always Source branch)
2. Depend on the project, you may need to do pod install, or run Carthage. Do add step using keyword ‘Cocoapods’ or ‘Carthage’. Again you may not need to specify anything in the step’s config (in case of Cocoapods, Bitrise will setup pod repo and do pod install by default)
3. Now we have the project setup, we need to run tests. To do so add the step ‘Xcode Test’. In this step we should check if all pre-filled parameters are actually what we want, for example: the scheme, the iDevice to run tests, the iOS version…
At this point, we’re proud to say our mission was archived. This workflow will tests our app when there’s a pull request toward our develop branch. In effect, we can stop here, save our changes, and make a pull request to see if everything connects correctly together. Our workflow now will look similar to below:
It’s worth to mention that some cloud repo, like Bitbucket, integrates Bitrise seamlessly, so we will see a green checkmark once workflow finish successfully:
Below is two popular extra steps that make our CI/CD fancier ^^
4. Export apps, logs or artifacts: this step will export everything generated by the previous steps: logs, unit tests reports, archives, IPA, dSYM… so we can download it for review. Once implement this, we will get all the stuffs in the Apps & Artifacts tab:
5. Send success/fail message to Slack: we may not want to go check our Bitrise or Git repo from time to time to see if the tests passed. By sending message to our communication tools, we passively and immediately notified workflow’s result. So go search for ‘Slack’ step. This step required our Slack’s Wekhook URL or Slack’s API token, which is sensitive so we should stored them in ‘Secrets’
The final workflow now look like this:
Some helpful tips
It’s better to use environment variables in our workflow, rather than hard-code ones. When all ENV VARS in one place, it’s easier to review our configurations.
Variables should avoid Bitrise prefix: almost all steps in Bitrise generate output variables which have Bitrise prefix. Do not use this prefix help you avoid confuse when reading step’s config and possible conflicts.
Create common workflow steps: as you may notices, a workflow can be combined of multiple other workflows. If we intend to create many workflows for different scenarios, we should group the common steps in one workflow. For example, every workflow need firsts steps similar to SSH-> Git clone -> Pod install. So those steps could be set up in a workflow name ‘PreBuild’.
Use Pull, Push cache steps: Bitrise provide Pull/Push steps, which allow to pull cache builds from previous build, and push the current cache when workflow done. It improve considerably workflow’s build time.
Use option ‘Run if previous steps failed’: every step has this toggle. Use it when we feel convenient. For example, we don’t need to export apps build if previous step archiving app failed. It helps saving time since we don’t have to spend time in running a step for nothing.
Enable rolling builds: activate all theses options in App’s Settings. It will cancel all previous builds for Pull Requests/Pushes to the same branch, so we avoid duplicated worked
Schedule workflow: in addition of trigger on events, we can schedule workflow to run at a specific time. At the app’s dashboard, click on “Start/Schedule a build” -> toggle ON “Schedule this build” and setup the day, time and the workflow we want to run. This feature is mostly use for time consuming tasks, like UI tests, performance tests, TestFlight build delivery. Since it’s time consuming, we don’t want them to run during our workday, because they will create traffic jams without much adding value. We should make our CI/CD works while we sleep ;)
Where to go from here?
There are still many awesome step features to discover. What we did in this tutorial is just a scratch on the surface of what CI/CD can help us improving our daily performance. Take your time to discover them and try them. That may help you generate some ideas on how to facilitate your daily tasks’s.
Cloud CI/CD is a great start in enabling automation in our development process. In fact, my team use this tool alone during two first years and it improves a lot our work’s velocity, quality and happiness. But it turns out that CI/CD level up to full power and versatility only when combining with Fastlane. Fastlane is an open source automation app that allows you to write customized tasks, defined your own action’s rules, and sync all our work platforms seamlessly. To name a few:
- Capture screenshots automatically
- Distribute beta builds to multiple Beta testing services (Testflight, Firebase App Distribution, Hockey…)
- Publish multiple apps at once with a single command line
- Automatic code signing for iOS
- Update test, build, delivery info on your JIRA ticket
- All of these happen while we’re working on something else
Basically Fastlane is able to sync with every platform that provide an API, and his abilities is limited only by our imagination ;]
If you’re interested to know more about Fastlane, this is the part 2 of this tutorial: