eXtreme Agile: Continuous Mobile Deployment with VSTS

By | February 2, 2016

If you’ve followed .Netitude for a while, you definitely know I’m a fan of Continuous Integration. But what about Continuous Deployment?

This is the first installment in a 3-part series on how you can embrace Continuous Deployment/Delivery as part of your Agile practices your mobile development environment. If you like this one and want to round out your solution, check out the sequels!

In the web world, Continuous Deployment (or Delivery) is the concept that every checkin to the repo is not only compiled and tested (with Unit Tests), but also deployed out to a live website (usually internal, of course) for instant real-world usage.

Given services like TestFlight, HockeyApp, etc can’t we do this with mobile?

Yes we can!

Visual Studio Team Services not only has a robust collection of extensions and tasks that you can put in to a build process (eg: run powershell scripts, etc) but also has a Release Management component that can stage and deploy releases based on whatever criteria you like.

For my scenario, I went full-bore on this thing and I’d like to show you how you could wow the pants off your stakeholders by doing the same thing.

Required Components

  • Visual Studio Team Services account & Project
  • HockeyApp account (not a free one, unfortunately, as you’ll be shipping 4 apps instead of the 2 allotted to the free account)
  • MacInCloud account (so you can build the iOS binary)
  • Xamarin account (Starter if you want to do this with a very simple app, else you’ll need to upgrade to something more substantial)

Let’s start with one of the easier end-to-end implementations of the 4. Believe it or not, this is going to be


Mostly because I didn’t really care too much about the versioning numbers being kicked out to HockeyApp, the Android CD setup proved to be really easy.

Step 1 – Set up the Continuous Integration build

Jump to Continuous Deployment setup
In Visual Studio Team Services, create a new Xamarin.Android build:

The beauty of the new VSTS system is you can choose to or no to use VSTS as your source control as well. If you don’t want to, throw it up on Github and you can still do your CI/CD using the instructions here. Or throw it on some other git repository somewhere else, or hell even use Subversion!
For my example I use VSTS as git source control, so that’s what you’ll be seeing screenshots for. The concepts follow, though.


4) This is one of the more important parts of the screenshot above. Without it you’ll have to do some tweaking after this build definition spins up.

5) If you’re building with a hosted agent (agent queue = Hosted), you will be using your allotted build minutes. If you want to avoid this, you can run the build agent on your own machine/VM, put it in the ‘Default’ queue (or another custom-named queue) and target that here.

After this step the build definition is created, but not yet saved to your project. Now’s the time to review the settings created by the wizard:


1) These tasks must be left in place or you won’t get your Xamarin solution to build because the Xamarin tools on the machine you’re targeting (especially in the Hosted queue) won’t have licenses applied to them. If you’re using your own box, however, you could of course have a license already applied on that machine and could possibly get rid of these tasks. Keep in mind, though, that you only have a limited number of active build machines for your Xamarin account. What I tend to do with these is tighten them as much as I can around the other tasks in the project that I might add/move around. You’ll notice how the deactivate happens *before* the publishing of the artifacts. That’s a good example.

2) These lines are, refreshingly, simple the username and password for your Xamarin account. They’re present in both the Activate and Deactivate tasks. Later I’ll show you how to protect these from prying eyes if you share your VSTS privileges with other folks.

3) This step can be removed if you’re not using Xamarin Test Cloud for your testing purposes (I’m not).

4) Notice this build methodology. It’s building each csproj – not the whole SLN as we’re used to in Microsoft land. For this reason, I recommend you put any Android-specific projects in the same subfolder (eg: “Android”). This would be things like your Android app & any “bridging” libraries. The other chunk of projects you’ll want to make sure you can target in this manner are any “common” or “portable” ones. A good idea for these is a “Portable” subfolder in the repo. The important thing to remember when we fab this flow up is that you don’t need the Xamarin license activated while you build Portable libs. So this (as I mentioned before) enables you to tighten that Activate/Deactivate loop in the build definition later.

Before we go any further let me show you how to protect that Xamarin account password:


Head over to the ‘Variables’ tab on the build definition, click ‘Add variable’ then type ‘xamarinpwd’ in for the variable name, and put your password in to the ‘Value’ field. Now here’s where it gets cool:

