Following the topic of Continuous Integration (CI) that I started here, in this post we are going to see how we can build our app, and run unit tests remotely with a pipeline in Azure.

If you want to go straight to the Continuous Deployment (CD) part, check this other post.

In Azure, pipelines are built using .yml files, where part of the setup is configured and the steps of the pipeline are defined.

Pipeline Code

In Azure, and in most of the CI/CD services you can find, we need to indicate what is going to trigger the pipeline, where do we want to execute it in terms of operating system and what are going to be the steps for the pipeline to follow.

We are going to see each section individually and then finish with a complete example.

Trigger

With triggers, there is a lot of customization that you can achieve.

You can for example run builds on a schedule like so, thanks to Cron. In this example, the pipeline is going to be triggered at 10 PM every day with the contents of develop.

schedules:
  - cron: '0 22 * * *'
    displayName: 'Nightly build'
    branches:
      include:
        - develop
YAML

For most of us, what you would probably want is to run the pipeline when you merge a pull request to the developbranch or when you want to release a new version to production after a merge to main/master.

For that, we just need the following line at the top of our file. In this case, we want to run the pipeline every time a change is made in the develop branch.

trigger:
  - develop
YAML

Pool

The pool is what is going to tell Azure where do you want to execute your pipeline. For Android, we could use the Linux based machines, but if you are building an iOS app, then you would have to choose a macOS option.

pool:
  vmImage: 'ubuntu-latest'
YAML

Steps

For Android, here is where we run the Gradle tasks or the bash scripts that our pipeline is going to execute sequentially.

The first thing to do is checkout the Git repository. As the pipeline is inside the repo, we can just do it like so. You can read more about the options of this command here.

steps:
  - checkout: self
    persistCredentials: true
    clean: true
YAML

After that, we can start running Gradle tasks. In the following example, I’m building all the variants of the Android app and then running unit tests on each of them.

- task: Gradle@2
    displayName: 'Build app and run unit tests'
    inputs:
      gradleWrapperFile: 'gradlew'
      options: '--stacktrace'
      tasks: 'clean assemble testPreDebug testProRelease'
      publishJUnitResults: true
      javaHomeOption: 'JDKVersion'
      sonarQubeRunAnalysis: false
      sqGradlePluginVersionChoice: 'build'
YAML

Each step is going to be shown when the pipeline is executed, the displayName variable is going to define the name of the step to be shown.

Afterward, in the inputs variable is where we execute Gradle like we would do locally. You can do it using normal bash script but instead of using the task Gradle@2, you would use the CmdLine@2 task, and then in the inputs field, you could run a script like so.

# For a bash script
- task: CmdLine@2
    displayName: 'Run a script'
    inputs:
      script: |
        # Some bash commands separated by a line jump

# For single line bash commands
- task: Bash@3
    displayName: 'Run bash command'
    inputs:
      targetType: 'inline'
      script: |
        # A bash command
YAML

Variables

To keep your keys or other data secret and use it when you run a pipeline, you can store them in variables. This can be done in the Azure web when you are editing a pipeline, it’s pretty straightforward.

Then to use them, you just need to use this syntax $(VARIABLE_NAME).

Complete sample pipeline

The following sample runs on changes in the develop branch on a Linux machine and executes these steps.

  1. Checkout the repo
  2. Set the username to whom the commit will belong
  3. Checkout branch and fetch any changes
  4. Set the Java JDK to use
  5. Write the local.properties file with pipeline variables.
  6. Assemble the app and run unit tests
# Android
# Build your Android project with Gradle.
# Add steps that test, sign, and distribute the APK, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/android

trigger:
  - develop

pool:
  vmImage: 'ubuntu-latest'

steps:
  - checkout: self
    persistCredentials: true
    clean: true

  - script: |
      git config --global user.name "MyUsername"
      git config --global user.email "myuser@email.com"
      git config --global push.followTags true

  - task: CmdLine@2
    displayName: 'Checkout branch'
    inputs:
      script: |
        export branchName=$(echo $(Build.SourceBranch) | cut -c12-)
        git fetch
        git checkout $branchName
        git reset --hard origin/$branchName

  - task: Bash@3
    displayName: 'Use JDK11 by default'
    inputs:
      targetType: 'inline'
      script: |
        echo "##vso[task.setvariable variable=JAVA_HOME]$JAVA_HOME_11_X64"

  - task: Bash@3
    displayName: 'Config local.properties'
    inputs:
     targetType: 'inline'
     script: |
       echo KEY=$(KEY) > ./local.properties

  - task: Gradle@2
    displayName: 'Build app and run unit tests'
    inputs:
      gradleWrapperFile: 'gradlew'
      options: '--stacktrace'
      tasks: 'clean assemble testPreDebug testProRelease'
      publishJUnitResults: true
      javaHomeOption: 'JDKVersion'
      sonarQubeRunAnalysis: false
      sqGradlePluginVersionChoice: 'build'
YAML

After digging into the code, there’s some preparation that needs to be done before running the pipeline, which is giving permissions to the users to access the repository where our Android project is hosted.

Users Permissions

For this pipeline, we need permissions to read the repository, so that we read the Git branch that we want to test.

In order to give this permission to the user that is going to be running the pipeline, we need to go to our pipeline, and in then in the three dots there is a Manage Security option, where we must change the read permission to allow.

To see which user is going to be running the pipeline, you can go to your pipeline settings, where you are going to see a Variables tab. There is a variable called system.collectionId, which you can then use to search for the user in the Manage Security section that we talked about before, so that you know who to give the permissions to.

Featured image by An Tran on Unsplash


If you want to read more content like this and support me, don’t forget to check the rest of the bolg or subscribe here to get an email every time I publish new content.

Categorized in: