Androidで社内リリースの自動化


はじめまして。FiNCでAndroid Applicationを開発している南里です。
Androidアプリ全般の設計と実装を行っています。
インターンとして2年前くらいに加わり、そのまま新卒社員として入社しました。
ちょうどその頃から<a href="http://enigme.black/wp-content/uploads/2015/04/201504100203_R.jpg" target="_blank">バイソン</a>と呼ばれたりもしています。
急速に拡大するプロジェクトで生じたチーム開発の問題点とAndroidチームが行った対処法についてお話したいと思います。

## プロジェクトの拡大による問題点とその対策

FiNCはヘルスケアのアプリ(Web, iOS, Android)を開発しています。
ここ1年ほどでプロジェクトが急速に拡大し、エンジニアの数も約４倍に増えました。
そこで出てきた大きな問題点がコミュニケーション量の爆発的増加です。
中でも、実際に実装した機能と、プロジェクトツール上で確認できる機能のステータスが一致しないといった、
実装機能のチェック時のコミュニケーションロスと1日に複数回の社内配信を手動で実施しているという2つの問題がありました。
そこで、マージ済のコミットを自動的に収集し、開発アプリの社内配信とリリースノートの自動化を行うことにしました。

## アプリの自動化
* CicleCIとFabricを用いた配信の自動化
* リリースノートの自動化


## 開発環境
* Android Studio(おなじみGradle)
* Git, Github
* CircleCI

## 全体構成
![Kobito.eKDEmD.png](https://qiita-image-store.s3.amazonaws.com/0/43762/6551ad17-73c6-a9ea-5268-8d5fbf29b3bd.png "Kobito.eKDEmD.png")


## 実装
手順は以下です。
<br>
#### 1. Fabricを導入する(初期設定)
#### 2. build.gradleをカスタマイズする。(配信のカスタマイズ)
#### 3. CircleCI設定を行う。(トリガーの設定)
<br>
### 1. Fabricの導入(初期設定)
FabricとはTwitter社が提供しているプラグインで、もろもろ機能が利用できるパッケージです。
このパッケージが内包している、Crashlytics(バグのレポートに良く使います)の中のBetaという配信機能を利用します。


まず、AndroidにFabricプラグインをインストールします。
（PreferenceのPluginにあります。)

