概要
STYLYは多数のハードウェアに対応したXRのサービスです。STYLYでは、UnityクライアントのソースコードにPRが作成されるたびに、Github Actionsでworkflowを起動し、それによりUnityBuildAutomationへプロジェクト登録を行い、ビルド・テスト環境を構築しています。今、構築し運用しているシステムでは、9種のプラットフォームにおいて、ビルド・テストする環境が整備されています。また、主要なプラットフォームであるスマホ版STYLYにおいては、AirTest+PocoによるAndroid,iOSの両方を実機で自動テストする環境を構築しました。また、Codacyというサービスと連携し、ソースコードの静的解析を常時行っています。
基本アーキテクチャ
STYLYのCIの基本アーキテクチャは私(@kotauchisunsun)が作りました。主な流れは、
①PRの作成をトリガーに、
②GithubActionsが発火
③ビルドを作るUnityBuildAutomation(旧UnityCloudBuild)のConfigを作成
④UnityBuildAutomationのビルド・テスト結果のWebhookの通知をAWSのLambdaで受けとり、
⑤テスト結果を該当のPRへコメントとして書き込む
という風なステップになっています。この仕組みは2022年あたりに作成し始めました。もともとなぜこのようなものを作ったのかというと、「STYLYのクライアントのリリースがほぼ手動であることに問題意識を感じた」ためです。STYLYではUnityCloudBuildのデイリービルドを使っており、一部のリリースビルドは自動化されていました。しかし、私はそれだけではなくもっと早い段階で製品のチェックをすべきである。いわゆる「シフトレフト」をすべきであると考えていました。そのため、PRが作られた段階でUnityCloudBuild上に設定を作成し、ビルド・自動テストが行われる仕組みを構築することを考えました。
3.早期テストで時間とコストを節約
早い段階で欠陥を見つけるために、静的テスト活動と動的テスト活動の両方をソフトウェア開発ライフサイクルのなるべく早い時期に開始すべきである。早期テストは、シフトレフトとも呼ばれる。ソフトウェア開発ライフサイクルの早い時期にテストを行うことにより、コストを低減または削減できる
なぜ混成構成なのか
GithubActionsで、Unityをビルドする方式だとGameCIによる先行例があります。
【Unity】GitHub Actions × Game CIでWebGLのCI/CD環境構築(デプロイ先はNetlify)
しかし、これにはUnityのライセンスの問題が絡みます。この方式は、UnityのProライセンスを手動アクティベーションするためのラインセンスをGithubActionsへ登録をします。しかし、UnityProのライセンスは1つあたり2台のコンピュータにしか入れられない制約があります。そのため、「GithubActionsのように毎回インスタンスが変わる(と思われる)環境下にインストールしてよいのか」「単一ライセンスで並列にビルドしたとき、どういう動作をするのか。ライセンス違反ではないのか。」といったところに問題が発生します。こういった問題を解決するためにBuild ServerやFloating Licenseがありますが、ライセンス契約がさらに必要になるなど別の要因が生まれます。
また、素のUnityCloudBuildで良いのではないか?という疑問もあると思います。これはなぜそうなっていないかというと、要件が「PRごとにビルドを作り、テストを行いたい」というのが目的であったためです。確かに、UnityCloudBuildには、ブランチを指定して、Githubのプッシュごとにフックし、ビルドを作成する機能はあります。しかし、それはあくまでブランチを指定してConfigを手動で作る必要があり、欲しい機能は「PRを作ったときに同時にビルドやテストが自動で走ってほしい」という内容でした。そのため、UnityCloudBuildの素のままの機能では難しいという認識でした。
Unity分野では社内でJenkinsを立てる風潮もあるのですが、Jenkins自体を保守するのも大変で、なおかつ対応プラットフォームが10以上あり、それぞれに専用で仕組みを作るのも大変であるという認識でした。そのため、Jenkinsの採用は見送りました。
一方で、先述したようにSTYLYでは一部のクライアントのリリースにデイリービルドを設定しているものがありました(@hammmm @afjk @hacha_)。そのため、「UntyCloudBuild自体にSTYLYをビルドするだけのポテンシャルはある」という認識でした。そのため、「UnityCloudBuildの設定をGithubActionsから行えないか?」というのが次の問題でした。
Cloud Build APIの奇手
これはマニアックな話になりますが、UnityCloudBuildはREST APIが公開されています。
しかも、OpenAPIによる仕様書も公開されているため、openapi-generatorなどでクライアントを生成し、ConfigやProjectを自由自在に作ることが可能です。STYLYではGithubActionsから利用しやすいようにbash用のクライアントを生成し、それをコマンドとして呼び出しています。
ただし一部最新のオプションにはOpenAPIの仕様書が対応していない場合があります。過去にUnityCloudBuildがWindows Runnerに対応したとき、これを再現するためのオプションはOpenAPI上には存在しませんでした。しかし、UnityCloudBuildのデフォルトのBuilderがWindowsとなってしまい、今までMacのBuilderを想定していて動いていたCI部分が失敗するようになってしまいました。このような場合、ブラウザのNetworkInspectorを確認するのが重要です。UnityCloudBuild上でリクエストされる内容と、OpenAPIでリクエストされる内容はほぼ互換になっています。そのため、UnityCloudBuildのWebUI上でConfigを保存したときのリクエストボディを解析することで、OpenAPI上の仕様書にはない隠しパラメーターを探すことが可能で、それを利用してUnityCloudBuildの隠し挙動を調整することが可能です。
テスト結果通知の仕組み
基本的なCIの仕組みの図を再掲しますが、STYLYでは少々複雑なCI構成をしています。この理由は、「テスト結果をPR上に通知したかった」という理由があります。
UnityCloudBuild上で自動ビルドし、テストが動く部分までは先ほどのCloud BuildのREST APIで実装することが可能です。しかし、その結果の通知はできません。
STYLYで、私は「シフトレフト」を進めたい。と考えていました。そのために、何が必要か。と考えた場合、それは「フィードバック」であるということでした。「ビルドが作成できる」というのは当たり前であり、そのうえで、「テストを動かし、その結果を開発者に認知させる必要がある。」と考えていました。そうでなければ、「いつの間にかビルドが壊れている」「いつのまにかテストが通らなくなっている」といった事態が起こり、先々においてCIの廃墟化が待っている。と思っていました。そのため、「テストが通ったか」という通知は最初から作るべきであると思っていました。
この通知する仕組み自体はかなり巧妙なテクニックを利用しています。一番のボトルネックとなるのはUnityCloudBuildのWebHookです。UnityCloudBuildではビルドの成功時、失敗時にWebHookを呼び出す機能があります。このWebHookをAWSのlambdaで受け取るように設定します。(しかし、このWebHokの仕様書が存在しないため、根気強くビルドを繰り返しデバッグを行います。)大きく問題となるのが、UnityCloudBuild上のConfigとGithub上のPRの紐づけです。UnityCloudBuildのConfigはGithubActions上から作ってはいますが、PRとの紐づけ情報は持ちません。したがって、AWSのlambdaに送られるwebhookの情報にもPRの情報を送る方法は基本的にはありません。したがって、UnityCloudBuildでビルドの成功・失敗を検知ができて、それをAWSのlambdaに送ったとしても、もともとのPRへその成否を書きこむ術はありません。これをどのように回避しているかというと、UnityCloudBuildのConfigのTargetNameに工夫をしています。画像の例であれば、vr1234というのが、その部分にあたり、vrがプラットフォームの種別で、1234がPR番号にあたります。こういったTargetNameのConfigをGithubActionsから作成します。このTargetNameの情報はwebHookに保持されるためlambda上にも情報が渡されます。lambdaでは、このTargetNameをパースし、vrであればsteam版とし、PRの番号が1234にテストの成否を書きこむ。という挙動を実装しています。あとは、vrの部分をXRealやPICOなどに拡張していくことで、それぞれのプラットフォームでのテスト結果を書きこめるようになります。このようにTargetName部にプラットフォームの種別とPR番号を埋め込むことによりGithubのPRにまでテスト結果をフィードバックできる仕組みになっています。
歴史的な変遷
現在のSTYLYのCI環境は巨大になりつつありますが、私が全部作った。というわけではありません。主に私(@kotauchiusunsun)が作ったのは、基本アーキテクチャの部分(GithubActions→UnityCloudBuild→lambda)とSteamの部分、あとはSTYLY Mobile(スマホ版STYLY)の8系(現在は10系)の自動テストを不完全に作りました。あと同時にCodacyによる自動レビューの仕組みを導入したのが2021年5月ぐらいの時期だったと思います。
そのあと、2022年8月ごろに @segur が頑張っていたようです。当時はSTYLY Moblie 8系が現役で、その自動ビルド・テスト周りを作っていました。その後、WebClient(Galleryの表示部)やStudioの自動ビルドを作り、それを開発環境下でプレビューできる仕組みの構築を行っていました。また、コードの差分がなかった場合、ビルドを作成しないなどの仕組みの基礎がこのあたりで生まれました。
2023年からは @uechan16 が拡張しています。私がEMになったのを契機に大規模化と高機能化をお願いしています。STYLY Mobileはバージョン9へリニューアルした際に、CI周りが壊れてしまい、運用できない状態だったので、それを再度、PR駆動で動くようにリニューアルしました。また、その際に、SATCHもビルドとテストが稼働するようにしました。そして、これらでビルドされたバイナリを利用し、AirTest+Pocoで実機の自動テストができるようになりました。このあたりで大規模なCIシステムのリファクタリングを挟みました。2,3年もCIを作っており、内部的なコードや処理フローが複雑化し、コードの重複も生まれてきたので、このタイミングでアーキテクチャやコード構成の見直しを行いました。そのあと、PICO4やQuest3のアプリも対応しました。
感想
もともSQiPで @uechan16 がUnity周りのテストを発表することになっていたのですが、実はSTYLYの全体のCIについて書いた資料を公開していないな。ということに気づきました。昨今ではADR(Architecture Decision Record)を残すことも多くなってきましたが、そういった資料を私も残していかないといけないかな。と思い、この資料を公開しました。UnityまわりでのCIの資料を探してもUnityCloudBuildに関する事例があまりなく、どちらかというとJenkinsに寄った資料が多い認識です。しかし、ベンチャーなど規模の小さい開発体制であれば、UnityCloudBuildもプラットフォーム選定の対象になるのではないか。と思った次第です。