Android Studio(Gradle)でapkファイルを作成する時にstorePassword/keyAlias/keyPasswordの指定方法をいくつか検証してみた。

#前提条件

今回対象とした環境

* Android Studio v0.8.2
* Gradle 1.12 (Android Studioに内包されているもの)
* OS X Mavericks 10.9.4
* 利用しているプロジェクトはAndroid Studioが生成したHello worldプロジェクト
* ファイルの有無確認などのエラーハンドリングは全て省略
* Gradleを実行する時のコマンドは全てgradlewがある階層で以下をタイプしています

```bash
./gradlew clean assembleRelease
```

#Android StudioのGUI

一番基本的な方法としてGUIを利用する。

Build > Generate Signed APK... > Generate Signed APK Wizardを淡々とこなしていく。

新規でアプリを作成した場合、まずはこの方法でkeystoreファイルを作成する。

#app/build.gradleに直接書き込む

ここからGradleを使ってみる。
まずは直接build.gradleに書き込む方法。

```groovy:app/build.gradle
plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword "storePassword"
            keyAlias "keyAlias"
            keyPassword "keyPassword"
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}
```

buildTypeのreleaseに```signingConfig signingConfigs.release```を追加することを忘れない。
個人で開発しているのであれば、この方法が一番手っ取り早い。

#gradle.propertiesから読み込む

gradle.propertiesはデフォルトのpropertyファイルです。このファイルに定義しておけば簡単に読み込むことが出来ます。

```properties:gradle.properties
KEY_ALIAS=keyAlias
KEY_PASSWORD=keyPassword
STORE_PASSWORD=storePassword
```

```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword STORE_PASSWORD
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

```

gradle.propertiesのkeyをそのままbuild.gradleで使えるので、シンプルに書けます。

gradle.propertiesファイルを.gitignoreに入れ、このファイルを共有しないようにすれば、特定のPCからのみリリース作業が出来るようになります。
個人の開発であれば便利ですが、チーム開発であったり、複数人がリリース作業が出来る状態になるのであれば、gradle.propertiesファイルの扱いをどうするかを考える必要が出てきます。

#環境変数から読み込む

環境変数に値を設定した結果

```bash
% env | egrep "key|Pass"
KEY_ALIAS=keyAlias
KEY_PASSWORD=keyPassword
STORE_PASSWORD=storePassword
```

```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword System.getenv('STORE_PASSWORD')
            keyAlias System.getenv('KEY_ALIAS')
            keyPassword System.getenv('KEY_PASSWORD')
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

```

signingConfigsのreleaseでSystem.getenv(String)で値を設定。

#プロパティファイルから読み込む

```properties:$HOME/myapp.signconfig.gradle
storePassword=storePassword
keyAlias=keyAlias
keyPassword=keyPassword
```


```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            def signFile = file("${System.getenv('HOME')}/myapp.signconfig.gradle")
            def signingProps = new Properties()
            signingProps.load(new FileInputStream(signFile))

            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword signingProps['storePassword']
            keyAlias signingProps['keyAlias']
            keyPassword signingProps['keyPassword']
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}
```

signingConfigsのreleaseで$HOME直下にあるプロパティファイルを参照し、それぞれのプロパティ値を設定。

#コンソールから直接入力

```groovy:app/build.gradle
apply plugin: 'com.android.application'

gradle.taskGraph.whenReady { taskGraph ->
    if(taskGraph.hasTask(':app:assembleRelease')) {
        def storePasswordValue = new String(System.console().readPassword("Enter store password: "))
        def keyAliasValue = new String(System.console().readLine("Enter key alias: "))
        def keyPasswordValue = new String(System.console().readPassword("Enter key password: "))

        android.signingConfigs.release.storePassword = storePasswordValue
        android.signingConfigs.release.keyAlias = keyAliasValue
        android.signingConfigs.release.keyPassword = keyPasswordValue
    }
}

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword ""
            keyAlias ""
            keyPassword ""
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}
```

taskGraphを利用して、configurationフェーズ後に:app:assembleReleaseが存在していた場合のみコンソールでパスワードなどの入力を求めるようにしてあります。
signingConfingsでやってしまうと、別に:app:assembleReleaseではないタスクを実行したときもコンソールで入力を求めてしまうためです。

signingConfingsでstorePasswordなどに空文字を設定しているのは、これをしないと証明書つきのapkを作成してくれないためです。

#キーチェーンを使う

キーチェーンに3種類の値を設定する。

```groovy:app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "com.shiraji.myapplication"
        minSdkVersion 14
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
    }

    signingConfigs {
        release {
            storeFile file("${System.getenv('HOME')}/release.jks")
            storePassword getPassword("shiraji", "storePassword")
            keyAlias getPassword("shiraji", "keyAlias")
            keyPassword getPassword("shiraji", "keyPassword")
        }
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.release
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

String getPassword(String currentUser, String keyChain) {
    def stdout = new ByteArrayOutputStream()
    def stderr = new ByteArrayOutputStream()
    exec {
        commandLine 'security', '-q', 'find-generic-password', '-a', currentUser, '-gl', keyChain
        standardOutput = stdout
        errorOutput = stderr
        ignoreExitValue true
    }
    (stderr.toString().trim() =~ /password: "(.*)"/)[0][1]
}
```

getPassword(String, String)が重要で、execを利用してキーチェーンを叩く。
最初に許可をするかのポップアップが出るので、注意。

#個人的な感想

ネタ的な方法も検証してみたけど、案外全て使えるのでは？と思った。
個人で開発しているなら、直接書き込むことをおすすめします。
チーム開発の場合、パスワードへのアクセス権限の有無があったりしますので、最適な方法を検証して下さい。上記の手法の組み合わせを行うといいと思います。