assembleRelease task dependency - Ask for keystore

2019-03-09 13:34发布

问题:

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

回答1:

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
    }
    ...


回答2:

This is my complete solution for release key signing.

  1. It detects if console is not available in daemon mode.
  2. 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



回答3:

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"


回答4:

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 ""
    }
}


回答5:

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.