I would like to have my Gradle build to create a release signed apk file using Gradle.
I\'m not sure if the code is correct or if I\'m missing a parameter when doing
You should not put your signing credentials directly in the build.gradle.kts file. Instead the credentials should come from a file not under version control.
Put a file signing.properties where the module specific build.gradle.kts is found. Don't forget to add it to your .gitignore file!
signing.properties
storeFilePath=/home/willi/example.keystore
storePassword=secret
keyPassword=secret
keyAlias=myReleaseSigningKey
build.gradle.kts
android {
// ...
signingConfigs {
create("release") {
val properties = Properties().apply {
load(File("signing.properties").reader())
}
storeFile = File(properties.getProperty("storeFilePath"))
storePassword = properties.getProperty("storePassword")
keyPassword = properties.getProperty("keyPassword")
keyAlias = "release"
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
// ...
}
}
}
It's amazing how many convoluted ways there are for doing this. Here is my own way, where I try to adhere to Googles own recommendation. However, their explanation is not fully clear, so I will describe the procedure for Linux in detail.
The default Google instructions for automatically signing an app during the build, without keeping the passwords and signature files in your app development (GIT) path, is rather obscure. Here are the clarified step-by-step instructions how to do so.
You have an app called "MyApp" in a directory given by the following path:
$HOME/projects/mydev/MyApp
. However, the MyApp directory is used and
controlled with GIT.
We obviously don't want to have our signature or password files anywhere in
the GIT controlled directory, even if we are very able to use .gitignore
etc, it is still too risky and easy to make a mistake. So we want our keystore and signature files outside.
We need to do three (3) things:
build.gradle
file to use (1) and (2).For this example we name the two files:
keystore.properties
MyApp-release-key.jks
We can put both of these files here:
cd $HOME/projects/mydev/
The first file contain the clear text passwords used in; and paths to the release-key file in (2). Start with filling this out, as it will make a copy paste operation easier for the next step.
cd $HOME/projects/mydev/
Edit keystore.properties
so that it's content is:
storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation
The only tricky part here, is the myStoreFileLocation
. This is the path as seen from the module build.gradle
file during the build. This usually means a path similar and relative to: $HOME/projects/mydev/MyApp/app/build.gradle
. So in order to point to the MyApp-release-key.jks
file, what we need to put here is:
../../../MyApp-release-key.jks
Here, we also chose the "myapp" alias for the key. Then the final file should look:
storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myapp
storeFile=../../../MyApp-release-key.jks
The second file is automatically generated when you create the signature key. If you have no other apps and this is your only keystore, then create the file with:
cd $HOME/projects/mydev/
keytool -genkeypair -v -keystore MyApp-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myapp
This will ask you for two passwords and a bunch of info. (Same stuff as in Android Studio.) Now copy/paste your previously chosen passwords.
gradle.build
file to use the aboveThe following parts need to be present in your app/module's Gradle build file. First, add the following lines outside and before your android {}
block.
//def keystorePropertiesFile = rootProject.file("$HOME/.android/keystore.properties")
def keystorePropertiesFile = rootProject.file("../../keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
Then, inside the android {}
block, add:
android {
...
defaultConfig { ... }
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
// Tell Gradle to sign your APK
buildTypes {
release {
signingConfig signingConfigs.release
...
}
}
}
Now from shell, you can re-build your app with:
cd $HOME/projects/mydev/MyApp/app/
./gradlew clean build
This should generate a properly signed app that can be used in Google Play.
UPDATE: 2019-04-02
More recent versions of keytool
and something is telling you that you should use a PKCS12 based keyfile instead of the original/default as I use above. They then go on telling you you should convert to the new open PKCS12 format. However, it seem that the Android development tools are not quite ready for this yet, because if you do, you will get the following weird errors:
com.android.ide.common.signing.KeytoolException:
Failed to read key XXX from store "F:\XXX\XXX.jks": Get Key failed: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
So don't use a converted key!
Easier way than previous answers:
Put this into ~/.gradle/gradle.properties
RELEASE_STORE_FILE={path to your keystore}
RELEASE_STORE_PASSWORD=*****
RELEASE_KEY_ALIAS=*****
RELEASE_KEY_PASSWORD=*****
Modify your app/build.gradle
, and add this inside the android {
code block:
...
signingConfigs {
release {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
// Optional, specify signing versions used
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
....
Then you can run gradle assembleRelease
Also see the reference for the signingConfigs Gradle DSL
@Destil's answer is good if you can reuse the same configuration across all projects. Alternatively, Android Studio comes with a local.properties
file that can maybe be used instead, but it's supposedly IDE-generated and I can't find a way to extend it from within Android Studio.
This is a variation of @jonbo's answer. That answer allows project specific settings but it comes with a bit of developer overhead. Specifically, significant boilerplate is required to move the signingConfigs
definition into a separate file -- especially if you need to do so for multiple projects, which is a prime reason for picking this solution over Destil's. This can be somewhat alleviated by also including the line
apply plugin: 'com.android.application'
in the credentials file, as this will allow IDE completion.
Finally, most solutions here do not allow building the project in debug mode -- which handles debug-signing automatically -- without providing a syntactically if not semantically valid signingConfigs
definition. If you do not need to produce a release build from a given machine, this extra step can be seen as an unnecessary obstacle. On the other hand, it can be an aid against ignorant or lazy colleagues running debug builds in production.
This solution will allow debug builds without worrying about credentials at all, but it will require valid credentials to produce release builds, and it takes very little boilerplate. However, as a downside it might encourage others to replace dummy values with real credentials and there's no way to protect against that.
// app/build.gradle
// Define this structure in signing.gradle to enable release builds.
ext.signing = [
storeFilePath : 'path/to/keystore',
storePassword : 'keystore password',
keyAlias : 'key alias',
keyPassword : 'key password',
]
if (file('signing.gradle').exists()) {
apply from: 'signing.gradle'
}
android {
...
signingConfigs {
release {
storeFile file(project.signing.storeFilePath)
storePassword project.signing.storePassword
keyAlias project.signing.keyAlias
keyPassword project.signing.keyPassword
}
}
buildTypes {
debug { ... }
release {
signingConfig signingConfigs.release
...
}
}
}
This creates a dummy property that serves purely to produce a syntactically valid build file. The values assigned to ext.signing
's properties are irrelevant as far as debug builds go. To enable release builds, copy ext.signing
into signing.gradle
and replace the dummy values with valid credentials.
// signing.gradle
ext.signing = [
storeFilePath : 'real/keystore',
storePassword : 'real keystore password',
keyAlias : 'real key alias',
keyPassword : 'real key password',
]
Of course, signing.gradle
should be ignored by VCS.
If you have the keystore file already, it can be as simple as adding a few parameters to your build command:
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=$KEYFILE \
-Pandroid.injected.signing.store.password=$STORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
No permanent changes to your Android project necessary.
Source: http://www.tinmith.net/wayne/blog/2014/08/gradle-sign-command-line.htm
An alternative is to define a task that runs only on release builds.
android {
...
signingConfigs {
release {
// We can leave these in environment variables
storeFile file('nameOfKeystore.keystore')
keyAlias 'nameOfKeyAlias'
// These two lines make gradle believe that the signingConfigs
// section is complete. Without them, tasks like installRelease
// will not be available!
storePassword "notYourRealPassword"
keyPassword "notYourRealPassword"
}
}
buildTypes {
...
release {
signingConfig signingConfigs.release
...
}
}
...
}
task setupKeystore << {
final Console console = System.console();
if (console != null) {
//def keyFile = console.readLine(“\nProject: “ + project.name + “Enter keystore path: "))
//def keyAlias = console.readLine(“Project: “ + project.name + “Enter key alias: ")
def storePw = new String(console.readPassword(“Project: “ + project.name + “. Enter keystore password: "))
def keyPw = new String(console.readPassword(“Project: “ + project.name + “.Enter keystore password: "))
//android.signingConfigs.release.storeFile = file(keyFile);
//android.signingConfigs.release.keyAlias = keyAlias
android.signingConfigs.release.storePassword = storePw
android.signingConfigs.release.keyPassword = keyPw
}
}
//Validate t
def isReleaseConfig = gradle.startParameter.taskNames.any {it.contains('Release') }
if (isReleaseConfig) {
setupKeystore.execute();
}