Build a release variant without disclosing the signing configuration
I’m ashamed to admit this, but, I rarely test against release variants before going to production. I build a signed APK and test that instead. And between you and I, this generally happens just before pushing to the Play Store.
Not good.
But then we hired a smart QA guy. One of the first things he asked, dumbfounded: “Why are you killing my freedom by making me manually build signed APKs every time I need to test something?”
He had a good point. Especially considering that you can add signing configurations to the build.gradle
file. It’s pretty simple and Google shows you how to do it in an article called Signing Your Applications:
... android { ... defaultConfig { ... } signingConfigs { release { storeFile file("myreleasekey.keystore") storePassword "password" keyAlias "MyReleaseKey" keyPassword "password" } } buildTypes { release { ... signingConfig signingConfigs.release } } } ...
Easy! But then your company grows up. What was a three person team doubles in size. You hire more QA guys. You hire contractors. And then you realize: “Shit. We probably shouldn’t give everyone access to our production signing configurations.” Which is why Google gives this sage advice:
Note: Including the passwords for your release key and keystore inside the build file is not a good security practice. Alternatively, you can configure the build file to obtain these passwords from environment variables or have the build process prompt you for these passwords.
Source: http://developer.android.com/tools/publishing/app-signing.html
Easy! Except for one small thing: Every developer on your team needs to set up and manage their own environment variables. And don’t forget to configure your build server. And then do it for every single app your growing team is working on. Then–and this is the most important part–don’t forget to document everything you just did.
OK. So, there has to be an easier way. And there is.
Step one: Create a signing configuration
This is pretty simple. Create a new file in your project’s root directory. I typically name this file release.gradle
, but you can name it whatever you like. In the file, add the same signingConfigs
block that you would have added to your build.gradle
file. It should look something like this:
android.signingConfigs { release { keyAlias 'com.example.app' keyPassword 'key password' storeFile rootProject.file('release.keystore') storePassword 'store password' } }
Two things to point out here. First, we prepend android
to signingConfigs
on line 1. At build time, this file will be added to build.gradle
outside of the android { [...] }
block. Using android.signingConfigs
will allow the android plugin to pick it up.
Second, the keystore is called release.keystore
on line 5. It’s stored in the project’s root.
Since a major point of this exercise is to keep signing configurations outside of source control, don’t forget to add both release.keystore
and release.gradle
to .gitignore
.
This bears repeating: Don’t check in release.keystore
or release.gradle
.
Step two: Change the build file
Now we need to change build.gradle
so that release.gradle
is added at build time:
apply from: rootProject.file('release.gradle') ... android { ... buildTypes { release { ... signingConfig signingConfigs.release } } } ...
Let’s compare this to Google’s suggested solution above. Since signingConfigs
was moved to release.gradle
, we don’t need it in build.gradle
anymore. So take that out.
The key to this whole endeavor is apply from:
on line 1. Here we are loading our release.gradle
file as a plugin.
Script plugins can be applied from a script on the local filesystem or at a remote location. Filesystem locations are relative to the project directory, while remote script locations are specified with an HTTP URL. Multiple script plugins (of either form) can be applied to a given build.
Source: https://docs.gradle.org/current/userguide/plugins.html
This is exactly the same idea as apply plugin: 'com.android.application'
. The distinction is that the Android Gradle plugin is a binary, whereas our plugin is a script.
Step three: Create a keystore
I’m not going to go into any detail here because Google adequately covers it in Signing Your Applications. The only things I’ll add are that you should put the keystore in your project’s root, and you should name it the same thing you put in release.gradle
above.
Step four: Build your app
Open up the Build Variants dialog located on the bottom left of Android Studio (also available by going to View > Tool Windows > Build Variants). All the project’s modules will be listed. Select the release variant from the dropdown next to your app’s module. Build then run your app.
Epilogue
Share the release.gradle
and release.keystore
files with developers you trust. The developers you don’t trust will have to create their own ‘dummy’ versions of these files. It really doesn’t get any easier.