はじめに
3月23日(木)に行われたGotanda.mobile #2で発表した内容になります。
LT資料:
- AndroidアプリをOSSで運用してみる // Speaker Deck
- yamacraft/android.RequestPermissions: RequestPermissions動作確認用サンプルプロジェクト
概要
個人開発しているAndroidアプリをOSSとして運用する上で、「公に見せたくない情報の管理をどうするか」という懸念点があると思います。
今回はその中でbuild.gradle
内の情報の一部をリポジトリ上で見えない形で運用する方法の一部を共有します。
あくまでもこれが正解なのかどうかわからないので、より良い方法があればコメントなどにご意見おねがいします!
local.propertiesによるテキストの保存
Androidプロジェクトで「リポジトリに残したくないテキスト情報」はlocal.propertiesで管理するのが一般的です。
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Mon Mar 20 10:49:41 JST 2017
sdk.dir=/XXXX/android-sdk
RELEASE_STORE_PASSWORD=XXXXXX
RELEASE_KEY_ALIAS=XXXXX
RELEASE_KEY_PASSWORD=XXXXX
apply plugin: 'com.android.application'
android {
// local.propertiesの読み込み
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
signingConfigs {
release {
storeFile rootProject.file('release.jks')
storePassword properties.getProperty("RELEASE_STORE_PASSWORD")
keyAlias properties.getProperty("RELEASE_KEY_ALIAS")
keyPassword properties.getProperty("RELEASE_KEY_PASSWORD")
}
}
local.properties自体はAndroidプロジェクトの.gitignoreにデフォルトで記載されていますので、リポジトリに入ることはありません。
ただしこのとき、CI等がビルドしようとした際にproperties.load(project.rootProject.file('local.properties').newDataInputStream())
の処理でlocal.propertiesが見つけられずエラーを吐いてしまいます。
なので、CIではビルド前に空のlocal.propertiesファイルを作るとよいでしょう。
checkout:
post:
- cp local.properties.ci local.properties
これ、普通に- echo > local.properties
でも良かったかなあと……
Circle CIの環境変数を利用する
先ほどの設定の場合、自身のローカル環境以外、特にCircleCIではlocal.propertiesがない(もしくは空)ためビルドができません。
どうするかというと、CircleCIの環境変数を利用します。
該当のプロジェクトの設定から「Environment Variable」を選び、「Add Variable」で環境変数を設定します。
こうして登録した環境変数のvalueは最後の数文字以外は「x」で埋められるため、入力した本人でも内容がわからないようになります。
ただ問題があって、CircleCIの環境変数には上限があるらしく(しかもとても小さい)、今回のテストプロジェクトでは必要な変数を全て登録できませんでした。
CircleCIに任意のデータを登録しておく方法 - Qiita
ということでどうしたかというと、Circle CIのドキュメントにある環境変数の登録の手段として、「暗号化したテキストデータをリポジトリに含め、CIが動くときに復号化して~/.circlerc
に追記する」があったのでそれを利用することにしました。
- Environment Variables - CircleCI
- circleci/encrypted-files: Storing encrypted files in source on CircleCI
ということで、
export RELEASE_STORE_PASSWORD=xxxxxxxxxx
export RELEASE_KEY_ALIAS=xxxxxxxxx
export RELEASE_KEY_PASSWORD=xxxxxxx
というファイルを $openssl aes-256-cbc -e -in secret-env-plain -out secret-env-cipher -k $KEY
で暗号化したsecret-env-cipher
をリポジトリに加え、Circle CIの環境変数にKEYを登録し、
dependencies:
pre:
- openssl aes-256-cbc -d -in secret-env-cipher -k $KEY >> ~/.circlerc
とcircle.ymlに記述することで、復号化した中身を~/.circlerc
に追記して環境変数の登録を対応しました。
当たり前ですが、secret-env-plain
は.gitignoreでリポジトリの除外対象として対応します。
こうして取得した環境変数をbuild.gradleから読み込む場合はSystem.getenv("環境変数名")
をつかいます。
apply plugin: 'com.android.application'
android {
signingConfigs {
release {
storeFile rootProject.file('release.jks')
storePassword System.getenv("RELEASE_STORE_PASSWORD")
keyAlias System.getenv("RELEASE_KEY_ALIAS")
keyPassword System.getenv("RELEASE_KEY_PASSWORD")
}
}
複合:local.propertiesとcircle ciの環境変数を取捨選択する
ということでローカル環境、CI環境それぞれで必要な情報を暗号化する手段を紹介しましたが、個人的にやりたいことは「基本はlocal.propertiesを参照、なければ環境変数から取得」です。
これはProperties.getProperty()
で「取得できなければ第二引数に書かれたものを返す」処理で対応することができました。
なのでサンプルプロジェクトのbuild.gradleでは最終的にこうなっています。
apply plugin: 'com.android.application'
android {
// local.propertiesの読み込み
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
signingConfigs {
release {
storeFile rootProject.file('release.jks')
storePassword properties.getProperty("RELEASE_STORE_PASSWORD", System.getenv("RELEASE_STORE_PASSWORD"))
keyAlias properties.getProperty("RELEASE_KEY_ALIAS", System.getenv("RELEASE_KEY_ALIAS"))
keyPassword properties.getProperty("RELEASE_KEY_PASSWORD", System.getenv("RELEASE_KEY_PASSWORD"))
}
}
おわりに
現時点のサンプルプロジェクトではrelease.jksが生バイナリのままリポジトリに上がっているなど、まだ個人的にイマイチな感じがあります。このあたりも追々解決し、ご紹介できればと思います。