Android Studioのツールバーにて、以下のアイコンが表示されるようになります。
(右から3番目。ここから配信します。)
![スクリーンショット 2016-05-16 1.13.20.png](https://qiita-image-store.s3.amazonaws.com/0/43762/cf7f4ae8-b3f4-2b5e-a4a6-f622dc1f0ac2.png "スクリーンショット 2016-05-16 1.13.20.png")


実際の実装に関しては、<a href="https://docs.fabric.io/android/crashlytics/build-tools.html#gradle-advanced-setup" target="_blank">公式ホームページ</a>
に書いてあります。
初期化はアプリケーションクラスに書きます。

```Application.java
// Set up Crashlytics, disabled for debug builds
Crashlytics crashlyticsKit = new Crashlytics.Builder()
    .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
    .build()

// Initialize Fabric with the debug-disabled crashlytics.
Fabric.with(this, crashlyticsKit);
```

<br>
### 2. build.gradleをカスタマイズする。(配信のカスタマイズ)
GradleとはGroovyのDSLを利用したビルドシステムのことで、Android Studioに標準で採用されています。

### ・タスク一覧の確認
手順1でCrashlyticsを導入するとgradleにタスクが追加されています。
プロジェクトのrootで`gradle tasks`と打ちます。

![Kobito.GfFnav.png](https://qiita-image-store.s3.amazonaws.com/0/43762/422bca49-ce9a-4d2d-38d6-9338777bd408.png "Kobito.GfFnav.png")

crashlyticsで始まるタスクが4つ追加されています。
Distributionの後にはapkファイルの名前が表示されています。
もっというと、Build Variantsがそのまま表示されるようになっています。

Build Variantsとは"Product Flavor"と"Build type"の組み合わせで、最終的なapkファイルはここを元に作成されます。
上記の場合、以下の4パターンのapkファイルが作成できます。





|apk|Product Flavor|Build Type|
|:--:|:--:|:--:|
|apk1|production|release|
|apk2|production|debug|
|apk3|staging|release|
|apk4|staging|debug|


このタスクはビルド済みのapkファイルをBetaにアップロードするタスクです。
成功すると、Fabricのページで配信状況を確認できるようになります。


### ・配信の設定
また`配信グループ`と`リリースノート`を指定するためにはmanifestPlaceHoldersに追加します。

```build.gradle
productFlavors {

    staging {
        manifestPlaceholders = [
        　　 // 配信グループの指定
            ext.betaDistributionGroupAliases = "team_name" // groupsにaliasが表示されているので、それを利用
            // リリースノートの指定
            ext.betaDistributionReleaseNotes = getLatestCommitMessage() // 最新のコミットメッセージを取得
        ]    
    }

    production {
    }
}
```


最新のメッセージを取得する部分は以下に切り出し。
ファイル切り出したほうがいい気がしますが、今回はやっていません。

### ・リリースノート作成

1. 最後の配信日時がrelease_note.txtに記載されているので、その日時を取得(なければ、タスク終了後に追加)
2. 前回配信からの差分取得
3. 配信日時の更新

```
// 更新されたコミットを取得し、配信日時を更新
def getLatestCommitMessage() {
 　　// 最新配信日時を取得する。その情報はrelease_note.textに格納。
    def lastDistributedDate = new File("${project.rootDir}/release_note.txt").text.trim()
　　 // 最後のマージ日時からの差分コミットの取得 
    def gitLog = ["sh", "-c", "cd ${project.rootDir} ; git log マージ先ブランチ --pretty=format:\'%cn: %s\' --after=\'$lastDistributedDate\'"].execute().text
    // 最新のコミットの時刻を取得
    def updateCommit = ["sh", "-c", "cd ${project.rootDir} ; git log マージ先ブランチ -1 --format=\'%cI\'"].execute().in.text.trim()
    // release_note.txtに記載されている日時の更新
    def updateDate = new File("${project.rootDir}/release_note.txt")
    updateDate.write(updateCommit);
    // 何もない場合は
    if (gitLog.trim().length() > 0) return (gitLog)
    return 'git log -n 1'.execute().text
}
```
もし上記 git commandの部分でうまくいかなければ、CicleCIにsshで繋いで確認できます。(CircleCIのGUIから可能)
<br>
### 3. CircleCI(トリガーの設定)
CicleCIとはビルド、テストなどをトリガー発火で継続的に実行することです。
すぐ導入するためのコードも書いてます。<a href="http://qiita.com/neonankiti/items/eb7d40d41c8d197fce17" target="_blank">易しいCircle Ci for Android</a>

CircleCIがGithubにおいて、マージされたタイミングでCIが走り、テスト、ビルドが完了するとアプリが配信されます。

以下はそのまま適用できると思いますが、
deploymentのところに`マージ先のブランチ名`とその際に実行するタスクを追加します。
`./gradlew crashlyticsUploadDistributionStagingDebug`は`gradle tasks`で確認できるもののいずれかを書きます。(もちろん配信環境に合わせて)

```circle.yml
machine:
    java:
        version: openjdk7 # デフォルトはopenjdk7
    environment:
        ANDROID_HOME: ~/Applications/android-sdk-macosx # なくてもよい。                                                                                                     

dependencies:
    override:
        - echo y | android update sdk --no-ui --filter "android-23, build-tools-23.0.1" # デフォルトで導入されているパッケージは入れる必要なし。下記参照
test:
    override:
        - ./gradlew test
deployment:
    develop:
        branch: マージ先ブランチ名
        commands:
            - ./gradlew assembleDebug、
            - ./gradlew crashlyticsUploadDistributionStagingDebug # マージ先ブランチがdeployされたタイミングで実行されるタスク
```


上記の設定をすることによって、誰でも簡単にアプリを配信し、リリースノートをQAに伝達することが可能になります。


![Kobito.7R6LMT.png](https://qiita-image-store.s3.amazonaws.com/0/43762/c954c9a8-08c5-3732-ce1b-6d0d2fba6138.png "Kobito.7R6LMT.png")



うまくいきました。(内容はマスクしてます。)
補足ですが、上記リリースノートは`実装者の名前`と`PRのタイトル`を出すようにgit logをカスタマイズしています。
PRのタイトルにJIRAのチケットのurlを貼るオペレーションさえ徹底すれば、QA担当者が最適に作業しやすい状態を作れてみんな幸せになります。
(こちらの運用は圧倒的にミスが少なくなりました。)

## 最後に
プロダクトの規模が拡大するにつれて、いかに属人化をなくし、効率化を進めていくかということはこれからも直面し続ける課題になると思います。
Androidエンジニアの南里でした！