I started to know the concept of continuous integration (CI) when I was doing my Master’s degree at NUS. However, only when I really applied this technology to our products development process, did I start to feel its huge values over the software development lifecycles, especially, when you are working in an agile environment. It is of great help in improving software quality.
Usually, a CI system is a series of tools applied altogether. For example, you need an automation server, saying Jenkins or Travis, as well as a mature build scripts which automates the build, test, validation and product packaging processes. This article tries to present a picture as comprehensive as possible about how to manage software build process using Jenkins as continuous integration system for the two most popular mobile platforms, i.e. iOS and Android as these two mobile platforms are current major mobile operating system.
Jenkins master-agent organisation
Jenkins is good continuous integration system with plenty of awesome plugins available to fully customise your building process. Usually, you will have a main build machine serving as a “Jenkins Master” and this master will distribute various building jobs to its build agent node. This distributed building system gives you the possibilities to manage and build a large number of projects.
Jenkins master selection
I will take Debian as Jenkins master for example. On agent nodes, you don’t need to configure the Jenkins itself, but for the software build environment setup, it is still your own responsibility to configure it well, e.g. you have to configure your environment paths for JDK, ant, gradle, xcode, etc.
Let’s assume we have a product named Arophix, which is a mobile application running on Android and iOS platforms. In our case, we will build Android application on Debian which is also our Jenkins master, and build iOS application on macOS which is the Jenkins agent node.
Regarding how to install Jenkins master, please refer to my another article setup-jenkins-with-docker.
Jenkins master-agent logical diagram
Usually, we should only let Jenkins mater do “master” jobs only meaning that it should not take any heavy build jobs. And we should have TWO agents to build iOS and Android respectively, then the master will only be acting as a facilitator, e.g. triggering the release and packaging the release artifacts. However, this will require at least 3 computers, i.e. Jenkins master, iOS agent and Android agent, to manage the whole build process. Sometimes, in order to save the cost of the third build machine, we can utilise either of the build agent as Jenkins master as well, but just make it be dockerised (run as a docker container) so that it won’t mess up the environment configuration for the typical build jobs on that agent machine. Below diagram is utilising Debian agent as the Jenkins master.
From this topology diagram, we can have below information.
- Actor trigger arophix-release job from Jenkins master. This job will check the SCM server to fetch the latest revisions. This revision will be the source code base for Jenkins master and agent to build.
- Master and agent will receive the revision number copied from arophix-release job build artifacts and start to pull the source code of this revision. And then the jobs and-arophix-release and ios-arophix-release will be triggered in parallel on Debian master and macOS agent. After the and-arophix-release and ios-arophix-release job complete, the final build artifacts of Android and iOS will be available on Debian master and macOS agent.
- Job arophix-package-all will copy the build artifacts generated from and-arophix-release and ios-arophix-release and then do the final packaging process. The final deliverable will stored on the Jenkins master machine.
This is how we will organise our Jenkins master and agent and how we will distribute the corresponding iOS and Android jobs.
Jenkins jobs strategy
For Jenkins jobs, there are various ways to configure according to different build requirement and circumstances. For example, you can configure the jobs according to your product flavours (lite or pro version) or different build mode (debug or release) and any other possible requirements so long as it is meaningful.
For the mobile product development, I will configure in such a way:
Continuous integration (ci) jobs
On Debian master
- debug mode ci job for Android: and-arophix-ci-debug
- release mode ci job for Android: and-arophix-ci-release
On macOS agent
- debug mode ci job for iOS: ios-arophix-ci-debug
- release mode ci job for iOS: ios-arophix-ci-release
On Debian master
- initiate job for release: arophix-release
- Android release job: and-arophix-release
- merging job for release: arophix-package-all
On macOS agent
- iOS release job: ios-arophix-release
Of course, we can have some other jobs, for example, validation jobs, compilation jobs, unit test jobs, system test jobs and so on. But for simplification purpose, i will not add these jobs into consideration.
In the real case of your Jenkins setup, if you have enough budget for the build machine cost, it will much better to distribute your build jobs to different build agents evenly and ask your Jenkins master to take the facilitator role only so that your build jobs will be faster and more efficient.
Schedule your jobs
For the Jenkins jobs, we should schedule them periodically. For the CI jobs, usually, should be every 2 hours because within 2 hours there might be some code push from developers. And the bad commits will be discovered every 2 hours if any, which will help the person committing the bad code to debug and fix, thus the rest of the team members will be impact less due this bad commit. According to development circumstances, you can schedule this jobs more or less frequent.
Jenkins is using CRON expression for the jobs scheduler, please refer to “How schedule build in Jenkins” for some examples.
Please be ware that the Jenkins executors are executed concurrently on the node, which means it may have some impacts on the shared resources, e.g. the Android emulators. Thus, I would like to say that you should only allow for 1 executor for each Jenkins node to escape from this kind of concurrence issues on shared emulators. But, of course, this may not make sense for some circumstances, it depends.
A fact we have to face is that usually the SCM repository is not in a small size, sometimes it can reach a few gigabytes. In such case, it will be better to create some symbol links for different jobs to the same local repository so that you can save your disk space considerably. For example, you can let the debug and release jobs point to the same repository to save disk space. However, just be aware that repository sharing my make you loose some of the intermediate build outputs, for example some test reports, which maybe cleaned up by another job being executed subsequently.
Clean up isolation
To avoid the issue of repository sharing, one possible solution is to isolate the cleanup actions. More specifically, for example, ios-arophix-ci-debug job should only do cleanup the build intermediate files for debug mode and should not cleanup any intermediate files for release mode which will be the responsibility of ios-arophix-ci-release job.