Platform Engineer (旧 DevOps Engineer) の 根本 征 です。
前回は GitHub Actions + GitLab Flow を使った API / Frontend のデプロイフローの改善について紹介しました。
iOS の継続的デリバリーも GitHub Actions を活用して改善することができたので、今回はその内容を紹介したいと思います。
iOS のみならず、Android 開発 や Flutter などのマルチプラットフォーム開発での継続的デリバリーにも応用できると考えています。
Git Flow の採用・Git Flow の簡単な説明
前回の記事では、API / Frontend のデプロイフロー・ブランチ戦略において GitLab Flow を採用したと述べました。 しかしモバイルアプリのリリースには、下記の要素があり GitLab Flow や GitHub Flow と相性が悪いと考えました。
- リリース毎に App Store Connect / Google Play Console で市場に出ているバージョンより上げる必要がある
- リリースには審査が必要になり、恣意的にいつでもリリースできる状況ではない
また、モバイルアプリのリリースフローとしては他にトランクベース開発などを採用しているプロダクトもあると思います。 しかしトランクベース開発は、1週に1回必ずリリースするなどという比較的大規模な開発向きで、弊社のモバイル開発の現状とは合わないと考えました。
上記の理由から今回は、モバイルアプリのデプロイフロー・ブランチ戦略で多く採用されている Git Flow を採用しました。
この後の説明をより理解しやすくするために、簡単に Git Flow について説明します。
① 機能実装をする際は、develop ブランチから feature ブランチを作成し、作業を開始します。作業・レビューが完了したら develop ブランチに merge します。
② リリースの準備を行う段階で、develop ブランチから release ブランチを作成します。
③ release ブランチでリリースに必要な確認・修正を行い、リリースできる状態になったら master・develop ブランチに merge します。
④ リリースブランチを master ブランチに merge 後、Tag を付けて本番環境などにリリースします(モバイル開発だと App Store Connect / Google Play Console に申請)。
⑤ リリース後に深刻なバグが発見された場合には、Hotfix リリースを行います。master ブランチから Hotfix 用のリリースブランチを作成します。
⑥ Hotfix 用のリリースブランチ上で修正・確認が終わり、リリースできる状態になったら master・develop ブランチに merge して本番環境などに再度リリースします。
次からは、この Git Flow の中でどのように継続的デリバリーを実現したかについて解説します。
継続的デリバリーの解説
リリースブランチ・Pull Request の作成
① での develop ブランチ上での開発が進み、リリースの準備をするタイミングで ② で説明したリリースブランチの作成 を行います。
リリースブランチは手元で手動で作成することが多いのですが、今回は GitHub Actions + Fastlane を活用して自動化することができました。
まず、リリースバージョンの上げ方を Semantic Versioning に従います。Semantic Versioning 自体についてはここでは深く解説しませんが、Major・Minor・Patch によってバージョンの上げ方を規則化させることができます。
Fastlane には increment_version_number という Action があり、Major・Minor・Patch のいずれかを引数に渡してあげることで、Semantic Versioning に基づいてバージョンを上げてくれます。
そして、下記の Fastlane によって
- Semantic Versioning を使ったリリースバージョンのアップデート、ファイルのコミット
- リリースブランチの作成、GitHub への Push
- master・develop ブランチへの Pull Request の作成
まで行うことができます。
increment_version_number の Fastlane Action 自体は現在 iOS のみ提供されていますが、下記の記事で Android での事例が紹介されていますので、参考にしてもらえればと思います。
Automating semantic versioning model in mobile releases | ThoughtWorks
最後にこの Fastlane を GitHub Actions 経由で実行させるようにします。
GitHub Actions では様々なワークフローのトリガーの種類がありますが、今回は任意のタイミングで実行させたいため、GitHub Action のUIから 手動でトリガーすることができる workflow_dispatch を利用します。
ワークフローをトリガーするイベント - GitHub Docs
workflow_dispatch では引数も与えることができるため、今回のバージョンアップは Major か Minor か渡せるようにします(Patch は Hotfix リリースのみに使うためここでは使いません)。
これによって、GitHub Actions の UI から Major か Minor を指定してワークフローをトリガーしてあげることによって、リリースバージョンのアップデート、リリースブランチ・Pull Request の作成まで自動化することができました。
2つのリリース Pull Request を同時に merge する
先程のステップで、リリースブランチから master・develop ブランチに向けた 2 つの Pull Request が自動で作成されました。 そして ③ で説明した 通り、リリースブランチがリリースできる状態になったら master・develop ブランチに merge します。
もちろん手動で 2 つの Pull Request を merge することもできますが、どちらかを merge し忘れるという可能性が出てきます。 忘れることなく同時にこの 2 つの Pull Request を merge させるために、以下の GitHub Actions を作成しました。
この GitHub Actions によって、Pull Request に release という label を付けたら、develop と master ブランチに向けられた2つの Pull Request を同時に merge することができます。
Tag & GitHub Release の作成、App Store Connect へ申請
リリースブランチが develop・master ブランチに merge され、master ブランチに新しいコミットが入ると、Git Flow では ⑤ で述べた Tag の作成 & 本番環境へのリリース を行います。 iOS 開発の場合、このタイミングで App Store Connect に申請を出す(サブミット)ことが多いです。
master ブランチへのコミットをトリガーに Tag & GitHub Release の作成は下記の GitHub Actions で自動化することができます。
また、このタイミングでリリース版アプリのビルド・App Store Connect へのアップロードを行います。
ここでは Fastlane の Deliver Action を活用しています。
Deliver Action の設定によって、Apple Store Connect へのアップロードだけでなく、メタデータ・スクリーンショットのアップロード、申請(サブミット)、承認後の自動リリースまで行うことができます。
Hotfix リリース
上記までが、通常のリリースフローになります。
通常のリリース以外に、リリース後に深刻なバグが発見された場合には Hotfix リリースを行います。 ⑤ と ⑥ の Step に従って、master ブランチから Hotfix 用のリリースブランチを作成をし、修正後 master・develop ブランチに merge してリリースします。
Hotfix リリースを行う際には、Semantic Versioning に従って Patch(x.x.0 → x.x.1) のみのアップデートになります。
そして、GitHub Actions では master ブランチからチェックアウトし、Hotfix 用のリリースブランチ・Pull Request の作成が行われるようにします。
その後、Hotfix 用のリリースブランチ上で修正・確認が終わった後は、通常のリリースと同じく
- Pull Request に release の label を付け、master・develop ブランチに merge
- master ブランチにコミットが入り、自動で Tag & GitHub Release の作成、そして App Store Connect へ申請
という手順でリリースしていくことができます。
【任意】検証版アプリの配布(Firebase App Distribution)
Git Flow とは直接関係しませんが、GitHub Actions を使った検証版アプリの配布についても簡単に解説します。
リリース前にアプリの動作確認をするために、よく下記のサービスなどを活用して検証版アプリの配布・動作確認を行います。
そして、検証版アプリのビルド・アップロード作業を GitHub Actions を使って自動化することができます。 Git Flow だと ① の feature ブランチでの機能開発が終わって develop ブランチに merge した際 や、リリースブランチにコミットがあった際にトリガーするのがよさそうです。
GitHub Actions でのワークフローは下記のようになります。
上記のワークフローでは、トリガーとして特定のブランチへのコミットの他に、workflow_dispatch を用意しています。
① での機能開発の最中に、「develop・release ブランチにまだ merge したくないけど特定の feature ブランチをビルド・配布したい」という状況はよく起こると思います。
GitHub Actions の workflow_dispatch を使うことで、GitHub Actions の UI からブランチを指定した上で手動でトリガーすることが可能です。
継続的デリバリーの手順まとめ・おわりに
最後にこれまでの GitHub Actions を活用した継続的デリバリーの手順をまとめます。
【通常のリリースフロー】
feature ブランチを作成, develop ブランチにマージ
リリースを準備するタイミングで GitHub Actions の UI から、リリース準備のためのワークフローを実行(Major か Minor を選択)
リリースバージョンのアップデート(Major か Minor)、リリースブランチの作成、master・develop ブランチへの Pull Request の作成がされる
このタイミングで、検証版アプリのビルド・Firebase App Distribution への配布もされるため、実機などで動作確認をする
リリースできるタイミングになったら、Pull Request に release の label を付け、develop・master ブランチへ同時に merge される
master ブランチにコミットが入り、自動で Tag & GitHub Release の作成、そして App Store Connect へ申請される
【Hotfix リリース】
- Hotfix リリースを準備するタイミングで GitHub Actions の UI から、Hotfix リリース準備のためのワークフローを実行
- リリースバージョンのアップデート(Patch)、リリースブランチの作成、master・develop ブランチへの Pull Request の作成がされる
- 上記のリリースブランチに対して不具合修正をコミットする
- リリースブランチへのコミット毎に自動で検証版アプリのビルド・Firebase App Distribution へ配布されるので、実機などで動作確認
- 修正を確認したら、Pull Request に release の label を付け、develop・master ブランチへ同時に merge される
- master ブランチにコミットが入り、自動で Tag & GitHub Release の作成、そして App Store Connect へ申請される
これによって、リリースに必要な作業をほぼGitHub上で完結させることができました
(Fastlane の Deliver アクションによって、Appl Store Connect への作業をどのぐらい自動化するかにもよりますが)
モバイルアプリ開発におけるリリースは、APIやフロントエンド開発と比較してリリース頻度は低くなりがちなものの、リリースのために必要な作業は多くなりがちです。
この継続的デリバリーによって、
- より俊敏にアプリの改善を市場にリリースできるようになる
- 開発者がプロダクト開発によりフォーカスできるようになる
ことを期待しています。
参考・注意点
- Using Github Actions to Automate Our Release Process – Rebecca Franks - @riggaroo
Automating semantic versioning model in mobile releases | ThoughtWorks
この記事で紹介した、GitHub Actions のワークフローは セルフホストランナー を活用しているため、GitHub ホストランナーと記述が異なる部分があります。
- 一部の GitHub Actions のワークフローでは、他のワークフローをトリガーさせるために 個人アクセストークンを利用 しています。