To avoid writing the keystore password in plain text, I'm trying to add a dependency to the assembleRelease task created by the android Gradle plugin.
I've checked the Gradle documentation Manipulating existing tasks but I'm unable to place the dependency where it should
This is my task, defined in $root$/myApp/build.gradle above the android plugin.
task readPasswordFromInput << {
def console = System.console()
ext.keystorePassword = console.readLine('\n\n\n> Enter keystore password: ')
}
apply plugin: 'android'
Then, I've tried the two possibilities offered by Gradle: task.dependsOn and task.doFirst, but none works. The latter appears to be ignored, while dependsOn does add the dependency, but too late in the dependency chain. Running ./gradlew tasks --all prints this
:assembleRelease - Assembles all Release builds [libs:ActionBarSherlock:bundleRelease, libs:DataDroid:bundleRelease, libs:SlidingMenu:bundleRelease]
:compileRelease
...
[SEVERAL TASKS]
...
:packageRelease
...
[SEVERAL TASKS]
...
:readPasswordFromInput
The problem is, the keystore password is needed in the task packageRelease
Just as a side note, this works as I want
buildTypes {
release {
def console = System.console()
ext.keystorePassword = console.readLine('\n\n\n> IF building release apk, enter keystore password: ')
debuggable false
signingConfigs.release.storePassword = ext.keystorePassword
signingConfigs.release.keyPassword = ext.keystorePassword
signingConfig signingConfigs.release
}
}
but it asks for the password every single time you use gradlew, no matter if it's a clean or an assemble
Thanks!
EDIT
Thanks to @Intae Kim, here's my build.gradle version 2.0
task readPasswordFromInput << {
def console = System.console()
ext.keystorePassword = console.readLine('\n\n\n> Enter keystore password: ')
android.signingConfigs.release.storePassword = ext.keystorePassword
android.signingConfigs.release.keyPassword = ext.keystorePassword
}
tasks.whenTaskAdded { task ->
if (task.name == 'validateReleaseSigning') {
task.dependsOn readPasswordFromInput
}
}
apply plugin: 'android'
Then, the buildTypes
release {
debuggable false
signingConfig signingConfigs.release
runProguard true
proguardFile 'my-file.txt'
}
Gradle executes correctly, but it only generates a release-unsigned.apk
try:
tasks.whenTaskAdded { task ->
if (task.name == 'packageRelease') {
task.dependsOn readPasswordFromInput
}
}
with your readPasswordFromInput
task.
UPDATED:
In this way you can see that following code works.
def runTasks = gradle.startParameter.taskNames
if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'a' in runTasks || 'aR' in runTasks) {
android.signingConfigs.releaseSign.storeFile = file('/path/to/keystore')
android.signingConfigs.releaseSign.storePassword = System.console().readLine('KeyStore Password: ')
android.signingConfigs.releaseSign.keyAlias = ...
android.signingConfigs.releaseSign.keyPassword = System.console().readLine('Alias Password: ')
android.buildTypes.release.signingConfig = android.signingConfigs.releaseSign
}
and if you encounter build fail, it may be required to asign an empty keysign config on android.signingConfig
:
android {
...
signingConfigs {
releaseSign
}
...
This is my complete solution for release key signing.
- It detects if console is not available in daemon mode.
- It hides passwords.
Use gradle --no-daemon assembleRelease
if you are using daemon mode.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.+'
}
}
tasks.whenTaskAdded { task ->
if (task.name == 'validateReleaseSigning')
task.dependsOn keystoreInfo
}
apply plugin: 'android'
repositories {
mavenCentral()
}
android {
compileSdkVersion 18
buildToolsVersion '18.0.1'
defaultConfig {
minSdkVersion 7
targetSdkVersion 18
}
signingConfigs {
release {
release {
storeFile file('release.keystore')
storePassword ''
keyAlias ''
keyPassword ''
}
}
buildTypes {
release {
debuggable false
signingConfig signingConfigs.release
}
}
}
}
task keystoreInfo << {
def console = System.console()
if (console == null)
throw new IllegalStateException('no console available, use --no-daemon flag')
def storeFile = console.readLine('Keystore: ')
def storePassword = console.readPassword('Keystore password: ')
def keyAlias = console.readLine('Key alias: ')
def keyPassword = console.readPassword('Key password: ')
android.signingConfigs.release.storeFile = file(storeFile)
android.signingConfigs.release.storePassword = new String(storePassword)
android.signingConfigs.release.keyAlias = keyAlias
android.signingConfigs.release.keyPassword = new String(keyPassword)
}
Gist http://gist.github.com/grzegorz-zur/6416924
I've created solution, which works fine for me, you can test it out
android {
signingConfigs {
release {
storeFile = file('android.keystore')
keyAlias = "my_key_alias"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
task readPasswordFromInput << {
if(!project.hasProperty('keyStore') || !project.hasProperty('keyPass') || !project.hasProperty('storePass')) {
println "\n\$ Enter signing details manually or run with \"-PkeyStore={key.store.name} -PstorePass={StoreSecretPassword} -PkeyPass={KeySecretPassword}\""
}
if(!project.hasProperty('keyStore')) {
def newKeyStore = System.console().readLine("\n\$ Enter keystore location or enter (default: android.keystore): ")
if(newKeyStore != '') android.signingConfigs.release.storeFile = file('${newKeyStore}')
} else {
android.signingConfigs.release.storeFile = file(project.keyStore)
}
android.signingConfigs.release.storePassword = project.hasProperty('storePass') ? project.storePass : new String(System.console().readPassword("\$ Store password: "))
android.signingConfigs.release.keyPassword = project.hasProperty('keyPass') ? project.keyPass : new String(System.console().readPassword("\$ Key password: "))
}
tasks.whenTaskAdded { task ->
if (task.name == 'validateReleaseSigning') {
task.dependsOn readPasswordFromInput
}
}
Then you can pass all arguments from CLI on prompt (uses readPassword
, so it won't be visible) or you can pass them as CLI arguments to script
gradle assemble
gradle assemble -PkeyStore="~/.android/my.keystore"
gradle assemble -PkeyStore="~/.android/my.keystore" -PstorePass="MyStorePass"
gradle assemble -PkeyStore="~/.android/my.keystore" -PstorePass="MyStorePass" -PkeyPass="MyKeyPass"
Here is what I do.
task('readPasswordFromInput') << {
def console = System.console()
ext.keystorePassword = console.readLine('\n\n\n> Enter keystore password: ')
android.signingConfigs.release.storePassword = ext.keystorePassword
android.signingConfigs.release.keyPassword = ext.keystorePassword
}
tasks.whenTaskAdded { task ->
if (task.name.matches("validateReleaseSigning")) {
task.dependsOn('readPasswordFromInput')
}
}
signingConfigs {
debug {
storeFile file("my-debug-key.keystore")
}
release {
storeFile file("my-release-key.keystore")
storePassword ""
keyAlias "release_key"
keyPassword ""
}
}
Google recently added an official way to do this, see https://developer.android.com/studio/publish/app-signing.html#secure-shared-keystore
It may not answer my original question (ask for password), but I think it's the better way to simplify the deploy and keep the credentials safe.