エクサウィザーズ Engineer Blog

株式会社エクサウィザーズのエンジニアチームブログ

GitHub 上で完結する、GitHub Actions + Fastlane をフル活用した iOS の継続的デリバリー

f:id:tadashi-nemoto0713:20210224120254p:plain

Platform Engineer (旧 DevOps Engineer) の 根本 征 です。

前回は GitHub Actions + GitLab Flow を使った API / Frontend のデプロイフローの改善について紹介しました。

techblog.exawizards.com

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 を採用しました。

f:id:tadashi-nemoto0713:20210118184341p:plain


この後の説明をより理解しやすくするために、簡単に Git Flow について説明します。

① 機能実装をする際は、develop ブランチから feature ブランチを作成し、作業を開始します。作業・レビューが完了したら develop ブランチに merge します。

f:id:tadashi-nemoto0713:20210316161849p:plain


② リリースの準備を行う段階で、develop ブランチから release ブランチを作成します。

f:id:tadashi-nemoto0713:20210212164923p:plain


③ release ブランチでリリースに必要な確認・修正を行い、リリースできる状態になったら master・develop ブランチに merge します。

f:id:tadashi-nemoto0713:20210212182725p:plain


④ リリースブランチを master ブランチに merge 後、Tag を付けて本番環境などにリリースします(モバイル開発だと App Store Connect / Google Play Console に申請)。

f:id:tadashi-nemoto0713:20210212182646p:plain


⑤ リリース後に深刻なバグが発見された場合には、Hotfix リリースを行います。master ブランチから Hotfix 用のリリースブランチを作成します。

f:id:tadashi-nemoto0713:20210212171006p:plain


⑥ Hotfix 用のリリースブランチ上で修正・確認が終わり、リリースできる状態になったら master・develop ブランチに merge して本番環境などに再度リリースします。

f:id:tadashi-nemoto0713:20210317122731p:plain


次からは、この Git Flow の中でどのように継続的デリバリーを実現したかについて解説します。

継続的デリバリーの解説

リリースブランチ・Pull Request の作成

① での develop ブランチ上での開発が進み、リリースの準備をするタイミングで ② で説明したリリースブランチの作成 を行います。

f:id:tadashi-nemoto0713:20210212164923p:plain
リリースブランチの作成


リリースブランチは手元で手動で作成することが多いのですが、今回は GitHub Actions + Fastlane を活用して自動化することができました。

まず、リリースバージョンの上げ方を Semantic Versioning に従います。Semantic Versioning 自体についてはここでは深く解説しませんが、Major・Minor・Patch によってバージョンの上げ方を規則化させることができます。

f:id:tadashi-nemoto0713:20210305142013j:plain:w200
Semantic Versioning

Fastlane には increment_version_number という Action があり、Major・Minor・Patch のいずれかを引数に渡してあげることで、Semantic Versioning に基づいてバージョンを上げてくれます。

そして、下記の Fastlane によって

  • Semantic Versioning を使ったリリースバージョンのアップデート、ファイルのコミット
  • リリースブランチの作成、GitHub への Push
  • master・develop ブランチへの Pull Request の作成

まで行うことができます。

f:id:tadashi-nemoto0713:20210315131519p:plain

increment_version_number の Fastlane Action 自体は現在 iOS のみ提供されていますが、下記の記事で Android での事例が紹介されていますので、参考にしてもらえればと思います。

Automating semantic versioning model in mobile releases | ThoughtWorks

最後にこの Fastlane を GitHub Actions 経由で実行させるようにします。

f:id:tadashi-nemoto0713:20210312182827p:plain

GitHub Actions では様々なワークフローのトリガーの種類がありますが、今回は任意のタイミングで実行させたいため、GitHub Action のUIから 手動でトリガーすることができる workflow_dispatch を利用します。

ワークフローをトリガーするイベント - GitHub Docs

workflow_dispatch では引数も与えることができるため、今回のバージョンアップは Major か Minor か渡せるようにします(Patch は Hotfix リリースのみに使うためここでは使いません)。

これによって、GitHub Actions の UI から Major か Minor を指定してワークフローをトリガーしてあげることによって、リリースバージョンのアップデート、リリースブランチ・Pull Request の作成まで自動化することができました。

f:id:tadashi-nemoto0713:20210314171333p:plain
GitHub Actions で workflow_dispatch を実行する

f:id:tadashi-nemoto0713:20210312183859p:plain
master・develop ブランチに向けられた Pull Request

f:id:tadashi-nemoto0713:20210302224859p:plain
バージョン部分のファイル差分

2つのリリース Pull Request を同時に merge する

先程のステップで、リリースブランチから master・develop ブランチに向けた 2 つの Pull Request が自動で作成されました。 そして ③ で説明した 通り、リリースブランチがリリースできる状態になったら master・develop ブランチに merge します。

f:id:tadashi-nemoto0713:20210212182725p:plain

もちろん手動で 2 つの Pull Request を merge することもできますが、どちらかを merge し忘れるという可能性が出てきます。 忘れることなく同時にこの 2 つの Pull Request を merge させるために、以下の GitHub Actions を作成しました。

f:id:tadashi-nemoto0713:20210312181035p:plain

この GitHub Actions によって、Pull Request に release という label を付けたら、develop と master ブランチに向けられた2つの Pull Request を同時に merge することができます。

