前回の記事
何をGithub Actionsで処理して、何をCircleCIで処理するか
どうやって実現するかを前回書きました
今回は実際にコードを見ながらどう実現するかを解説していきます。
おさらい
まずおさらいです。
Github Actions
ビルド
Lintチェック
CircleCI
テスト
Slackへの通知
自動マージ
デプロイ
※僕はAndroidエンジニアのためビルド等は基本Androidコマンドを叩いています。ご了承ください。
※Github Actions, CircleCIの細かい文法に関しては説明しません。
ビルド
name: Build
on:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew compileDebugSources
ここは簡単
純粋にGithub Actionsの処理を書いてあげればOK
Lintチェック
name: Build
on:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew lintDebug
本当はマルチモジュールでかつDangerを使ってGithubのコメントに結果を表示していますが、ここでは割愛
詳しくは以下の記事へ
https://qiita.com/dosukoi_android/items/75b9fe01aa021296d586
テスト
ここからが本題
先にGithub Actionsのワークフローを全て掲載し最後にCircleCIのワークフローを掲載します(CircleCIはconfig.ymlで一元管理しているため)
今回はレビュワーがapproveをしたらテストを走らせます。
Github Actions
name: UnitTest
on:
pull_request_review:
types: [ submitted ]
jobs:
test:
# ここでapproveされたらというハンドリング&Git-flowを使用している場合はブランチ名でもハンドリング
if: ${{ github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'master' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Call CircleCI API
# ここでCircleCIのAPIを叩く
run: |
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{ "branch": "master", "parameters": { "build_variant": "dev", "task": "test", "pull_request_title": "${{ github.event.pull_request.title }}", "pull_request_html_url": "${{ github.event.pull_request.html_url }}", "pull_request_user": "${{ github.event.pull_request.user.login }}", "pull_request_url": "${{ github.event.pull_request.url }}", "pull_request_sha": "${{ github.event.pull_request.head.sha }}"}}' \
https://circleci.com/api/v2/project/github/${{ github.event.repository.full_name }}/pipeline
デプロイ
Github Actions
name: Deploy
on:
pull_request:
types: [closed]
branches: [master]
jobs:
build:
# PRがcloseかつmergedがtrueの時だけ→つまりマージされた時だけ起動
if: github.event.pull_request.merged == true
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Call CircleCI API
run: |
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Circle-Token: ${{ secrets.CIRCLE_TOKEN }}" \
-d '{ "branch": "master", "parameters": { "build_variant": "dev", "task": "deploy", "pull_request_title": "${{ github.event.pull_request.title }}" } }' \
https://circleci.com/api/v2/project/github/${{ github.event.repository.full_name }}/pipeline
CircleCI
version: 2.1
parameters:
# これでどのジョブを起動するかハンドリング
task:
type: enum
enum: ["deploy", "test"]
default: "deploy"
# 環境ごとにも変えられる
build_variant:
type: enum
enum: ["dev", "stg", "production"]
default: "dev"
# ここから下はSlack通知で使用
pull_request_title:
type: string
default: ""
pull_request_html_url:
type: string
default: ""
pull_request_url:
type: string
default: ""
pull_request_user:
type: string
default: ""
pull_request_sha:
type: string
default: ""
executors:
android:
docker:
- image: circleci/android:api-30
environment:
JVM_OPTS: -Xmx1536m
GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx1536m -XX:+HeapDumpOnOutOfMemoryError" -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false'
orbs:
android: circleci/android@0.2.1
jobs:
deploy:
executor: android
steps:
- checkout
- run:
# aabファイルを作成する
name: Build with Gradle
command: |
ENV=(<< pipeline.parameters.build_variant >>)
ENV_UPPER_CASE=${ENV[@]~}
./gradlew ":navigation:phone:bundle${ENV_UPPER_CASE}Release"
- run:
# DeployGateのAPIを叩く
name: Distribute App
command: |
curl \
-H "Authorization: token $DEPLOY_GATE_API_KEY" \
-F "file=@navigation/phone/build/outputs/bundle/<< pipeline.parameters.build_variant >>Release/phone-<< pipeline.parameters.build_variant >>-release.aab" \
-F "message=<< pipeline.parameters.pull_request_title >>" \
-v "https://deploygate.com/api/users/$DEPLOY_GATE_USER_NAME/apps"
test:
executor: android
steps:
- checkout
- run:
name: Unit Test
command: |
ENV=(<< pipeline.parameters.build_variant >>)
ENV_UPPER_CASE=${ENV[@]~}
./gradlew "test${ENV_UPPER_CASE}DebugUnitTest"
- run:
# 失敗したらSlack通知
when: on_fail
name: Unit Test Failure Notification
command: |
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"attachments": [{"color": "#D73A49", "title": "<< pipeline.parameters.pull_request_title >>", "title_link": "<< pipeline.parameters.pull_request_html_url >>", "text": "Failure Unit Test", "author_name": "<< pipeline.parameters.pull_request_user >>"}]}' \
$SLACK_WEBHOOK
auto_merge:
executor: android
steps:
- run:
name: Wait For Status Check
command: sleep 5s
- run:
# GithubのマージのAPIを叩く
name: Auto Merge
command: |
curl \
-X PUT \
-H "Authorization: token $PERSONAL_ACCESSTOKEN" \
-H "Content-Type: application/json" \
-d '{"sha": "<< pipeline.parameters.pull_request_sha >>", "merged": "true", "message": "Pull Request successfully merged"}' \
"<< pipeline.parameters.pull_request_url >>/merge"
workflows:
version: 2.1
build_and_deploy:
# APIを叩くときのパラメータがdeployの時にこのジョブを起動
when:
equal: [deploy, << pipeline.parameters.task >>]
jobs:
- deploy:
name: Deploy
test:
# APIを叩くときのパラメータがtestの時にこのジョブを起動
when:
equal: [test, << pipeline.parameters.task >>]
jobs:
- test
- auto_merge:
name: Auto Merge
requires:
- test
# testというジョブが終わったらauto_mergeというジョブを起動
まとめ
以上となります。
ビルドからデプロイまでほぼ自動化し、マンパワーで行うことはレビューだけです。
コンフリクトさえしてなければ、レビュワーがLGTM!とするだけで、テスト、マージ、デプロイまで全て自動化してくれます。
自動化って男のロマンじゃないですか??