Xamarin DevOps with VSTS - Versioning Apps For HockeyApp

This post continues the series on Visual Studio Team Services and shows how we can version Xamarin Android and iOS applications in the VSTS Build Definition. This then provides a build system with the versioned applications suitable for deployment into HockeyApp.

To keep the post a reasonable length I will detail the deployment to HockeyApp in a seperate post.

Please note I updated this post on 28th May 2016 to reformat the Regex values input into the Version Assemblies build task's Build Regex Pattern field as they were not displaying correctly.

This post was updated to add the section Updating the minor version on 15th June 2016 following questions asked by Matthew Belk and Nicolas in the comments

Resources

Key Points

  • We can use the versioning of the apps described in this post with a HockeyApp deployment.

  • The build number format for the Xamarin Android and iOS builds is different

  • We use the Version Assemblies Build Task obtained from the VSTS marketplace.

  • The Version Assemblies Build Task can also be used for shared assemblies such as Portable Class Libraries. In this case it modifies the AssemblyInfo.cs file.

  • Thanks to Gerald who noticed that the screenshots and regex values didn't originally match - they were correct in the markdown editor but the blog formatting was modifying the values incorrectly!

Install Colin's ALM Corner Build & Release Tools

To start off we can leverage the excellent work of Colin Dembovsky and install his VSTS extension. It's also on Github Colin's ALM Corner Build Tasks - Version Assemblies. The extension also provides the Replace Tokens Build Step which would be really useful:-

It replaces tokens in a file using Environment variables defined in a Release Definition.

Locate the VSTS Marketplace entry for Colin's ALM Corner Build & Release Tools.

Click the Install button. The dialog below is displayed.

Confirm the correct VSTS site is selected and click the Continue button.

Click the Confirm button.

Click the Close button. We have now installed Colin's ALM Corner Build & Release Tools extension and are ready to modify the existing Xamarin.Android and Xamarin.iOS Builds to add the Version Assemblies Build Task.

Versioning an Android app

This section builds upon the VSTS Build Definitons etc setup in the post Setup Android CI Builds.

The instructions for using the Version Assemblies Build Task with Android and .Net assemblies using AssemblyInfo.cs can be found on GitHub.

Essentially as part of the build, before building the actual application we will be modifying the VersionCode value to match the build number. Version Your App defines the VersionCode as

An integer value that represents the version of the application code, relative to other versions. The value is an integer so that other applications can programmatically evaluate it, for example to check an upgrade or downgrade relationship. You can set the value to any integer you want, however you should make sure that each successive release of your application uses a greater value. The system does not enforce this behavior, but increasing the value with successive releases is normative.
Typically, you would release the first version of your application with versionCode set to 1, then monotonically increase the value with each release, regardless whether the release constitutes a major or minor release. This means that the android:versionCode value does not necessarily have a strong resemblance to the application release version that is visible to the user (see android:versionName, below). Applications and publishing services should not display this version value to users.

The steps below will support for a deployment to HockeyApp such that the versions of the application will match the builds, below is an example of the HockeyApp dashboard for the Android app:-

The following steps show how to setup a build step which will modify the VersionCode defined in the AndroidManifest.xml with each build. This will work well in HockeyApp so that the 'Code' column will increment in line with each build so we can identify different versions of the application.

The AndroidManifest.xml should be defined something like:-

    <manifest xmlns:android="http://schemas.android.com/apk/res/ android"android:versionCode="1" android:versionName="1.0" 

The AndroidManifest.xml is located in the Android application project's Properties folder. Double click the file to open it in the editor.

Click the Source tab at the bottom of the page to view the file in XML format:-

The next step is to edit the Build number format to something suitable. Click on the VSTS 'Build' page. Right click on the HelloDevOps Android CI buid definiton.

Click the Edit option.

Next click the General tab.

Edit the Build number format field value to

1.0.0$(rev:.r)

The Build number format is explained at Specify general build definition settings. The'$(Rev:.r) ensures that each build has a unique name. If nothing else in the build number changes then this integer value is incremented by one. Note we are using a different format between the Android and iOS build definitions due to the differences in the platforms.

Now we can add the Version Assemblies Build Task to the build definition. Click the Build tab. Next click the + Add build step… button. The Add tasks dialog is displayed:-

Select the Build category, scroll down and then click the Add button on the Version Assemblies item. Click the Close button:-

Select the Version Assemblies build step and drag it to sit before the first build step compiles the application, in this case Xamarin.Android.

Use the ... button to the right of the Source Path field to navigate to the Properties folder of the Android application project.

Edit the File Pattern field value to AndroidManifest.xml

Edit the Build Regex Pattern field value to:-

(?:\d+\.\d+\.\d+\.)(\d+)

Expand the Advanced panel and edit the Build Regex Group Index field value to 1.

