UnityのアプリビルドをGitHub Actionsに移行した話
はじめに
本記事では、弊社のゲーム開発プロジェクトで利用しているアプリビルドのCIツールの移行についてご紹介します。
弊社ではCIツールとして今までJenkinsを採用してきましたが「ジョブの設定の再利用性とレビューフローの不確立における属人化」と「プラグイン依存における保守性の低下」からGitHub Actionsへの移行を推進しています。
今回はその一環として実施した、UnityのアプリビルドをJenkinsからGitHub Actionsへ移行した時の対応例や移行中に発生した問題の解決策などについて解説します。
また、本記事は2024年3月7日に開催された「CyberAgentGameConference2024(CAGC2024)」のセッション内容をAIによる自動文字 起こしをベースに加筆修正したものになります。
UnityのアプリビルドをGitHub Actionsに移行した話
GitHub Actionsとは
GitHub Actionsとは「ビルド」「テスト」「デプロイ」のパイプラインを自動化できるCI/CDのプラットフォームになります。
特徴としてはワークフローをYAMLで記述することができ、イベントトリガーはプルリクエストや、Issueに対してなど豊富にあり定期実行もできます。 また、公式非公式含めGitHub上で提供されているアクションを、自分のワークフローで使用できるなどが挙げられます。
弊社では開発の効率化を目指し、GitHub Actionsを積極的に活用しています。表の4つの機能は一例に過ぎませんが、これらを活用することで開発プロセスの効率化と品質向上を実現しています。
機能名 | 説明 |
---|---|
定期的なコードのクリーンアップ | コードフォーマットに沿っているか空白やスペースを定期的にチェックします。フォーマットに沿っていない場合は修正した内容のプルリクエストを作成します。 |
プルリクエストの自動テスト | コンパイルエラーやPrefabのMissingなどの実行エラーをチェックします。 |
プルリクエストに適切なラベルの付与 | プルリクエストに対して開発バージョンや、どの機能のプルリクエストか視認性を高めるために実行しています。 |
アプリバージョンごとのブランチの自動マージ | 開発をしていると発生するバージョンブランチを自動でマージします。 |
アプリビルドをGitHub Actionsに移行した経緯
弊社ではJenkinsと呼ばれるCIツールを長年使用していますが、運用していく中でさまざまな問題が挙げられるようになりました。 その中でも大きな問題点が2つあり、1つ目が「属人化」 2つ目が「保守性の低下」になります。
Jenkinsの問題点① 属人化
Jenkinsの特徴として、ジョブの設定はGUIを通じて簡単に行うことが可能ですが、コードベースでの管理や履歴の追跡が困難であり、再利用性に欠けるという問題点を抱えていることが挙げられます。
また、設定変更のレビュープロセスを作成することが難しく、結果として設定作成者以外にその内容が理解されにくい状況となります。
Jenkinsの問題点② 保守性の低下について
また、Jenkinsの別の特徴としてさまざまなプラグインを使用することが可能です。ただし、セキュリティ上の観点から定期的なアップデートが必要になってきます。
その問題点として多くのプラグインがインストールされている場合、プラグイン間のバージョン依存によりアップデートが困難になることがあります。
Jenkinsの問題点を解消できる可能性
「属人化」の問題については、GitHub ActionsのワークフローをGitHub上でYAML形式のコードとして管理することで、再利用が可能となり、レビューもプルリクエストを作成することで行うことができます。
「保守性の低下」については、プラグインの代わりにActionを使用することで改善が見込めると考えられます。
以上のことからGitHub Actionsへの移行を決意しました。
移行のプロセス
まずは、移行を話す上で環境の前提の話から説明します。 上の図はGitHub Actions移行前のJenkinsでのアプリビルドの構成になります。
弊社ではiOS、Androidをターゲットにしているゲームをメインに配信しているので、各プラットフォームのDevelopやReleaseといった環境に合わせた設定をしているジョブから、メインで処理を実行しているビルドジョブをトリガーしている構成となっています。 これをGitHub Actionsでも同様の構成にして、手動または定期的に実行できるようにします。
ビルドジョブ
GitHub Actionsは、YAML形式を用いて処理を記述することが可能で、これによりJenkinsで用いていたYAMLコードを直接利用することができます。
そのため、ビルドフローの大部分は変更されず、図に示されているような処理が行われます。
ビルド処理を除いて、以前はJenkinsのGUIで行っていた設定をワークフローのinputsと呼ばれる入力パラメータに追加しています。これにより、workflow_dispatchというイベントトリガーを使用して、手動での実行も可能にしています。
さらに、DevelopやReleaseといった各環境において、一部の変数の内容を変更する必要がある場合、リポジトリ毎に設定を行うことができます。これは、指定した環境に応じて変数の内容を変更する機能、すなわち環境変数を設定変更できるようにするためです。具体的には、ジョブの環境(environment)をinputsから指定することで実現することができます。
【補足】GitHubの環境変数について
GitHubでは、各リポジトリに対し環境変数として「environment」を設定することができます。 ジョブのenvironmentに特定の環境を指定することにより、登録済みのseacrets/variablesを参照できます。
さらに、他のenvironmentと同じ変数名を登録することで、ジョブのenvironmentを単に変更するだけで、その中身を切り替えることができます。
この機能は、環境によって異なる証明書やプラグインの変更が必要な場合に特に便利です。その他にもプラグインのバージョン等を事前に登録しておくことで、ビルドワークフローで余分な分岐を作ることなく、より使いやすい環境を構築することができます。
各環境のジョブ
定期実行はスケジュール(schedule)というイベントトリガーを用いて、特定の環境のみ実行しています。各環境で必要なパラメータはinputsで設定、固有で入力するものは省いています。
また、メイン処理となるワークフローの呼び出しについては、さまざまな方法が考えられるのですが、今回はworkflow_callを使用しています。workflow_callは使用場面がかなり限られる方法ではあるのですが、usesにファイルパスを指定するだけで、簡潔に呼び出すことができるので今回は採用しました。
移行中に起きた問題と対応について
実際のところそこまで大きな問題はなかったのですが、一点だけ問題が発生しました。それはワークフローのinputs上限です。
GitHub Actionsでは、ワークフローのinputsに設定可能な入力プロパティの数が最大10個までという制約があります。これまでJenkinsで活用していた入力変数が10個以上あった場合、すべてを移行することは難しい状況でした。
この問題に対する解決策として、Xcodeや.NETなどのバージョンを指定する項目は環境変数で設定するようにしました。また、入力プロパティに関してはJSON形式で受け取るように修正しました。これにより、複数の入力プロパティを一つのJSONオブジェクトとして扱うことで、上記の制約を回避することができました。
JSON形式の入力プロパティについて
それではJSON形式での入力プロパティの設定方法とその展開手順について、概要から説明します。その後、それぞれのプロセスについて詳しく解説します。
初めに、workflow_dispatchのinputsにstring型で受け取ることが可能なプロパティを準備します。次にステップ内、特に処理の始めにおいて、JSON形式のプロパティを展開します。その後、展開した値をEnvironmentやOutputとして設定するという手順になります。
Json形式のプロパティを設定
まず最初に、inputsに対してstring型で受け取れるプロパティを設定します。これは特に難しい部分ではなく、通常通りstring型のプロパティを準備します。
今回の例では、画像中の赤字部分のような入力を想定しています。
Json形式のプロパティを展開
次に、先程用意した入力文字をステップ内で展開する方法を解説します。
画像中の①のように記述することで、JSON内のキーに対応する値を取得することが可能となります。取得した値は、②のようにEnvironmentやOutputとして設定します。Outputとして設定する際には、stepのid指定が必要です。
以上がJSONプロパティの設定と展開の手順です。この2つの工程を行うことで、JSONプロパティの設定と展開が完了します。
ここで1つ問題になるのですが、GitHub ActionsのGUIだとJSON形式の入力にしたことにより、手動でのキーと値の追加、変更が画像のように困難になってしまいました。
この問題への対応として、Slack AppからJSONのキーに対する値を入力するような新たなGUIの用意と、Dispatchesイベントをトリガーし、任意のワークフローを実行するような仕組みを作成しました。
Slack Appを使用したワークフローのトリガー
先ほどの問題に対するSlack Appを用いたワークフロートリガーの紹介を行います。ただし、紹介に先立ち、今回の対応経緯とSlack Appを選択した理由について改めて説明します。
- JSON形式のInputsへの切り替えによるプロパティ入力の対応 JSON形式のInputsへの切り替えにより、キーと値の変更が困難になってしまったため、GUIを用意しました。
- エンジニア以外の職種向けのアプリビルド方法の作成 弊社ではエンジニア以外の職種も実機確認等でアプリビルドを実行することがあり、その対応は必須でした。また、普段の業務でSlackを使用していることから、ワークフローを実行する際にGitHubのページに遷移する手間を省き、GitHubアカウントの用意や、意図しないワークフローの実行を防ぐ意図もありました。
- JenkinsのようなGUIパラメータ入力の再現 長年使用してきたツールとの差異を可能な限り減らすため、JenkinsのようなGUIパラメータ入力を再現しました。
以上の理由から、今回はSlack Appを選定し、ワークフロートリガーの紹介を行います。
実際のSlack Appの画面を参照しながら説明するとイメージしやすいと思いますので、上記の画像を基に説明を進めます。
Slack Appのホーム画面には、iOSとAndroid、そして環境別に分けられたビルドボタンが配置されています。ビルドボタンをクリックすると、Slackの機能であるモーダル(ポップアップウィンドウ)が表示され、このモーダルではJSON形式のキーに対する値を入力することができます。
必要な項目を全て入力し終えたら、モーダル内の「実行」ボタンをクリックします。すると、入力内容がJSON形式に変換され、選択した環境のワークフローがREST APIを介してトリガーされます。
ただし、このシステムはSlack Appとして動作しているため、意図しないメンバーからのアクセスや操作が可能になってしまいます。そのリスクを軽減するために、表示内容をSlackのユーザーグループによって変更したり、ホーム画面自体が表示されないように制御する機能も実装しています。
以上が今回のアプリビルドの移行についての説明となります。
今後の展望
1つ目は今回の移行中に私自身が知ったことになります。 setup-dotnetを使用することでYAMLではなくC#スクリプトで処理を動作させることが可能です。実際に弊社では一部の処理はシェルスクリプトなどではなくC#化を行い処理するようにしています。
C#化を進めることによりUnityエンジニアであれば誰でもメンテナンスができるようになります。 それにより、問題点の部分で説明した属人化をさらに解消できるのではないかと考えています。
2つ目は今回の実装である程度Jenkinsで実行していた処理をGitHub Actionsへ移行できることが可能であると分かりました。Unityのアセットバンドルやバッチ処理などをJenkinsから移行していき、後々は完全にGitHub Actionsのみで完結するのではないかと考えています。
まとめ
Jenkinsで発生していた属人化のコードベースでの管理や履歴の追跡については、コード自体GitHub上で管理することで解決することができました。さらにはYAMLからC#へのコード化を行うことで、Unityエンジニアであれば、誰でもメンテナンスが可能になりました。
レビューフローの確立については、プルリクエストを出すことによりレビューが可能になっています。
保守性の低下のプラグイン間のバージョン依存によるアップデートの困難化については、プラグインの代わりにAction(独立したスクリプト)を使用することや独自自前のスクリプトを使用することで依存性が少なく定期的なアップデートも容易になりそうです。