In my previous article, I explained what Azure DevOps isand all the tools it includes. But for me, the most interesting part of Azure DevOpsis the pipelines, and that’s what I’m going to focus on in this article.
Before we get started,let’s go back to basics.
In DevOps, a pipeline is a series of automated actions that allow you to compile, generate,package, test and deliver a solution with minimal human intervention and where each step depends on another.
Automation is increasingly important for two primary reasons. First, programmer time is expensive, so it is very important to deploy at that time where it has the best chance of adding real value. Second, even the best programmers can be the source of human error and avoiding that wherever possible is an advantage.As a result, human intervention is preferred only when there is a clear gain to be realized and all repetitive tasksshould be automated wherever possible.
More than ‘just’ automation, though, a pipeline can complete actions at times when a human is not available (for example, generating a midnight version of the app everynight) and can complete parallel actions.
Why Azure DevOps rather than another?
In the example I’ll develop below, I need a MacOS machine tocompile, sign and deploy theapp on the Apple AppStore. As you may have read in the previous articleIntroduction to Azure DevOps,Azure DevOps allows me to do all of this without difficulty and even free ofchargeatfirst.
The application that will serve as the example here is very simple: it is a to–dolistthat only manages data in memoryand loses it when the application stops. It isequipped withunit testing,interface tests,and manages these dependencies withCocoapods.
The various stages of your Azure DevOps pipeline:
The prerequisites to create your Azure DevOps pipeline
If you want to build this pipeline yourself then you’re going to need a number of things.
First of all, if you want to reuse the code, I’ll make available here, you can jump directly to the next paragraph. If not, here are the first prerequisites:
You will also need a number of additional elements:
An Internet connection
An Azure DevOps account (free to create)
A Git repository (either hosted directly on Azure DevOps or on another online source code manager such as GitHub, Bitbucket and GitLab)
If you’re covered there, you’re set to go – let’s get creating the pipeline.
10 Steps to create an Azure DevOps Pipeline
1. Defining triggers
Because an Azure DevOps pipeline role is to automate actions, it requires triggers. To begin your pipeline, you can use a number of different triggers:
A change in a Git branch or tag
A change to one or more specific files
The creation or modification of a pull request
Of course, for any of these triggers you can include additional interconnected triggers, too.
For this example, I will use the modification of a Git branch as the trigger, namely the modification of the “develop” branch. If you follow the Git Flow model, this implies that the Azure DevOps pipeline will be triggered only after the merge of a branch where the pull-request (or merge request) has been previously validated.
2. Defining your Azure DevOps Pipeline tasks
Within Azure DevOps, there are two ways to define the actions of a pipeline: via the user interface or via a YAML file.
The user interface is very convenient as a simple slide/deposit allows you to compound a pipeline. However, this method is not recommended by Microsoft as it does not allow versioning the evolution of the pipeline or allow you to make templates.
This is not the case with YAML files. Admittedly, YAML is a little rougher to deal with at first touch but it has some significant advantages over the user interface:
It is possible to version the tasks of the pipeline
It is possible to have a different state of the same script on several branches (in order to test or to generate different behaviors as required)
You can create templates that can be used in multiple pipelines while maintaining a similar body
It is not dependent on a user interface that is likely to evolve over time
It has more advanced functionality including compiling code, launching unit tests, signing applications, and communicating with third-party environments.
In our case, we want to be able to complete the following steps:
Launch unit tests
Compile a version for the App Store signed with our certificates
Recover output elements (IPA in our case) if you are on the “develop” branch
Deploy the app on TestFlight if on the testing branch
Deploy the app to the App Store if you’re on the master branch
3. Selecting your environment
Clearly, when creating an iOS app, we don’t have any choice about our computer: we need a Mac to compile our code. Fortunately, Azure DevOps offers three environments, namely Windows, MacOS, and Linux, and all in several versions. As I write this article, when it comes to MacOS, two different versions of OSX are available: MacOS X Mojave 10.14 and MacOS X Catalina 10.15.
If you do not have specific requirements related to the compiler’s operating system or test machine, my advice is to always select the latest one. To achieve this, you can employ the following code:
4. Signing the app
In order for the application to be published directly on the App Store or on TestFlight to be testable within an organization, it is necessary that the application be signed with a valid profile of that organization. Without this step, it’s impossible to go on, and so it will be necessary to recover a development or production certificate (in my case it’s production) as well as a provisioning profile for this application.
Of course, Azure DevOps has the ability to handle secure files such as the certificate and the provisioning profile. These files allow for the publishing of apps on your behalf on Apple’s App Store so it’s imperative to protect those same files. What’s more, depending on your organization, many peoples might have access to your Azure DevOps pipeline.
As the code suggests, two tasks are required here: “InstallAppleCertificate” and “InstallAppleProvisioningProfile”.
Note that each of the two tasks has a respective key called “deleteCert” and “removeProfile.” Once “true” these two keys allow you to remove the two files from the compilation machine or the virtual machine, respectively. It will take longer for the next pipeline run to copy the files, but it will not leave a trace after your execution which is preferred since you are using a machine that is not yours.
5. Managing your dependencies with Cocoapods
Most applications have dependencies.
Whether it’s your own libraries or those you’ve picked up from the community, everything has to be compiled at the same time in your project. Often we’ll use a dependency manager like Nuget (for my fellow .NETdevelopers), Gradle for my Android developer friends, or Cocoapods on iOS.
Of course, there are other options such as Carthage or Swift Dependencies Manager, but for the purposes of this article, we will settle for Cocoapods.
One last step to take before compiling the application.
Easy – nothing very complicated here at all.
6. Building the app
Compiling the application is the core of our project.
To do this, we need to use the certificate and provisioning profile that we previously assigned. Azure DevOps provides us with two variables after the “InstallAppleCertificate@2” and the “InstallAppleProvisioningProfile@1” tasks have been successfully completed:
Once successfully completed this step will generate the application’s IPA.
7. Running unit tests
As with any self-respecting project, we must have unit tests. Certainly, for the purposes of this article, we don’t need extensive testing, but we’ll have enough for a demonstration. Note that, unlike .NET, XCode relies on the previous compilation to run unit tests.
It is, therefore, necessary to compile the application before launching our tests. In our case, in order to save time and avoid making compiling twice, we will compile in the release version (if you have specified “Release” for the variable “Configuration”) of our application. To enable this, we add an option in the “Build Build Settings” of our project so that unit tests can run.
This is the “Enable Testability” option.
8. IPA Recovery
In some cases, it may be worth recovering an app’s IPA directly and installing it on a device via iTunes. To do this, simply publish the IPA file directly in the recoverable Artifacts in the Azure DevOps interface.
9. Deploying the app in testing
To deploy the app on TestFlight, we’ll add the plug-in “Apple App Store” to our Azure DevOps (available as an online or hosted version). This allows us to contact App Store Connect and TestFlight to upload our new versions of the app.
But beware: you need to manually upload a version of the application for the first time before you can use the pipeline!
We’ve just seen the 10 crucial steps in order to compile an iOS application from A to Z on Azure DevOps. Without a doubt, it’s extremely convenient and easy to use.
However, so far we have only used Azure DevOps completely within itself – the code is hosted on this platform and so is the pipeline.
Yet because of its relative ‘youth’ compared to AWS, very little source code is to be found directly on Azure DevOps.
Thus, in the next article of this series, we’ll see how to link Azure DevOps to another Git provider such as Bitbucket, GitHub or GitLab, in order to manage not only our source code but also all of the necessary triggers.