Product

Build you CI/CD pipeline for Mobile Applications with Jenkins, Github Actions and Azure Devops

This article will cover the main challenges when implementing a CI/CD pipeline for mobile applications. We will also provide examples of how you can implement a CI/CD pipeline for Android and iOS applications in the most used Mobile CI/CD tools.

Thu 27 October 2022

85% of teams who integrated security scanning into their CI/CD pipeline respect their patch SLO, fix vulnerabilities faster, change code more often and express a better understanding of their code base.

This article will cover the main challenges when implementing a CI/CD pipeline for mobile applications. We will also provide examples of how you can implement continuous integration pipelines for Android and iOS applications in the most used Mobile CI/CD tools.

Before incorporating security checks into our CI/CD pipelines, we have to ensure that they are built correctly and stable and that we have the right observability to fix them in case something breaks.

Here are some of the common challenges we see when adopting CI/CD for mobile applications:

  • Lack of CI/CD expertise:

    Most DevOps solutions require advanced CI/CD expertise to properly set up, customize, and maintain automated workflows and processes. Most application developers need time and experience to learn this skill set.
  • Unclear ownership:

    Implementing CI/CD is not a one-time task. As with every code written by humans, it can fail, break or behave unexpectedly. So, poor CI/CD pipeline management or implementation often makes it a cumbersome task to identify the failure causes immediately. This, in turn, makes it harder to redirect to the concerned teams for fixing code errors.

  • Restricting to Dev and QA Vs Prod environment:

    A mobile application usually consists of a client binary installed on the device and a backend to communicate with via an API. The CI/CD pipeline might have different configurations and environments to run the tests. One common mistake is to test using Dev and QA environments, and neglect the differences with the Prod environment. So the code in production becomes untested and can have errors, security issues, or unexpected errors.

  • Neglecting Dynamic analysis:

    Multiple companies implement CI pipelines with only static analysis. This is mainly because of the difficulty encountered while automating the application's dynamic analysis. The Static analysis aims to analyze binaries or source codes to find known patterns of vulnerabilities. However, static analysis is known for producing too many alerts for minor software flaws and unexploited defects. This is why the OWASP standard recommends implementing both static and dynamic analysis. The dynamic analysis is designed to detect security vulnerabilities while running the application. It covers only the scope used in the application and provides high confidence about the identified issue.

  • Difficulty to run UI automation:

    In a CI/CD pipeline, automating UI interactions is an important step to increase test coverage and ensure the scalability and quality of the tests. The usual challenge is automating all the UI interactions and ensuring they are stable through the different application versions, OS versions, and multiple devices.

  • Hidden limitations of CI/CD vendors:

    To have a fully automated CI/CD pipeline for a mobile application, you need to conform to the mobile application's rigid operating system requirements, rigorous code signing, frequently-updated SDKs and tooling, device testing, and app store submission. So, choosing the CI/CD vendor is essential to cover your needs and ease the implementation and maintenance.

Now let’s look at how we can add the Build of a mobile application (Android and iOS) to some of the most used CI/CD platforms.

Continuous integration for Android applications using Jenkins

We will learn how to generate an Android APK from source code in Jenkins. I assume you have already installed Jenkins.

  • Step 1: Install Java JDK:
    On your terminal run:
sudo apt install default-jdk
  • Step 2: Install Android SDK:
    Go to Android Download page, and grab the link listed in the table Command line tools only > Linux. Don’t download anything. Just copy the link.
# here you paste the link you grabbed in the developer.android.com site
sudo -iu jenkins wget https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip
sudo -iu jenkins mkdir android-sdk
sudo -iu jenkins unzip commandlinetools-linux-8512546_latest.zip -d android-sdk

# this step is important to accept the Android SDK license
yes |sudo -iu jenkins android-sdk/tools/bin/sdkmanager --licenses
  • Step 3: Install Gradle Plugin:

alt text
Manage Jenkins

.1 Open Jenkins: Manage Jenkins >> Manage Plugins >> Available

.2 Search for Gradle

alt text
Search Gradle

.3 Click on Install

alt text
Gradle installed

  • Step 4 Configure Jenkins:

.1 Open Jenkins: Manage Jenkins >> Configure System >> Global properties >> Environment variables

alt text
Add variables

.2 Add the variables:

  # depending on your local paths
  ANDROID_HOME : /var/lib/jenkins/android-sdk
  JAVA_HOME : /usr/lib/jvm/java-11-openjdk-amd64

