#前提条件
今回対象とした環境
- Android Studio v0.8.2
- Gradle 1.12 (Android Studioに内包されているもの)
- OS X Mavericks 10.9.4
- 利用しているプロジェクトはAndroid Studioが生成したHello worldプロジェクト
- ファイルの有無確認などのエラーハンドリングは全て省略
- Gradleを実行する時のコマンドは全てgradlewがある階層で以下をタイプしています
./gradlew clean assembleRelease
#Android StudioのGUI
一番基本的な方法としてGUIを利用する。
Build > Generate Signed APK... > Generate Signed APK Wizardを淡々とこなしていく。
新規でアプリを作成した場合、まずはこの方法でkeystoreファイルを作成する。
#app/build.gradleに直接書き込む
ここからGradleを使ってみる。
まずは直接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ファイルです。このファイルに定義しておけば簡単に読み込むことが出来ます。
KEY_ALIAS=keyAlias
KEY_PASSWORD=keyPassword
STORE_PASSWORD=storePassword
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ファイルの扱いをどうするかを考える必要が出てきます。
#環境変数から読み込む
環境変数に値を設定した結果
% env | egrep "key|Pass"
KEY_ALIAS=keyAlias
KEY_PASSWORD=keyPassword
STORE_PASSWORD=storePassword
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)で値を設定。
#プロパティファイルから読み込む
storePassword=storePassword
keyAlias=keyAlias
keyPassword=keyPassword
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直下にあるプロパティファイルを参照し、それぞれのプロパティ値を設定。
#コンソールから直接入力
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種類の値を設定する。
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を利用してキーチェーンを叩く。
最初に許可をするかのポップアップが出るので、注意。
#個人的な感想
ネタ的な方法も検証してみたけど、案外全て使えるのでは?と思った。
個人で開発しているなら、直接書き込むことをおすすめします。
チーム開発の場合、パスワードへのアクセス権限の有無があったりしますので、最適な方法を検証して下さい。上記の手法の組み合わせを行うといいと思います。