Edit the Regex Replace Pattern field value to versionCode="\d+

Edit the Prefix for Replacements field value to versionCode="

Click the Save button and then click the OK button on the Save dialog.

Now we can test the build by clicking the Queue build... button. The Queue Build dialog is displayed:-

Now click the OK button to start the build. Once the Build has completed we can scroll back up in the build output window to see if the Version Assemblies task ran ok. As can be seen below it ran ok:-

Now by clicking on the Logs tab we can inspect the output of the Version Assemblies task in more detail and observe that a match was found and the version changed:-

If we now click on the Build name and navigate back to the list of builds we can see that the build Name has changed to 1.0.0.1:-

Verify the Android application was correctly versioned

In order to verify the Version Assemblies worked correctly we can download the build artifacts and inspect the application package .apk file. Todo so we first double click on the build name and then click the Artifacts tab:-

Now click the Download link. I find it easiest to copy the .apk file out of the drop.zip file and into the Documents folder since I have far less files there.

Now to verify the application package is versioned correctly we can use the Android Sdk tools. Note by selecting the Open AndroidSDK Manager.. option on the Xamarin Studio Tools menu we can see the installation path of the Android SDK.

Open a Terminal window and navigate to the build-tools folder of the SDK. Then navigate to the version folder within this. For example the path on my Mac is /Users/richardwoollcott/Library/Developer/Xamarin/android-sdk-macosx/build-tools/23.0.1.

Now execute the command

./aapt dump badging /Users/richardwoollcott/Documents/com.intuitive.hellodevops.apk

As can be seen below the VersionCode is correctly set:-

It's probably best to queue a second build and verify that the VersionCode has changed to 2:-

Versioning an iOS app

This section builds upon the VSTS Build Definitons etc setup in the post Setup iOS CI Builds With MacinCloud.

I could not find much useful documentation for using Version Assemblies with an Xamarin.iOS application. However I noted that Thomas Dohmke used it in his Xamarin Evolve talk Manage Xamarin apps with HockeyApp and Visual Studio Team Services so I tweeted him and he replied very quickly - massive thanks Thomas!

Versioning the iOS app ready for HockeyApp requires changing the data defined in the Info.plist file. This file contains the configuration information for the application.

The steps below will support for a deployment to HockeyApp such that the versions of the application will match the builds, below is an example of the HockeyApp dashboard for the iOS app:-

The Apple documentation Bundle Structures provides information about this.

Essentially as part of the build, before building the actual application we will be modifying the CFBundleVersion value to match the build number. Bundle Structures defines the CFBundleVersion as

The bundle version string specifies the build version number of the bundle. This value is a monotonically increased string, comprised of one or more period-separated integers. This value cannot be localized.

The current default values can be seen below:-

We need to edit the Build field value to 1.0.0:-

Note that we can also click the Source tab to view the file in a Property List view. In this view it is the Bundle version property:-

If we now open the Info.plist in a text editor we can see that the actual value we are changing is the CFBundleVersion.

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">  
<plist version="1.0">  
<dict>  
    <key>CFBundleName</key>
    <string>HelloDevOps</string>
    <key>CFBundleIdentifier</key>
    <string>com.intuitive.hellodevops</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1.0.0</string>

The next step is to edit the Build number format to something suitable. Click on the VSTS Build page. Right click on the HelloDevOps iOS CI buid definiton.

Click the Edit option.

Next click the General tab.

Edit the Build number format field value to

1.0$(rev:.r)

The Build number format is explained at Specify general build definition settings. The $(Rev:.r) ensures that each build has a unique name. If nothing else in the build number changes then this integer value is incremented by one. Note we are using a different format between the Android and iOS build definitions due to the differences in the platforms.

Now we can add the Version Assemblies Build Task to the build definition. Click the Build tab. Next click the + Add build step… button. The Add tasks dialog is displayed:-

Select the Build category, scroll down and then click the Add button on the Version Assemblies item. Click the Close button:-

Select the Version Assemblies Build Step and drag it to sit before the first build step compiles the application, in this case Xamarin.iOS.

Use the ... button to the right of the Source Path field to navigate to the Properties folder of the iOS application project.

Edit the File Pattern field value to /Info.plist

Edit the Build Regex Pattern field value to:-

(?:\d+\.\d+\.)(\d+)

Expand the Advanced panel and confirm the Build Regex Group Index field value to 0.

Confirm the Regex Replace Pattern and Prefix for Replacements field values are empty.

Click the Save button and then click the OK button on the Save dialog.

Now we can test the build by clicking the Queue build... button. The Queue Build dialog is displayed:-

Now click the OK button to start the build. Once the Build has completed we can scroll back up in the build output window to see if the Version Assemblies task ran ok. As can be seen below it ran ok:-