f:id:tadashi-nemoto0713:20210314132857p:plain

Tag & GitHub Release の作成、App Store Connect へ申請

リリースブランチが develop・master ブランチに merge され、master ブランチに新しいコミットが入ると、Git Flow では ⑤ で述べた Tag の作成 & 本番環境へのリリース を行います。 iOS 開発の場合、このタイミングで App Store Connect に申請を出す(サブミット)ことが多いです。

f:id:tadashi-nemoto0713:20210212182646p:plain


master ブランチへのコミットをトリガーに Tag & GitHub Release の作成は下記の GitHub Actions で自動化することができます。

f:id:tadashi-nemoto0713:20210314142612p:plain

f:id:tadashi-nemoto0713:20210314172613p:plain
Tag & GitHub Release の作成

また、このタイミングでリリース版アプリのビルド・App Store Connect へのアップロードを行います。

f:id:tadashi-nemoto0713:20210314172439p:plain

ここでは Fastlane の Deliver Action を活用しています。

Deliver Action の設定によって、Apple Store Connect へのアップロードだけでなく、メタデータ・スクリーンショットのアップロード、申請(サブミット)、承認後の自動リリースまで行うことができます。

Hotfix リリース

上記までが、通常のリリースフローになります。

通常のリリース以外に、リリース後に深刻なバグが発見された場合には Hotfix リリースを行います。 ⑤ と ⑥ の Step に従って、master ブランチから Hotfix 用のリリースブランチを作成をし、修正後 master・develop ブランチに merge してリリースします。

f:id:tadashi-nemoto0713:20210212171006p:plain


Hotfix リリースを行う際には、Semantic Versioning に従って Patch(x.x.0 → x.x.1) のみのアップデートになります。

f:id:tadashi-nemoto0713:20210305135019p:plain

そして、GitHub Actions では master ブランチからチェックアウトし、Hotfix 用のリリースブランチ・Pull Request の作成が行われるようにします。

f:id:tadashi-nemoto0713:20210314145833p:plain

その後、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 した際 や、リリースブランチにコミットがあった際にトリガーするのがよさそうです。

f:id:tadashi-nemoto0713:20210212182754p:plain


GitHub Actions でのワークフローは下記のようになります。

f:id:tadashi-nemoto0713:20210305134944p:plain

上記のワークフローでは、トリガーとして特定のブランチへのコミットの他に、workflow_dispatch を用意しています。

① での機能開発の最中に、「develop・release ブランチにまだ merge したくないけど特定の feature ブランチをビルド・配布したい」という状況はよく起こると思います。

f:id:tadashi-nemoto0713:20210317125311p:plain


GitHub Actions の workflow_dispatch を使うことで、GitHub Actions の UI からブランチを指定した上で手動でトリガーすることが可能です。

f:id:tadashi-nemoto0713:20210212184657p:plain
workflow_dispatch

継続的デリバリーの手順まとめ・おわりに

最後にこれまでの GitHub Actions を活用した継続的デリバリーの手順をまとめます。

【通常のリリースフロー】

  1. feature ブランチを作成, develop ブランチにマージ

  2. リリースを準備するタイミングで GitHub Actions の UI から、リリース準備のためのワークフローを実行(Major か Minor を選択) f:id:tadashi-nemoto0713:20210314171333p:plain

  3. リリースバージョンのアップデート(Major か Minor)、リリースブランチの作成、master・develop ブランチへの Pull Request の作成がされる f:id:tadashi-nemoto0713:20210312183859p:plain

  4. このタイミングで、検証版アプリのビルド・Firebase App Distribution への配布もされるため、実機などで動作確認をする

  5. リリースできるタイミングになったら、Pull Request に release の label を付け、develop・master ブランチへ同時に merge される f:id:tadashi-nemoto0713:20210314132857p:plain

  6. master ブランチにコミットが入り、自動で Tag & GitHub Release の作成、そして App Store Connect へ申請される f:id:tadashi-nemoto0713:20210314172613p:plain


【Hotfix リリース】

  1. Hotfix リリースを準備するタイミングで GitHub Actions の UI から、Hotfix リリース準備のためのワークフローを実行
  2. リリースバージョンのアップデート(Patch)、リリースブランチの作成、master・develop ブランチへの Pull Request の作成がされる
  3. 上記のリリースブランチに対して不具合修正をコミットする
  4. リリースブランチへのコミット毎に自動で検証版アプリのビルド・Firebase App Distribution へ配布されるので、実機などで動作確認
  5. 修正を確認したら、Pull Request に release の label を付け、develop・master ブランチへ同時に merge される
  6. master ブランチにコミットが入り、自動で Tag & GitHub Release の作成、そして App Store Connect へ申請される


これによって、リリースに必要な作業をほぼGitHub上で完結させることができました
(Fastlane の Deliver アクションによって、Appl Store Connect への作業をどのぐらい自動化するかにもよりますが)

モバイルアプリ開発におけるリリースは、APIやフロントエンド開発と比較してリリース頻度は低くなりがちなものの、リリースのために必要な作業は多くなりがちです。

この継続的デリバリーによって、

  • より俊敏にアプリの改善を市場にリリースできるようになる
  • 開発者がプロダクト開発によりフォーカスできるようになる

ことを期待しています。

hrmos.co

参考・注意点