Integrating Your CI/CD With AppCenter
by Gerade Geldenhuys
Recently I was tasked with automating the build and deployment process for the in-house mobile app that we develop for our client. We have a dedicated build box with GitLab runner set up to handle our CI needs. We decided to use Microsoft’s AppCenter to handle the building and distribution of our mobile app, but having to manually start builds to kick off the process is not really doing DevOps, is it?
To overcome this problem, I did a little research and came across the AppCenter CLI. This tool was exactly what we needed to enable communication between our build box and AppCenter to initialize the process of building and deploying our app to the testers or even the Play Store.
Starting a build
The AppCenter CLI is pretty comprehensive with one drawback. On AppCenter you have to configure a build for a branch. This means setting the build configuration (Debug or Release), the version of Xamarin to use when building your app and a couple of other options like deploying to the Play Store and whether you want to build this branch on every push.
With that being said, it’s really not that much of an effort to configure an app on AppCenter. Once it’s been configured you can use the appcenter build queue
command to queue a new build.
appcenter build queue --app 'appName' -b 'branchName'
That’s it, through just that one command you can have your app built and deployed to your testers/Play Store in minutes, this all by only pushing to your repository.
Distribution
Once our app has been successfully built, we want to distribute it to notify our testers that a new version is available. Not just that, when we merge and build a new app on the Main branch we want to automatically deploy it to the Play Store.
For this, we can turn to the appcenter distribute releases add-destination
command.
appcenter distribute releases add-destination -d 'Collaborators' -t 'group' -r 'releaseId' -a 'appName'
In the command above we are requesting AppCenter to notify all the users in the Collaborators group of a new release that was just built for our app by specifying the ReleaseId.
Our testers will then get an email with a link to install/update the app.
Keep me informed
This is all well and neat, but for my team, I didn’t want us to move between two different applications when building our projects. We use GitLab, and I want it to be our source of truth for everything CI related.
Once a build has been queued, I don’t want us to go to AppCenter to monitor the status of our builds, this is where I want to stay in GitLab and still know of these things without having to open a new tab.
To accomplish this, we can use the appcenter build branches show -a 'appName' -b 'branchName'
command to get the latest build status of a branch.
With this command, you can periodically ask AppCenter for the status of a build and output the result on GitLab whether the build is still in progress, succeeded or failed.
I did all this in PowerShell, below is a sample of said script that you can use in your CI setup, whether it be GitLab, GitHub, Azure DevOps, etc.
# $global:apiToken = "APP-TOKEN-GENERATED-BY-YOUR-APPCENTER-ACCOUNT" | |
# $global:appName = "THE NAME OF THE APP IN APPCENTER (You can find this in the URL of the app. eg: account/app-name)" | |
# $global:branchName = "THE BRANCH YOU HAVE CONFIGURED TO BUILD (eg feature/addAuth)" | |
function Login(){ | |
appcenter login --token $global:apiToken | |
} | |
try{ | |
## Login to AppCenter | |
Login | |
## Get configured branches | |
$configuredBranches = appcenter build branches list -a $global:appName --output json | ConvertFrom-Json | |
## Only branches configured on AppCenter can be built | |
## If branch is not configured, exit with a failure | |
if(($configuredBranches | Where-Object { $_.sourceBranch -eq "$global:branchName" } | Measure-Object).Count -eq 0){ | |
Write-Host "The branch $global:branchName has not been configured yet. Please do so on Appcenter." -ForegroundColor Red | |
Write-Host "The branch $global:branchName has not been configured yet. If the branch has been configured, manually start the first build for the CLI to pick it up." -ForegroundColor Red | |
$Host.SetShouldExit(1) | |
exit 1 | |
} | |
else{ | |
## Kick off a new build | |
appcenter build queue --app $global:appName -b $global:branchName | |
Write-Host "Build has been queued on AppCenter" | |
$buildComplete = $false; | |
## WHILE BUILD IS IN PROGRESS | |
while ($buildComplete -eq $false) { | |
## Get the latest status for the build that was just queued | |
$buildStatus = appcenter build branches show -a $global:appName -b $global:branchName --output json | ConvertFrom-Json | |
## If the build status is completed | |
if($buildStatus.status -eq "completed"){ | |
## Build has completed | |
if($buildStatus.result -eq "succeeded"){ | |
Write-Host "Build has complete and the app is being processed on the Playstore" | |
## If this is a UAT build, notify collaborators of new build | |
if($global:appName -eq "geradebbd.co.za/rtc-mobile-uat"){ | |
## Get all releases | |
$releases = appcenter distribute releases list -a $global:appName --output json | ConvertFrom-Json | |
## Get the release for this build | |
$builRelease = $releases | Where-Object { $_.version -eq "$($buildStatus.buildNumber)" } | |
## If there was a release for this build, notify collaborators | |
if(!($null -eq $builRelease)){ | |
Write-Host "Distributing and notifying collaborators of new build available for testing" | |
appcenter distribute releases add-destination -d "Collaborators" -t "group" -r $builRelease.id -a $global:appName | |
} | |
else{ | |
Write-Host "No release for build. Collaborators will not be notified." | |
} | |
} | |
} | |
elseif($buildStatus.result -eq "failed"){ | |
## If the build failed on AppCenter, exit CI job with a failure | |
Write-Host "Build failed on AppCenter. Please login to AppCenter to view the logs." | |
$Host.SetShouldExit(1) | |
exit 1 | |
} | |
elseif($buildStatus.result -eq "canceled"){ | |
##Build was canceled on AppCenter | |
Write-Host "Build was canceled on AppCenter." | |
} | |
## Break out of while loop | |
$buildComplete = $true | |
} | |
else{ | |
## Notify CI logs that build is still in progress | |
Write-Host "Build is still in progress" | |
## sleep for 15 seconds before checking for status again | |
Start-Sleep -Seconds 15 | |
} | |
} | |
} | |
}catch { | |
Write-Host $_.Exception | |
$Host.SetShouldExit(1) | |
exit 1 | |
} |
Summary
I hope this script can help your team as much as it did mine because just thinking of the task of installing the Android SDK and configuring it to build our app on our build machine was daunting in itself and that’s not even taking into account the effort it would take to have it notify our testers or deploying to the Play Store.