10

How can I set different signing configs for different variants?

For instance, we currently have the buildtypes Debug/Beta/Release with 2 flavors, free and paid, resulting in 6 variants. To make it a bit easier, let's forget the Debug variants and only focus on freeBeta/paidBeta/freeRelease/paidRelease.

What I'd like, is for each variant to use a separate different signingConfig.

So far the only solutions I could find is either putting the signingConfigs in the buildTypes so all Beta variants would have the same signingConfigs:

buildTypes {
    beta {
        signingConfigs.beta
    }
    release {
        signingConfigs.release
    }
}

Alternatively, using the flavors, in which case all free variants would have the same signingConfigs:

productFlavors {
    free {
        signingConfig signingConfigs.free
        applicationId 'com.example.free'
    }
    paid {
        signingConfig signingConfigs.paid
        applicationId 'com.example.paid'
    }
}

Is there a way to do this in the current productFlavor closure? Can this only be fixed by overridding the android.applicationVariants.all { variant -> and manually applying a signingConfig for each application variant based on some naming scheme or some other ugly hack?

I also found this answer, but it doesn't appear to work in the latest build tools; when compiling I get the following error:

FAILURE: Build failed with an exception.

  • Where: Build file '/home/dev/projects/app/build.gradle' line: 61

  • What went wrong: A problem occurred evaluating project ':app'.

    Could not find property 'free' on ProductFlavor container.

Community
  • 1
  • 1
Aegis
  • 5,573
  • 2
  • 29
  • 41
  • Is there a particular reason that you want to use different signing configs for each release type? You can sign multiple APKs with the same key. – Bryan Herbst Sep 23 '15 at 14:24
  • @Tanis.7x because the beta is distributed via another system then Google Play. And it also requires being signed. – Aegis Sep 23 '15 at 14:30
  • It looks like you already have that set up- you have the code snippet that will assign a different signing config for release and beta build types. My question was why each variant needs a different signing key. For example, `freeRelease` and `paidRelease` should be able to use the same key. – Bryan Herbst Sep 23 '15 at 14:32
  • @Tanis.7x That doesn't address the question. The application has already been previously distributed before being converted to a gradle project, so changing keystores isn't a solution. – Paul Lammertsma Sep 23 '15 at 14:38
  • That's the detail I was looking for. Thanks. – Bryan Herbst Sep 23 '15 at 14:39
  • 1
    BTW paid and free are just to make the concept easier this for a more complex app structure white labeling. – Aegis Sep 23 '15 at 14:45
  • I found [this related discussion](https://groups.google.com/forum/#!topic/adt-dev/3L8I1K_mjuo), in which Xavier Ducrohet gives some insights, but this doesn't address different build types per flavor, only different keystores per flavor, regardless of the build type. – Paul Lammertsma Sep 23 '15 at 15:19
  • I honestly would just add all signing configs as build types. Similar (debug) configurations could be set by ```stagingDebug.initWith(buildTypes.debug)```, hence sharing all the same configurations but the ones getting overridden. This would let you imitate the "flavor overrides build type" behavior, possible not needed configurations could then be just filtered out – David Medenjak Sep 25 '15 at 22:26

2 Answers2

5

The answer linked is actually working fine. I got it to compile like this (with buildTools 1.3.1 and gradle-wrapper 2.7). The error you were having (Could not find property "free" on ProductFlavor container) is most certainly due to the fact that your build types are defined before the productFlavors in your build.gradle

This won't work

signingConfigs {
    freeBeta {}
    freeRelease {}
    paidBeta {}
    paidRelease {}
}    
buildTypes {
    debug {}
    beta {}
    release {}
}
productFlavors {
    free {}
    paid {}
}

This would work (simply swapping the definition order of the productFlavors and buildType)

signingConfigs {
    freeBeta {}
    freeRelease {}
    paidBeta {}
    paidRelease {}
}    
productFlavors {
    free {}
    paid {}
}    
buildTypes {
    debug {}
    beta {}
    release {}
}

Here's a full working example:

signingConfigs {
        freeBeta {
            keyAlias 'freeBeta'
            keyPassword 'test'
            storeFile file('C:/keystore.jks')
            storePassword 'test'
        }
        freeRelease {
            keyAlias 'freeRelease'
            keyPassword 'test'
            storeFile file('C:/keystore.jks')
            storePassword 'test'
        }
        paidBeta {
            keyAlias 'paidBeta'
            keyPassword 'test'
            storeFile file('C:/keystore.jks')
            storePassword 'test'
        }
        paidRelease {
            keyAlias 'paidRelease'
            keyPassword 'test'
            storeFile file('C:/keystore.jks')
            storePassword 'test'
        }
    }

    productFlavors {
        free {

        }
        paid {

        }
    }

    buildTypes {
        debug {

        }
        beta {
            productFlavors.free.signingConfig signingConfigs.freeBeta
            productFlavors.paid.signingConfig signingConfigs.paidBeta
        }
        release {
            productFlavors.free.signingConfig signingConfigs.freeRelease
            productFlavors.paid.signingConfig signingConfigs.paidRelease                
        }
    }
Eric Labelle
  • 1,809
  • 2
  • 13
  • 22
  • 1
    Nope, the code lines are executed sequentially here, last release{} block will override beta{} block. So all product flavors will use release signing keys here – Jemshit Iskenderov Apr 02 '20 at 07:42
4

The https://stackoverflow.com/a/32810290/3961802 answer will not work.

    beta {
        productFlavors.free.signingConfig signingConfigs.freeBeta
        productFlavors.paid.signingConfig signingConfigs.paidBeta
    }
    release {
        productFlavors.free.signingConfig signingConfigs.freeRelease
        productFlavors.paid.signingConfig signingConfigs.paidRelease                
    }

In this case, the release build type will overwrite all flavors. So signing config for the freeBeta will be freeRelease.

At the moment, the only solution that I know is to sign all the build variants in a separate task.

signingConfigs {

    bananaDebug {}
    bananaBeta {}
    bananaRelease {}

    orangeDebug {}
    orangeBeta {}
    orangeRelease {}

    lemonDebug {}
    lemonBeta {}
    lemonRelease {}
}

productFlavors {

    banana {}

    orange {}

    lemon {}
}

buildTypes {

    debug {}

    beta {}

    release {}
}

applicationVariants.all {
    def flavorName = it.getFlavorName()
    def buildTypeName = it.buildType.name
    def buildVariantName = flavorName + buildTypeName.capitalize()
    def currentSigningConfig = signingConfigs.getByName(buildVariantName)

    it.mergedFlavor.signingConfig = currentSigningConfig
    // If you want to sign debug build
    buildTypes.debug.signingConfig currentSigningConfig
}
Denys Makhov
  • 164
  • 2
  • 6
  • 1
    Kudos for noticing the flaw mentioned by many other answers on stack overflow, however, this does not solve the problem either. Setting the `signingConfig` on the `mergedFlavor` does not actually sign the assembled apps. – Simas Sep 22 '19 at 20:25
  • In my case, everything works fine and I sign up Google Market apps without any problems. – Denys Makhov Sep 25 '19 at 10:29
  • This seems to be working fine for me as well. I have two flavors of the same app (appOne and appTwo) and three build types (debug, prod, staging), two of which requires signing. I was able to submit appOneRelease, appOneStaging, appTwoRelease, and appTwoStaging to their respective Play Store channels, each with their own signing config. – halileohalilei May 20 '20 at 10:47
  • Doesn't work for me either. As mentioned, running assemble generates an apk named `unsigned` and running `apksigner` to validate returns "DOES NOT VERIFY". – FirstOne Feb 18 '21 at 15:09