One of the things I find frustrating is that for every new project we seem to have to recreate the build and release pipeline. In each case we step through the same steps, run into the same, albeit familiar, issues and end up with a pipeline that looks incredibly similar to the pipeline we setup for the last project we started. In this post I’m going to share the first set of Azure DevOps build templates that you can reuse in order to build your Xamarin Forms for iOS, Android and Windows.
This started out as an experiment to consolidate three different build pipelines into a single build pipeline. Often for mobile applications developers resort to setting up a build pipeline for each target platform, and then potentially each target environment. For us this because untenable as we were working on a white-labelled product that would need to have a build pipeline for each offering, which would grow over time.
The first step was to leverage the ability in Azure DevOps to create multi-stage pipelines. I setup a different stage for each target platform. You might be asking, why did I set them up as different stages; couldn’t I have just created additional tasks, or perhaps even different jobs for the different platforms. Well, the nice thing about different stages is that they can use different build agents. This is of course an absolute must, since iOS needs to be built by a Mac agent, whilst Windows (ie UWP) needs to be built by a Windows build agent. I could have cheated and wrapped the Android build with either the iOS or Windows build to cut down on the build time but you’ll see in a bit why I kept them separate.
In addition, my initial goal for this experiment was to have a standard build template that we could use and that I could share with the community via my blog. However, I felt this was only a part solution – there are so many great posts out there on how to setup a build process for XYZ but they become stale the minute they’re published. What if we could create a set of templates that could be released, much like a product, and could evolve over time.
In this post I’m introducing the Pipeline Templates github repository where I’ll be evolving build templates to help developers avoid the complexity of setting up the entire Azure DevOps, instead, focusing on building amazing applications. It’s still early days but check out the repository, watch and provide feedback so that we can evolve these templates as a community.
We’ll be starting with three templates that can be used to build a Xamarin.Forms application for iOS, Android and Windows. If we look at the basic structure of the repository, you’ll see that there’s a folder for Azure and a sub-folder for Mobile. The rationale was that over time this repository could house templates for other build platforms.
I also wanted to separate mobile app builds away from builds for the web or cloud services. However, the more I think about this the more I feel this separation is somewhat arbitrary because web apps built using SPA frameworks like React, Angular, Vue etc are similar in so many ways to mobile or desktop apps. I can imagine this structure will evolve over time – if you’re going to reference the templates in this repository, make sure you use the tags to ensure your build process doesn’t change or break as the templates evolve (more on this later).
Creating the Azure Build Pipeline
In order to use the pipeline templates you need to reference the GitHub repository as a resource within your build pipeline. I’ll walk through creating a new build pipeline but if you can add resources to an existing Yaml based pipeline using similar steps.
Start from the Pipelines tab in the Azure DevOps web portal and click the New Pipeline button (top right corner of the screen). Follow the steps to select your source code repository and pick a New pipeline template to use (I typically go with the Starter pipeline but either way we’re going to replace most of the yaml anyhow).
When you’re on the review tab, I suggest that you rename the yaml file. It’s not immediately obvious that you can change the name of the yaml file. The following image shows the default name on the left and then if you tap on the azure-pipelines.yml text you can actually edit it. On the right side of the image is the new filename and I’ve placed it in a pipelines folder to keep everything tidy in the repository.
Rather than making changes at the review tab, I just hit save (not the default “save and run” since we know the pipeline isn’t setup correctly).
Connecting to a GitHub Resource
According to the documentation you should be able to just add a GitHub resource directly to the Yaml file. However, in doing this I found that my pipeline didn’t work until I created an endpoint, similar to if you were to add other authenticated repositories as a resource.
Creating an endpoint is actually quite simple but you may well need additional permissions on your repository, depending on what access permissions you have. Click on Project Settings, in the bottom left corner of the Azure DevOps web portal, then click on the Service Connections tab. Click on the New service connection button, that’s in the top right corner of the Service connections screen. Select the GitHub connection type and then populate the New GitHub service connection flyout.
Once you click the Authorize button, you’ll most likely need to adjust the Service connection name – you’ll need this value in the next step.
The next step is to go back to your newly created pipeline (Note: if it doesn’t appear when you click on the Pipelines tab, you’ll need to switch view to All instead of Recent since you haven’t run your pipeline yet). Once you have the pipeline open, click Edit to bring up the yaml editor.
Replace the contents of the yaml file with the following:
resources: repositories: - repository: builttoroam_templates type: github name: builttoroam/pipeline_templates ref: refs/tags/v0.1.0 endpoint: Pipeline-Templates
This block of yaml references the pipeline_templates repository that’s owned by builttoroam. It’s going to use the Pipeline-Templates service connection to access the repository where it’s going to look at tag v0.1.0. In the pipeline this resource can be referenced as builttoroam_templates, which is just a local name assigned to this resource by the pipeline.
Important: that you specify the ref attribute and specify a tag. If you don’t, you’ll be pointing to the latest available templates on master. Referencing master is fine as you setup your pipeline but you should change to referencing a tag release to make sure that as we change the repository, your build continues to work.
Xamarin.Forms Build Pipeline
Now that we’ve added a reference to the pipeline templates repository, we can make use of any of the templates. For example the following yaml references the ios Xamarin template.
stages: - template: azure/mobile/build-xamarin-ios.yml@builttoroam_templates parameters: solution_filename: 'src/MyApp.sln' solution_build_configuration: 'Release'
In this case to access the template in the pipeline templates repository, we firstly need to use the @[local resource name] syntax and then secondly we need to provide the path to the yml file (ie azure/mobile).
This yaml snippet defines the stages for the build pipeline, including a stage that’s defined in the referenced template file (in this case build-xamarin-ios.yml). You can add additional stages by simply repeating the -template section and adjusting the name of the template.
This code snippet shows just the first two parameters being passed in. At the end of this post is a full example showing the required parameters. There are some additional parameters that can be used to adjust output file name and folder, as well as for adding custom steps to the beginning, pre and post build and at the end of the stage.
The following is a fully worked example which includes iOS, Android and Windows. Note that there are a number of variables that are being passed into the templates. These are all defined within the Inspector XF Build Variables group, which can be defined via the Library tab in Azure DevOps.
resources: repositories: - repository: builttoroam_templates type: github name: builttoroam/pipeline_templates ref: refs/tags/v0.1.0 endpoint: Pipeline-Templates variables: - group: 'Inspector XF Build Variables' stages: - template: azure/mobile/build-xamarin-ios.yml@builttoroam_templates parameters: solution_filename: 'src/Apps/DotNet/Inspector.XF.sln' solution_build_configuration: 'Release' ios_plist_filename: 'src/Apps/DotNet/XF/InspectorXF/InspectorXF.iOS/Info.plist' ios_cert_password: '$(ios_signing_certificate_password)' ios_cert_securefiles_filename: '$(ios_signing_certificate_securefiles_filename)' ios_provisioning_profile_securefiles_filename: '$(ios_provisioning_profile_securefiles_filename)' build_number: '$(Build.BuildId)' full_version_number: '$(version_prefix).$(Build.BuildId)' ios_signing_identity: '$(ios_signing_identity)' ios_provisioning_profile_id: '$(ios_provisioning_profile_id)' - template: azure/mobile/build-xamarin-android.yml@builttoroam_templates parameters: build_android: $(android_enabled) solution_filename: 'src/Apps/DotNet/Inspector.XF.sln' solution_build_configuration: 'Release' android_manifest_filename: 'src/Apps/DotNet/XF/InspectorXF/InspectorXF.Android/Properties/AndroidManifest.xml' build_number: '$(Build.BuildId)' full_version_number: '$(version_prefix).$(Build.BuildId)' secure_file_keystore_filename: '$(android_keystore_filename)' keystore_alias: '$(android_keystore_alias)' keystore_password: '$(android_keystore_password)' - template: azure/mobile/build-xamarin-windows.yml@builttoroam_templates parameters: solution_filename: 'src/Apps/DotNet/Inspector.XF.sln' solution_build_configuration: 'Release' uwpBuildPlatform: '$(uwpBuildPlatform)' windows_package_manifest_filename: 'src/Apps/DotNet/XF/InspectorXF/InspectorXF.UWP/Package.appxmanifest' build_number: '$(Build.BuildId)' full_version_number: '$(version_prefix).$(Build.BuildId)' windows_cert_securefiles_filename: '$(windows_signing_certificate_securefiles_filename)' windows_cert_password: '$(windows_signing_certificate_password)'
There are also some secure files that have been added to the Library, which again are referenced here using a variable defined in the variable group. The process for adding secure files is that you upload the file, give it a friendly name and then assign that friendly name to a variable that’s in the variable group.
In this post I’ve given you a very quick introduction to the Pipeline Templates repository. Over the coming posts I’ll walk through some of the templates and what you can do with them in more detail. I’d love feedback on the templates – raise an issue to suggest changes on the GitHub repository and I’ll see if I can encorporate them.