Create Xamarin.Forms Android App Bundle (aab) and release it to Google Play Store with DevOps

Update: Here's my other blog post where I share the YAML code for the below build and release pipeline

I've decided to update my DevOps CD pipeline for one of my applications to produce the aab package instead of the apk. Why you might ask? Mainly because I wanted to decrease the size of my app package, which in turn should potentially result in more downloads and/or less uninstalls.

Don't take my word for it though and watch this video by Google to learn about all the reasons behind switching to aab. If that's not enough for you, there's a great blog post by Jon Douglas, from Xamarin team, which also lists perks of switching to aab package format. Jon also walks the reader through the basics of setting up your project to work with this "new" package format.

At the time of writing this blog post I believe that there's no official guideline of how to create the aab package with DevOps, therefore I've decided to write a guide myself. There is however a great article by Dan Siegel, which I took my inspiration from.

Let's get started!

NOTE: I started writing this blog post around 10th of Jan but haven't fully finished it because of my laziness/lack of time. In the meantime James Montemagno beat me to it and published (15th of Jan) a really nice blog post about DevOps and aab packages. I apologise in advance for the repetition of some of the content.

Build Pipeline

Assuming that you already have a working apk build pipeline, we will need to make few changes in it, in order to generate the signed aab file (instead of the singed apk) and then make it available to our release pipeline.

Firstly, we disable the Signing and aligning APK task because it doesn't support signing aab packages yet. We do however need to provide signed version of the aab package when uploading to Google Play Store. In order to sign the aab package we will need to update the Xamarin.Android build step with some extra msbuild arguments.

Xamarin.Android DevOps build task
/t:SignAndroidPackage 
/p:AndroidKeyStore="True" 
/p:AndroidSigningKeyStore="$(KeyStore.secureFilePath)"
/p:AndroidSigningKeyPass="$(KeystorePassword)" 
/p:AndroidSigningKeyAlias="<keystore_alias>" 
/p:AndroidSigningStorePass="$(KeystorePassword)"
Xamarin.Android DevOps msbuild arguments

Significance of the above arguments can be found in the documentation, therefore I won't be explaining them in here. Nevertheless, I think it's worth mentioning what are the $(...) values for these arguments and where do we get them from.

  • $(KeystorePassword) - this is one of the secret variables defined in your pipeline (see below screen), which contains the *.keystore password.
KeystorePassword secret variable
Download Secure File DevOps task

Lastly, we will need to update the Copy Files task to copy the aab file instead of the apk to the be used later by the release pipeline. This is as simple as changing your Contents part of the task to a wildcard expression that will match any file with the aab extension: **/*.aab.

Copy Files DevOps task

NOTE: Don't forget to add the <AndroidPackageFormat>aab</AndroidPackageFormat> entry to your Android's project csproj file. Below is the Relese configuration for my project, for reference:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
    <AndroidLinkMode>Full</AndroidLinkMode>
    <AotAssemblies>false</AotAssemblies>
    <EnableLLVM>false</EnableLLVM>
    <BundleAssemblies>false</BundleAssemblies>
    <AndroidPackageFormat>aab</AndroidPackageFormat>
    <AndroidDexTool>d8</AndroidDexTool>
    <AndroidLinkTool>r8</AndroidLinkTool>
    <MandroidI18n />
    <AndroidSupportedAbis>armeabi-v7a;arm64-v8a</AndroidSupportedAbis>
    <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
  </PropertyGroup>

You should now have the build pipeline producing signed aab package.

Copying aab packages to the drop folder

Release Pipeline

The below instructions are written under the assumption that you either already have a release pipeline or you can create one by yourself.

Fastlane is your friend. That's what I've learned when trying to upload/publish the aab package to Google Play Store using DevOps release pipeline. We will use the supply command, which is very well documented. These docs will provide you with basic examples and get you started in no time.

As you can imagine our command won't be as simple as in the samples found in the documentation. Nevertheless, we won't add too many parameters to it. The final result will look like this:

fastlane supply --aab com.your_company_name.your_app_name-Signed.aab --json_key $(serviceAccountAuth.secureFilePath) --track beta --rollout 1.0 --package_name com.your_company_name.your_app_name --skip_upload_apk true
Fastlane supply command to publish aab to Google Play Store

Some of the parameters in the above command are self explanatory but let's go through all of them anyway:

  • --aab - it's the path to the package file that was copied to the drop location from the build pipeline
  • --json_key - the path to a JSON file containing auth information of the Google Play Service Account. More details about this parameter later in the blog post.
  • --track - the release track that your package will be uploaded to. By default it's production, therefore it is vital to provide this argument ( beta or alpha), to avoid pushing untested builds straight to the end users.
  • --rollout - it's a value between 0 - 1, which indicates the percentage of users that the new build will be rolled out to. In my case, I'm rolling out to 100% of my beta users.
  • --package_name - Your app package name.
  • --skip_upload_apk - We won't be uploading an apk file, therefore we're skipping this step.

In the DevOps the above command can be executed with the Bash task. In my case it looks like this:

DevOps Bash task executing Fastlane supply command

Google Play Service Account

Let's talk about the --json_key parameter in the supply Fastlane command and its value $(serviceAccountAuth.secureFilePath). In order to upload application package to Google Play Console we need to authenticate and have sufficient permissions to do so. Google handles this authentication/authorisation process with Service Accounts.

Fastlane docs to the rescue again! After completing the steps in this document you will create yourself a Service Account, which we will be used with our Fastlane supply command. Ensure that you save the JSON key file generated with one of the steps listed in that documentation. You will need to upload that file to Library -> Secure Files in DevOps. Below are the screenshots of my service account in google dev dashboards.

If you're done with creating your Service Account you should see similar in your dashboards:

Service Account in Google Developers Console APIs
Service Account in Google Play Console

When editing permissions for the service account, make sure that you select the correct ones. Always aim to select the minimal required permissions for a specific account. For instance, if you will only deploy apps to Beta track from DevOps, then this account doesn't need to have permissions to production releases. See my permissions configuration, for reference:

Custom Service Account permissions

Once you're done with setting up the Service Account, you will need to upload the generated along the way JSON key file to the Secure Files in the DevOps. Afterwards, in your release pipeline, create a Download Secure File task which will reference that newly uploaded secure file.

Download Google Play Service Account JSON auth file

The important thing to note from the above screenshot is the output variable serviceAccountAuth.secureFilePath, which will contain the path to the JSON key file, which will be used as a parameter in the supply command.

Congratulations! You should now have a Continues Delivery (i.e. CD) pipeline for you app.

Summary

It's been a bit of a hassle to work out all of the above by myself, without any proper documentation. Nevertheless, the feeling of accomplishment once it all comes together compensates for the frustrations on the way.

I'm really glad to see that at least a blog post from James Montemagno showed up, to guide Xamarin devs on how to deal with aab packages in DevOps. It feels though as there's still quite a bit to do in terms of documentation around this subject.