Azure Devops pipeline for multi product flavors Android App
Azure Devops pipeline for android app using multi dimensions and multiple product flavors
Introducing Azure DevOps
The idea is to automate this process using a really good tool: Azure DevOps, this process is called continuous integration (continuous delivery / deployment).
This project is a simple example of how to implement parallel jobs in Azure Devops to support an arbitrary number of Gradle flavors and build variants of an Android app. It also shows how we would flavorize both sensitive and non-sensitive variant-specific properties.
In this example, I will use 2 flavor dimensions,
product
,environment
.
Where product
is for a product/company/brand/client name and environment
is Dev/QA/UAT/PROD etc
flavorDimensions "product", "environment"
productFlavors {
// Environments
production {
dimension "environment"
buildConfigField "String", "API_URL", '"https://"'
buildConfigField "String", 'environment', '"production"'
}
preprod {
dimension "environment"
applicationIdSuffix ".uat"
versionNameSuffix " - UAT"
buildConfigField "String", "API_URL", '"https://"'
buildConfigField "String", 'environment', '"staging"'
}
quality {
dimension "environment"
applicationIdSuffix ".qa"
versionNameSuffix " - QA"
buildConfigField "String", "API_URL", '"https://"'
buildConfigField "String", 'environment', '"quality"'
}
dev {
dimension "environment"
applicationIdSuffix ".dev"
versionNameSuffix " - DEV"
buildConfigField "String", "API_URL", '"https://"'
buildConfigField "String", 'environment', '"dev"'
}
// PRODUCTS >>>>
productA {
dimension "product"
applicationId "com.test.producta"
versionCode 1
versionName "1.1"
}
productB {
dimension "product"
applicationId "com.test.productb"
versionCode 2
versionName "2.2"
}
productC {
dimension "product"
applicationId "com.test.productc"
versionCode 3
versionName "3.3"
}
}
By default gradle assemble
command will create builds for all products with all environments and buildTypes.
To overcome we will specify to create build only for particular flavor/product likeassembleProductA
this will create all builds for productA with all environments and buildTypes.
1. ProductADevDebug.apk
2. ProductADevRelease.apk
3. ProductAQualityDebug.apk
4. ProductAQualityRelease.apk
5. ProductAPreprodDebug.apk
6. ProductAPreprodRelease.apk
7. ProductAProductionDebug.apk
8. ProductAProductionRelease.apk
Still we have debug apks that we don’t require in azure-pipeline, to overcome this, we will use build-variants variantFilter
flag in build.gradle file to skip debug builds to optimize processing and reduce compilation time for azure pipeline.
Add below code in build.gradle
file to skip debug builds from pipeline.
variantFilter { variant ->
if(variant.buildType.name == 'debug' || variant.buildType.name == 'Debug') {
setIgnore(true)
}
}
after running assembleProductA
command you will see only following builds.
1. ProductADevRelease.apk
2. ProductAQualityRelease.apk
3. ProductAPreprodRelease.apk
4. ProductAProductionRelease.apk
Even you can change apk file name by adding below code in gradle file, this will append VersionName & VersionCode in apk file name.
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = variant.name
outputFileName += "-v" + variant.versionName
outputFileName += "-" + variant.versionCode
outputFileName += ".apk"
}
}
Now Create a new Pipeline in Dev Ops to run the project
In Azure Devops, go to Pipelines -> New Pipeline -> Select your VCS -> select the repo -> Existing Azure Pipelines YAML file -> Branch = master or any other branch
, Path = azure_devops/azure-pipelines.yml
-> Continue -> Run.
Azure DevOps will automatically create a new file at the root of your project folder called azure-pipelines.yml
, you will need to commit this file to your code repository. It contains the job definition for your project defined using yaml
, it will be interpreted by Azure DevOps to know how to build and sign your application.
What is interesting with this sort of job definition is :
- You keep your build jobs with your source code using the
azure-pipelines.yml
yaml
is easy to read and maintain- If you migrate to another Azure account you keep your jobs configurations
Default configuration for gradlew
is to assemble, it will assemble all builds for all flavors, we will modify as per requirement and add assembleProductA
in Tasks in gradlew
configuration as shown below
And gradlew YAML will look like
steps:
- task: Gradle@3
displayName: 'gradlew assembleProductA'
inputs:
gradleWrapperFile: '$(Parameters.wrapperScript)'
workingDirectory: '$(System.DefaultWorkingDirectory)'
tasks: assembleProductA
You can also define jdk version and gradle option by adding jdkVersionOption: '1.8'
or gradleOptions: '-Xmx3072m'
in gradlew YAML file.
Add your signing configuration / keystore to sign release apk
- task: AndroidSigning@3
inputs:
apkFiles: '**/*.apk'
apksign: true
apksignerKeystoreFile: 'production.keystore'
apksignerKeystorePassword: 'keystorePassword'
apksignerKeystoreAlias: 'key123'
apksignerKeyPassword: 'aliasPassword'
apksignerArguments: --out $(Build.SourcesDirectory)/app/build/outputs/apk/vrelease/*.apk
zipalign: true
Generate the artifact then publish
Here I am using AppCenter to publish releases, you can use Google Play Store or any other platform to distribute app.
If everything is going well you will see something like this :
Ref Azure Gradle Publish Artifacts
note: this story was 2 years in draft as I am too lazy to publish
Happy coding!