Clicking the little ‘lock’ icon (alt tag “Secret”) makes this variable value hidden from eyes, and irretrievable once the build definition is saved. This means even after you’re done, if another admin, or even YOU, comes back in to the build definition and tries to make that variable non-secret by clicking the icon again, it does not show the value. Rather, it clears it out. Now, we use this variable as our password by simply doing:

Head back to the ‘Build’ tab, go to the Xamarin Activate and Deactivate tasks, and put $(xamarinpwd) in the Password field.

Now we’re ready to rock.

Let’s assume you’ve got a repo set up like so:



Now we can see from this that we can build the Portable projects before everything else, of course – that’s the point of portable projects. The caveat here is we don’t want to build the IOS or Windows projects during the Android CI. So let’s tweak up our build definition for this situation. First, add a new build for our Portable projects:

So we move the Portable build to be the first step in the definition, outside the Xamarin license activation. We set up this VS Build as shown. Telling it to build all csproj files under the Portable folder. Build it for the ‘Android’ platform, and for the Configuration specified on the Variables tab (see above). This allows us to configure each of the portable libs for Android if necessary (think #if directives).

In line with our hypothetical (but highly advised) repo structure, we also need to change the target of the Xamarin.Android build:

If you’ve got unit tests you want to run, you can leave the MSTest task in place, otherwise feel free to axe it. Now your build definition’s should look something like this:

Got some nuget packages? Easiest way to cover that base is by adding the Nuget Package Restore task to the very top:

At this point, believe it or not, your Xamarin.Android project will do Continuous Integration builds on Microsoft’s Hosted build controllers!

Now let’s get this thing Continuously deploying.

Step 2 – Set up the Release Management definition

The critical part of our build definition as far as continuous deployment is concerned is the last one – Publish Build Artifacts:

What this little gem does for us is, after the APK has been signed, puts the resulting APK in to a folder named ‘drop’ on the Server. Once we’ve got this artifact on the server in a known place, our Deployment task can find it and do stuff with it. So let’s move on to that.

Head to the Releases tab and create a new Release:

Unfortunately, there’s no straight up Template to get us started so we start Empty and we’ll throw in the tasks we need from there.

Mind you, I’m not going to be showing off all the Deployment –> QA –> Production steps that you could do with RM, or the gating for approval capabilities either. Let’s just get this APK out to a HockeyApp instance, mmkay? Once we’ve added a new Release definition, it’s time to get it configured.

Give it a good name, then click the ‘Link to Build Definition’ link that’s staring you in the face:

3) Make sure you choose the Build Definition we built in the previous steps, then click ‘Link’.

Now it’s time to add some tasks to this definition. This part they make pretty easy. After clicking the ‘Add tasks’ button, choose the HockeyApp task to add:

Note that after you click ‘Add’ you’ve gotta click ‘Close’.

All we’ve gotta do now, is wire up our HockeyApp account:

1) Manage your HockeyApp service connections by clicking the ‘Manage’ link, then putting in your API Token from your HockeyApp Profile page and giving it a name you want to show up in this drop down. Once you’ve got the connection created in VSTS, come back here and click that ‘Refresh’ button then pick the connection name you specified.

2) Put in the App ID for the App you’re deploying to in HockeyApp

3) For the binary file path, click the ‘…’ button to give you a head start, then finish it off with ‘*.apk’ (because know we’ll want only the APK file sent over to HockeyApp). It should look something like this when you’re done:

Now we’ve got the Release Managemen definition wired up, we just need to tell it to happen with every CI. We do this like so:

1) Head to the ‘Triggers’ area of our definition

2) Check the ‘Continuous deployment’ box

3) Choose the build definition we created earlier

4) Send it to the ‘Default Environment’ already configured in this Release definition

Click ‘Save’, check in some code, and watch the magic work!

One thing I wish VSTS would improve upon would be the ability to trigger a CI based on not just a check in to a repo, but a certain point in the repo. In a case like this we’d ideally do the CI only when any code under Portable or Android changed. Unfortunately this whole flow is going to execute if you change the XAML in your Windows head :( There would be ways to do this with separate repos (and including those in a main repo as a Git subrepo) but it’s a lot more overhead than I feel it’s worth.

Next time we’ll have a look at doing CI & CD with Windows Mobile and Big Windows. Stay tuned!