alt text
Add Android Home variable

  • Step 5: Create Android build job:

.1 Open Jenkins: New Item

.2 Enter the job name

.3 Choose a Freestyle project and press OK

.4 Go to Source Code Management -> Check Git and fill in:

  • Repository URL: Git URL to your repo. Take this URL from GitHub. It should be a format of git@github.com:{username}/{repo}.git

  • Credentials: Select your credentials or create a new one in the credentials menu.

  • Branches to build: branch_name

.5 In the Build section, select Invoke Gradle script

alt text
Gradle Config

.8 Fill in the name of your tasks

.9 In the Post-build Actions fill in Archive artifacts: **/*.apk

.10 Press Save

  • Step 6: Start the Build:

    From the job dashboard, click Build Now, and you will see the job run. After successfully building, you can see generated Android app in the project information.

alt text
Apk Artifacts

Continuous integration for iOS applications using Jenkins

  • Step 1: Installation of Jenkins with XCode plugin (requires MacOS):

.1 Open Jenkins: Manage Jenkins >> Manage Plugins >> Available

.2 Search for Xcode integration

alt text
Xcode Jenkins Integration

.3 Click on Install

  • Step 2: Create a Development team from Jenkins Global configuration:

.1 Open Jenkins: Manage Jenkins >> Configure System

.2 Go to the Xcode builder section

.3 Go to the Apple Development Teams section and click on Add button

.4 Fill in the two fields Team Name and Development Team ID

alt text
Xcode Jenkins Development Team

.5 Add a Keychain section

.6 Fill in the values for Keychain Name and Keychain password (The keychain path would already be there)

alt text
Xcode Jenkins Keychain

.7 Apply and Save

  • Step 3: Configure Jenkins Job to clone the repository:

.1 Go to the project path in Jenkins workspace (/Users/Shared/Jenkins/Home/workspace)

.2 Open the project using Xcode

.3 In the General tab on Xcode, check that the Provisioning profile and all the information are correct

.4 You can try to build the project using Xcode to make sure all the information is correct

.5 The checkbox Automatically manage signing should not be checked

.6 Note the Team Name and the Bundle Identifier since we will be using them in the Jenkins configuration

  • Step 5: Configure the build in Jenkins:

.1 Open Jenkins, go to your project, and click on configure

.2 In the Build section, click on the build step button and select the Xcode option. It will add the Xcode-related fields.

.3 In General build settings, click on the Settings button

.4 Select the Development Team that you have recently added to Jenkins Global Configuration

.5 Fill Xcode Schema File:

.6 Select Pack application, build and sign .ipa? Option.

alt text
Pack & sign Ipa Jenkins

.7 The export method of the .app to generate the .ipa file. It Should be one in ‘development’, ‘ad-hoc’, ‘enterprise’ or ‘app-store'.

.8 .ipa filename pattern: A pattern for the IPA file name. You may use ${VERSION} and ${BUILD_DATE} (yyyy.MM.dd) in this string.

.9 Go to Code signing & OS X keychain options

.10 Select Manual signing

.11 Fill in the Bundle ID and Provisioning profile UUID (You can find it from your provisioning profile .mobileprovision file)

.12 Select Unlock Keychain

.13 Select the recently created Keychain value from the drop-down menu

.14 Go to the Advanced Xcode build options section

.15 Fill in the Xcode Workspace File fields: and Xcode Project Directory: $WORKSPACE

.16 Press Apply and Save

  • Step 6: Start the Build:

From the job dashboard, click Build Now and see the job run.

After successfully building, you can see generated Ipa app in the project information.

Continuous integration for Android applications using GitHub Actions

The runners offered by GitHub Actions allow you to run your build on Linux, Mac, and Windows.

All the runners have Java and Android already installed and configured (the list of pre-installed software is pretty long). Workers can build most Android projects out of the box without further setup.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the code
        uses: actions/checkout@v2
      - name: Build the app
        run: ./gradlew build

Continuous integration for iOS applications using GitHub Actions

For the IPA build, we will use the task yukiarrr/ios-build-action. The action builds iOS project and can export to IPA file. You can check the full documentation in the GitHub repository

name: Build and sign Ipa file

on:
 push:
   branches: [ main ]
 pull_request:
   branches: [ main ]

jobs:
 build:
   runs-on: macos-latest

   steps:
   - uses: actions/checkout@v2
   - name: Build and export iOS
     uses: yukiarrr/ios-build-action@v1.5.0
     with:
       project-path:
       workspace-path: ".xcworkspace path"
       scheme: "project name"
       export-method: "Choose app-store, ad-hoc, package, enterprise, development"
       configuration: "Choose Debug or Release"
       output-path: artifacts/output.ipa
       p12-base64: ${{ secrets.P12_BASE64 }}
       certificate-password: ${{ secrets.P12_PASSWORD }}
       mobileprovision-base64: ${{ secrets.ADHOC_MOBILEPROVISION_BASE64 }}
       code-signing-identity: ${{ secrets.CODE_SIGNING_IDENTITY }}
       team-id: ${{ secrets.TEAM_ID }}

Continuous integration for Android applications using Azure DevOps

  • Step 1: Prepare the pipeline:

alt text
Azure DevOps

.1 Open your project and navigate to the Pipelines section on the left panel

.2 Click on Create Pipeline

.3 Select your Git repository

An empty Yaml pipeline is generated. Now we need to build the binary file using Gradle.

  • Step 2: Building Apk file using Gradle task:

.1 Click on show assistant

alt text
Azure DevOps Show assistant

.2 Search for Gradle

alt text
Azure DevOps Gradle

.3 You can turn off Publish to Azure Pipeline under JUnit Test Results section

.4 Set your task name to build the file

.5 Click Add button

.6 Click Save to save our CI pipeline

You can also add the tasks' Copy files' and 'Publish build artifacts' to inject the generated file into the pipeline if you want to run other jobs on the file.

Continuous integration for iOS applications using Azure DevOps

  • Step 1: Prepare the pipeline:

.1 Open your project and navigate to the Pipelines section on the left panel

.2 Click on Create Pipeline

.3 Select your Git repository

An empty Yaml pipeline is generated. Now we need to build the binary file using Gradle.

  • Step 2: Building Apk file using Gradle task:

We need the provisioning profile and the iOS certificate to sign the Ipa file.

To upload them to our pipeline, we will be using the Secure files section:

.1 Go to pipelines -> Library -> Secure files

alt text
Azure DevOps Secure files

.2 Upload your provision file. In my case, I will call it my_provision_profile_ostorlab

.3 Export the certificate public key (.p12) and upload it. I will call it my_certificate.p12

.4 Go back to your pipeline and add three variables:

p12FileName = my_certificate.p12
p12Password = my_certificate_password
provisioningProfile = my_provision_profile_ostorlab

alt text
Azure DevOps Variables

.5 Click on show assistant

.6 Search for install apple provisioning

alt text
Azure DevOps Gradle

.7 Fill provProfileSecureFile with the variable $(provisioningProfile)

.8 Click Add

.9 Click on show assistant

.10 Search for install apple certificate

alt text
Azure DevOps Gradle

.11 Fill the file name and the password with the variable contents

.12 Click Add

  • Step 3: Build and sign the Ipa file:

To build the .ipa file, we need to use macOS as vmImage.

.1 We need to add the build configuration as a variable. You can set the value as Debug or Release.

.2 We need to add the SDK variable. From the macOS Terminal application, run xcodebuild -showsdks to display the valid list of SDKs.

.3 Click on show assistant

.4 Search for Xcode

alt text
Azure DevOps Xcode

.5 In Signing style, select Manual signing

.6 Fill in Signing identity with the value $(APPLE_CERTIFICATE_SIGNING_IDENTITY). This variable corresponds to ‘Install Apple Certificate’ we have added previously

.7 Fill in Provisioning profile UUID with the value $(APPLE_PROV_PROFILE_UUID) defined by the task Install Apple Provisioning Profile’.

.8 Click Add

.9 Click save to save our CI pipeline.

If you run your build for the first time, it will fail, asking you to grant access to the variables and the secure files. Click on Authorize resources and rerun your build.

In this article, we went through some challenges when implementing the CI/CD pipelines. We also discussed building the APK and the IPA mobile applications in the most used CI platforms. The CI implementation should be completed with a CD pipeline to automate the deployment after the review process. Since the Mobile application space has multiple frameworks to develop and build the application, the CI implementation can vary depending on the framework tools.

The following article will cover the flutter framework and how you can automate the Build in those CI/CD platforms.