The easy way to push packages into HockeyApp from CI
As I mentioned in my last post, we use HockeyApp to distribute one of our internal mobile applications. As a platform, it provides many wonderful features including distribution, crash reports, feedback forms, analytics and team management. It’s also been acquired by Microsoft recently, which I’m sure will mean a lot of interesting things coming in the Windows 10 timeframe…
Our build and release pipeline is completely automated – just like they all should be! We have a very simple branching strategy (as we only ever have a couple of people working on it). We use pull requests to manage feature branches and anything that hits the master branch goes live. HockeyApp has a REST API that allows you to upload new versions quite easily.
We went through a couple of iterations before this process became easy though. At first we tried pushing packages using PowerShell. This didn’t work too well… unfortunately the API uses multiple form fields to accept the parameters and PowerShell’s built-in web commands aren’t the easiest to accomplish this with. So we wrote a C# console app to do it. This worked, but was a little nasty because we actually needed to compile and include it as an artefact for a later build to use.
We revisited this later and thought that if we are going to push all the packages into artefacts then we could just use curl from a Mac (or Linux) build agent. This made it really easy! We could now use three simple shell commands to push the packages to hockey app –
curl \ -F "status=2" \ -F "notify=1" \ -F "ipa=@dist/MyApp-%CiBuildNumber%.ipa" \ -H "X-HockeyAppToken: %HockeyAppToken%" \ https://rink.hockeyapp.net/api/2/apps/%iOSAppId%/app_versions/upload
curl \ -F "status=2" \ -F "notify=1" \ -F "ipa=@dist/com.mycompany.myapp-signed.apk" \ -H "X-HockeyAppToken: %HockeyAppToken%" \ https://rink.hockeyapp.net/api/2/apps/%AndroidAppId%/app_versions/upload
3. Windows Phone
curl \ -F "status=2" \ -F "notify=1" \ -F "ipa=@dist/MyApp.WP_Release_ARM.xap" \ -H "X-HockeyAppToken: %HockeyAppToken%" \ https://rink.hockeyapp.net/api/2/apps/%WPAppId%/app_versions/upload
They’re all the same right…? Yep – that’s the point. The only thing you need to remember is that the application IDs need to be the right one for your platform and you need to ensure you have the right package names.
The commands above have been parameterised so that you can store the application IDs and HockeyApp token as TeamCity parameters. This would be really handy if you wanted to say have multiple release channels (alpha/beta/RTW) by using a build template and setting the parameters on each configuration.
You may also notice the CiBuildNumber parameter in the iOS command. As this is a chained build we can pick up the Continuous Integration build’s number (which we’ve set up to be a version number – e.g. 22.214.171.124) and add it as a configuration parameter so that we can easily reference it in scripts.
And that’s it!
What to consider when doing Continuous Integration on a Xamarin app
OK, let me set the scene… It was a dark starry night at the local watering hole (READ: pub) and I was discussing one of our internal systems. We quickly identified that:
- none of us liked using the website on our phone
- we had an almost equal split between iOS, Android and Windows Phone users
- and several people wanted to learn Xamarin
So we did the only logical thing… spun up a very small team to build an app. While I could regale you with stories of building APIs and authenticating with Azure AD – I’ll leave that for another day and describe one of our larger challenges: building and packaging the app.
Aaron Powell has actually already written a blog post about how we handled versioning. This post will describe some of the challenges and considerations for process of building the app from source control and packaging it.
Considerations and Challenges
0 – A Build System
First off, let’s address the elephant in the room… We used TeamCity.
In the beginning, we weren’t really sure how much to structure our builds, whether we’d target Mac or Windows, etc. So we chose the most flexible system that we knew. As a consultant, I spend a lot of time answering questions with “it depends…” and that definitely is the answer when asked what build system to use.
So to those who ask why TeamCity over TFS/Jenkins/Bamboo/etc. My simply answer is –
- it can target OS X and Windows
- it’s flexible enough to incorporate more steps if needed and split out load over multiple chained configurations if needed; and
- I’ve used it a LOT so I’m comfortable with it.
1 – Target OS for Build Agents
Those who have used Xamarin will admit that it’s an easier setup when you can do everything on a Mac. However, we didn’t quite have that luxury. Our deciding factor here was simple – we need to compile a Windows Phone app. While we *could* (and actually did…) build for iOS on Mac and WP on Windows (either for Android), this would lead to several chained build configurations and as anyone in a couple-week skunkworks project would tell you –
So instead we chose to run our builds on a Windows VM that would utilise the Xamarin Build Host on its Mac host to build the iOS app.
2 – Distributing your app
Choosing decision is based on what channels you’ll use to distribute your app. In our case, we:
- Needed to push it out to all employees
- Didn’t want it on any stores
- Didn’t want to have to manage users’ device IDs
So we chose Enterprise provisioning for iOS and Windows Phone and a generated keystore for Android. This means we can use something like HockeyApp for distribution. This method of provisioning would also work well for distributing apps to testers before they are published to a store.
If you’re pushing your apps straight into a store then you’ll need to follow the guidelines for each platform’s provisioning.
Using an Enterprise Provisioning Profile means we can freely distribute our app through the organisation without the need for maintaining UUIDs (as with Ad-Hoc). When packaging your iOS app you can either –
- Sign into XCode as a user with access to the provisioning profile
- Install the certificate and provisioning profile manually
Check out Xamarin’s docs for a step-by-step guide on doing this.
If you don’t want to go via Google Play or Amazon Stores to get a signing key, you can generate your own keystore using –
keytool -genkey -v -keystore release.keystore
Store this in a safe place.
Windows Phone Provisioning
If you’re building a marketplace app, you can get your certificate by associating the project with the store app. This makes things very easy…
Using an Enterprise distribution profile meant we needed to distribute an AETX to our users in order to trust applications signed for our enterprise. We then need to sign the XAP (or APPX) file that is produced as a post build step. The trick here is to ensure that the manifests have the right author and publisher details in them.
XAP projects have a WMAppManifest.xml file in the Properties folder. In this file the App element should have the Author and Publisher attributes set to the CN of your certificate.
APPX projects have a Package.appxmanifest in their root. The Identity element’s Publisher attribute needs to match the certificate’s distinguished name and the Properties/PublisherDisplayName element should have the certificate’s CN as its value.
3 – Solution Build Configurations
They’re important. Follow the guides on the Xamarin docs for preparing iOS and Android for distribution carefully. They’re full of golden nuggets and are sure to get you through some tough times.
4 – Restoring Xamarin Components
Xamarin Components are fantastic! They’re just like commercialised NuGet packages.
However, getting them to work just like NuGet packages is a little bit tricky. There’s a command line tool available on https://components.xamarin.com/submit (direct download) that is used to package or restore components in a solution. To use this tool you need to authenticate with it. When you do, it drops a cookie called .xamarin-credentials in your home directory (~). It then uses that cookie for subsequent operations.
There are a couple of ways around this:
Store your cookie in source control and –
- Copy the cookie to the home directory before restoring components
- Add an environment variable pointing the COOKIE_JAR_PATH to your build process
- Run the command manually on the machine
We chose the source control + environment variable option because it seemed a little neater than littering my home directory. Looking back, there’s so much manual set up in any agent that I’m going to suggest running it manually on the agent.
We actually had a couple of iterations of build configuration. We started with a highly parallelised set of builds chained up the wazoo… and simplified it down to 2. The first compiled and packaged the applications and published them as artefacts on all branches. The second, which only ran for changesets on the master branch, grabbed the artefacts from the previous build and published them to HockeyApp.
Windows Phone signing also proved to be very difficult with the Enterprise profile. We originally used this because we didn’t want to be spinning up shadow accounts for each platform to get certificates, but signing and distributing a XAP/APPX manually is quite convoluted and tricky.
We learnt a lot from this project about Xamarin. By the end we had spent more time on infrastructure pieces (i.e. exposing APIs, build configurations, integrating third party components, etc) than actually putting the core functionality in. It was great fun and I wouldn’t change it for the world!