Signing product flavors with gradle
I am tyring to migrate my projects to gradle. One of my projects has multiple product flavors and each one of them has to be signed with a different signingConfig
in its release version. So this is what I tried so far:
buildscript {
...
}
apply plugin: 'android'
android {
compileSdkVersion 17
buildToolsVersion '17'
signingConfigs {
flavor1 {
storeFile file("keystore")
storePassword "secret"
keyAlias "aliasForFlavor1"
keyPassword "secretFlavor1"
}
flavor2 {
storeFile file("keystore")
storePassword "secret"
keyAlias "aliasForFlavor2"
keyPassword "secretFlavor2"
}
}
productFlavors {
flavor1 {
signingConfig signingConfigs.flavor1
}
flavor1 {
signingConfig signingConfigs.flavor2
}
}
}
dependencies {
...
}
When I run gradle build
I get a groovy.lang.MissingFieldException
and the following error message:
No such field: signingConfigs for class: com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
So I assume the productFlavors
.* part of the Gradle script is not the right place to put code signing configurations.
Solution 1:
You can declare signing config
for each flavor
in buildType
. Here is my gradle file for release signing flavors with different keystores.
android {
signingConfigs {
configFirst {
keyAlias 'alias'
keyPassword 'password'
storeFile file('first.keystore')
storePassword 'password'
}
configSecond {
keyAlias 'alias'
keyPassword 'password'
storeFile file('second.keystore')
storePassword 'password'
}
}
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
}
productFlavors{
flavor1 {
applicationId "com.test.firstapp"
}
flavor2 {
applicationId "com.test.secondapp"
}
}
buildTypes {
release {
productFlavors.flavor1.signingConfig signingConfigs.configFirst
productFlavors.flavor2.signingConfig signingConfigs.configSecond
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
buildTypes
block should be placed after productFlavors
block, I mean order is important.
Solution 2:
Per the user guide, signingConfigs for flavors are supported.
The problem here has to do with the scope of the signingConfigs object. I just assigned it to a variable inside the productFlavors
block, but outside the flavor1
flavor block to fix the issue:
productFlavors {
def flavor1SigningVariable = signingConfigs.flavor1
flavor1 {
...
signingConfig flavor1SigningVariable
...
}
Solution 3:
The gradle plugin for android only supports signing per build type, not per flavor. The reason for this is that any given variant (build type + flavors) can only be signed by one key, but can be a combination of several flavor groups. For example your flavor groups could be cpu (x86/arm) and version (free/paid), that's four different variants right there.
The solution you're looking for is to create separate build types for your different release versions. For example, your build types might be debug
, release
, release-beta
, like this:
...
android {
...
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.release
}
release-beta {
initWith release
signingConfig signingConfigs.release-beta
}
}
}
The initWith
above just tells gradle that release-beta
should be a copy of the release
build type, only signed with a different key.
Solution 4:
Maybe another interesting solution with dynamic flavor signing configs and other advantages
- Im fine with defining app id and app name of flavors in gradle (it is clear and just 2 lines for each flavor) but I do not want to define separate signing configs (gradle file would be too long when adding flavors)
- I also do not want to have sensitive signing information placed in gradle because of commiting it
- Bonus advantage is that debug build has another app id and app name.
.gitignore
...
/keystore.properties
.keystore.properties
storeFile=../mystore.jks
storePassword=...
keyAliasFlavor1=...
keyPasswordFlavor1=...
keyAliasFlavor2=...
keyPasswordFlavor2=...
app/build.gradle
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(rootProject.file('keystore.properties')))
android {
...
buildTypes {
debug {
...
manifestPlaceholders = [appNameSuffix: " Dev"]
applicationIdSuffix ".dev"
}
release {
...
manifestPlaceholders = [appNameSuffix: ""]
productFlavors.all { flavor ->
flavor.signingConfig = android.signingConfigs.create("${flavor.name}")
flavor.signingConfig.storeFile = rootProject.file(keystoreProperties["storeFile"])
flavor.signingConfig.storePassword = keystoreProperties["storePassword"]
flavor.signingConfig.keyAlias = keystoreProperties["keyAlias${flavor.name}"]
flavor.signingConfig.keyPassword = keystoreProperties["keyPassword${flavor.name}"]
}
}
}
productFlavors {
Flavor1 {
applicationId "..."
manifestPlaceholders = [appNameBase: "MyApp 1"]
}
Flavor2 {
applicationId "..."
manifestPlaceholders = [appNameBase: "MyApp 2"]
}
// ... and many other flavors without taking care about signing configs
// (just add two lines into keystore.properties for each new flavor)
}
}
app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
...
<application android:label="${appNameBase}${appNameSuffix}" ...>
...
</application>
</manifest>