The log file in this instance doesn't contain any extra information over that in the build output.

If we now click on the Build name and navigate back to the list of builds we can see that the build Name has changed to 1.0.1:-

Update 3rd June 2016:- When writing the Deploying To HockeyApp post I found there was a problem with the build numbers initially. When I looked into this I found it was because all of the versions of the ipa file were available in the folder so the first version of the ipa was always being deployed to HockeyApp.

To fix this problem I edited the Build Definition via the Repository tab and set the Clean field to true:-

Verify the iOS application was correctly versioned

In order to verify the Version Assemblies worked correctly we can download the build artifacts and inspect the application package .apk file. Todo so we first double click on the build name and then click the Artifacts tab:-

Now click the Download link. Opening the drop.zip file we can see that it correctly contains the file named HelloDevOps.iOS-1.0.1.ipa.

We can examine the ipa contents by copying it and renaming it HelloDevOps.iOS-1.0.1.zip. A confirmation dialog is displayed:-

Click Use .zip button. Now we can double click the zip file and a folder called Payload will be extracted. This contains a file HelloDevOps.iOS.app file:-

Right click the HelloDevOps.iOS.app file:-

Choose the Show Package Contents option and we can see the contents of the .app:-

Notice there is an Info.plist file. Right click this and choose the Open With-> XCode.app option:-

The Info.plist is displayed in a property list dialog:-

We can now verify that the Bundle version property has a value of 1.0.1.

This concludes the steps required to version Xamarin Android and iOS apps in VSTS build definitions. The Version Assemblies build task is configurable and uses Regular Expressions so other build number formats could be experimented with as required.

Updating the minor version

I've added this section after receiving comments from Matthew Belk and Nicolas.

Nicolas's comment can be seen below:-

Great post! Good information! But I was wondering, what if you change the major or minor number of your project (for example in the AndroidManifest) then your buildnumber is still fixed with the 1.0.0.X?

In this section we will experiment with editing the Build number format on the General tab of the Build Definition so that it is incremented in line with the minor version.

Matthew was querying the method used for the Android Version Code. Matthew's comment can be seen below:-

Loving this series, but I think that this system for updating the Android "versionCode" value will eventually break when the pattern in the build number changes, no? since the VSTS documentation for the $(rev:.r) says that it will only increment when no other elements change. Was it your intention that the actual application versions would still be maintained in the project files?

To answer the last part first - Yes the actual application version is defined in the AndroidManifest and the Info.plist.

As for the Android Version Code, I initially setup the Build Number Format following the example for Android in the GitHub page for Version Assemblies which uses 1.0.0$(rev:.r).

We will run through the scenario that we are changing the minor version, from version 1.0.0 to 1.1.0. We will increment the minor version for both Android and iOS apps to check everything's ok.

To start off with we need to edit the AndroidManifest.xml and edit the Version name from 1.0 to 1.1:-

Next we edit the Info.plist Version from 1.0 to 1.1 and the Build from 1.0.0 to 1.1.0:-

Next we can edit the Build number format on the General tab of the Build Definiton. For the HelloDevOps Android Release build definition we edit this from 1.0.0$(rev:.r) to 1.1.0$(rev:.r):-

And for the iOS build we edit the Build number format from 1.0$(rev:.r) to 1.1$(rev:.r):-

When running builds we get the following results for Android:-

And for iOS:-

Moving to the HockeyApp dashboard we see that the Android 1.1 builds are listed but that HockeyApp thinks that the latest version (Latest Downloadable version) is still the 1.0 version since the VersionCode is higher:-

The iOS builds don't have the same problem in HockeyApp and the version 1.1 is shown as the latest version:-

Looking at the documentation Specify general build definition settings we can see that there is a BuildID token which provides an unique Id for each build:-

Since this post Kevin Ford posted a great article Narrow Winding Roadway: Setting Up Builds With Xamarin Using MacInCloud and VSTS where he uses this BuildID I would reccommend a read of this.

So we can edit the Android and iOS Build number format to 1.1.$(BuildID), first Android:-

And iOS:-

The Android Version Assemblies build task aslo needs changing to accomodate this build number format. Edit the Build Regex Pattern from:-

(?:\d+\.\d+\.\d+\.)(\d+)

to:-

(?:\d+\.\d+\.)(\d+)

We can now see the builds for Android use the BuildID:-

And the iOS builds:-

Moving to HockeyApp we can see that the latest Android version is shown as 1.1:-

And for iOS:-

So in conclusion we can see that the iOS and Android Build Numbers are now unique which fixes the problem we saw in HockeyApp with the Android app using the original scheme. Additionally the format of the build names is now the same so this is an improvement.

Richard Woollcott

Read more posts by this author.

Taunton, United Kingdom