最近リリースされたCircleCI 2.0でAndroidライブラリをデプロイするまでをやってみたので、そのまとめ。
## 概要
今回CircleCIに対応させたのは以下のライブラリ
ChatMessageView
今回自動化したかったフローは
- 修正をGitHubへPush
- CircleCIでビルド&テスト
- Jfrog Bintrayにアップロード
- GitHubに自動でタグをPush
- READMEに記載しているライブラリのバージョンを書き換える
手順
1. CircleCIに登録
GitHubのアカウントがあれば、連携は簡単。
https://circleci.com/
2. プロジェクトの追加
サインアップが完了すると、PROJECTSタブからGitHubにあるレポジトリが読み込まれるので選択して追加できる。
レポジトリを選択するとAndroidの場合、デフォルトでOSはLinux、Platformは2.0,LanguageはGradle(Java)になっているのでデフォルト設定のままでOK。
3. プロジェクトに.circleci/config.yml
を追加
CircleCI 2.0では.circleci
配下にconfig.yml
を置いて、そこに設定を書く。
レポジトリ連携後にサンプルのymlファイルが書いてあるのでそれを雛形にするとよさそう。
今回の設定内容は以下
https://github.com/bassaer/ChatMessageView/blob/master/.circleci/config.yml
version: 2
jobs:
build:
branches:
only:
- master
- develop
docker:
# specify the version you desire here
- image: circleci/android:api-26-alpha
environment:
# Customize the JVM maximum heap limit
JVM_OPTS: -Xmx3200m
TERM: dumb
steps:
- checkout
# Download and cache dependencies
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "chatmessageview/build.gradle" }}
- run:
name: update android sdk
command: |
echo y | android update sdk --no-ui --all --filter tool,extra-android-m2repository,extra-google-google_play_services,extra-google-m2repository,android-26
echo y | android update sdk --no-ui --all --filter build-tools-26.0.0
- run:
name: gradle dependencies
command: ./gradlew androidDependencies
- save_cache:
paths: ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "chatmessageview/build.gradle" }}
# run unit tests!
- run:
name: unit test
command: |
./gradlew test
./gradlew lint test
- run:
name: Show list of system-images
command: sdkmanager --list --verbose | grep system-images
- run:
name: Setup Emulator
command: sdkmanager "system-images;android-24;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-24;default;armeabi-v7a"
- run:
name: Launch Emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on
background: true
- run:
name: Wait emulator
command: |
circle-android wait-for-boot
adb shell input keyevent 82
- run:
name: Run UI test
command: ./gradlew example:connectedAndroidTest
- run:
name: Distribute Bintray
command: |
if [ $CIRCLE_BRANCH = "master" ]; then
sh ./deploy.sh
else
echo "Bintray distribution was skipped."
fi
- run:
name: Update version
command: |
if [ $CIRCLE_BRANCH = "master" ]; then
sh ./version.sh
else
echo "Version update was skipped."
fi
- store_test_results:
path: example/build/test-results
- store_artifacts:
path: example/build/reports
destination: reports
今回の主な設定
masterブランチとdevelopブランチ以外のプッシュではCircleCIが走らないようする。
branches:
only:
- master
- develop
使用するAndroid APIのバージョンを設定
docker:
# specify the version you desire here
- image: circleci/android:api-26-alpha
ここからは実際に動作させたいタスクを以下のように記述していく
- run:
name: タスク名
command: 実行したいコマンド
- run:
name: 複数のコマンドがある場合
command: |
command1
command2
sdkのアップデート
build.gradleで設定しているbuildToolsVersion等と合わせるるとよさそう。
- run:
name: update android sdk
command: |
echo y | android update sdk --no-ui --all --filter tool,extra-android-m2repository,extra-google-google_play_services,extra-google-m2repository,android-26
echo y | android update sdk --no-ui --all --filter build-tools-26.0.0
ユニットテストとLintチェックを実行
- run:
name: unit test
command: |
./gradlew test
./gradlew lint test
ここでは使えるsystem-imagesのリストを確認したかっただけなので本来は不要。
- run:
name: Show list of system-images
command: sdkmanager --list --verbose | grep system-images
emulatorを起動してEspressoでテストしたい場合は以下でemulatorの設定を行う。
今回はarmeabi-v7a
が使えるのは24までで、別のsystem-imageだと起動できないことがあるのでひとまず24でセットアップ。
- run:
name: Setup Emulator
command: sdkmanager "system-images;android-24;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-24;default;armeabi-v7a"
エミュレータの起動。
- run:
name: Launch Emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on
background: true
エミュレータの起動に時間がかかるので待機。
スクリーンがロックされている場合があるのでadbのキーイベントを送って解除する。
公式ページには、この方法よりテストコードにスクリーン設定を記述するほうがよさそうと書いてあるが、 テストコードに記述した場合にもemulatorに接続できず失敗することがあったので、ひとまず両方記述。
- run:
name: Wait emulator
command: |
circle-android wait-for-boot
adb shell input keyevent 82
UIテスト実行。結構時間かかる。
- run:
name: Run UI test
command: ./gradlew example:connectedAndroidTest
ここまででビルド&テストは終了。
あとはJfrog Bintrayにデプロイする。
このあたりの処理はシェルスクリプトにまとめておくと管理しやすい。
以下では、CircleCIで用意された環境変数 $CIRCLE_BRANCH
で実行中のブランチがわかるので、masterでテストまで成功したらdeploy.sh
を実行する。
- run:
name: Distribute Bintray
command: |
if [ $CIRCLE_BRANCH = "master" ]; then
sh ./deploy.sh
else
echo "Bintray distribution was skipped."
fi
最後に、デプロイまでできたら、READMEに記載されているバージョンの書き換えとタグをGitHubへプッシュする。
上記と同様に一連の処理をシェルスクリプトにまとめて実行するだけ。
バージョンはbuild.gradle
でも読み込みやすいようにversion.properties
というファイルで管理している。
version=1.4.0
READMEには以下のようにバージョン名を記述していたので、
dependencies {
compile 'com.github.bassaer:chatmessageview:1.3.5'
}
バージョンの1.3.5
の部分をsedコマンドで置換する。
#!/bin/sh
VERSION=`cat version.properties | cut -d'=' -f2`
sed -i "/compile/s/[0-9]*\.[0-9]*\.[0-9]*/$VERSION/" ./README.md
以下は生成したタグとREADMEの変更をGitHutへプッシュする処理。
ポイントはコミットメッセージに[ci skip]
を含めること。
これによってCircleCIが回り続けることを回避できる。
# 続き
REPOSITORY="git@github.com:${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}.git"
git config --global user.name "CircleCI"
git config --global user.email "app.nakayama@gmail.com"
git add ./README.md
git commit -m "update version [ci skip]"
git tag -a $VERSION -m "version $VERSION"
git push origin master
git push origin --tags
4. SSHキーの設定
Circle CI連携時にGitHubの対象レポジトリに自動で読み取り専用の鍵が設定されるが、書き込みも行いたい場合は、
PROJECTS
> 対象プロジェクトのSettings
> SSH Permissions
> Add SSH Key
から鍵の追加を行うことができる。
5. Status Budge
PROJECTS
> 対象プロジェクトのSettings
> Status Budges
からステータスバッジを生成してくれるのでREADMEなどに記載できる。
最後に
1.0からすこし書き方が変わっているので以下を参考にすると2.0へ移行しやすい。
https://circleci.com/docs/2.0/migrating-from-1-2/
emulator周りの設定で、同じ設定なのにemulatorが起動できなかったり、テストが失敗することがあった。
ダッシュボードからもう一度走らせると通ったりするので、謎。🤔
今回はひとまずデプロイまで自動でできる設定を行ったが、CircleCIはpushされたブランチをビルドするみたいなので、JenkinsみたいにPullRequestをトリガーに起動する方法もあるのか調査したい。