はじめに
UnityのWebGLビルドを経験された方はご存じだと思いますが、
プロジェクトサイズによってはそれなりの時間がかかります。
ビルド中は手元のPCに負荷がかかってしまったり、
毎回のデプロイ作業が面倒だったりと、効率化の余地がある部分です。
そこでUnityのWebGLビルドに焦点を当てて、CI/CDの構築を目指しました。
また、基本利用は無料サービスのものを選定し、
お金をかけずに最低限必要そうなものを揃えることができたのでメモします。
GitHub Actions
GitHub Actionsを利用することでPush、Issue作成、PR作成など
GitHubプラットフォームのイベントをトリガーとしてワークフローを起動可能となります。
今回は深堀しませんが、テストの自動実行なども
トリガーとなるイベントのタイミングで呼び出すことができます。
GitHub Actionsのワークフローを起動する機能
を使って、
ビルド→デプロイまでのフローを自動化する
というのが今回の試みです。
GitHub Actionsは無料アカウントでも利用することができます。
実行上限が決まっており、勝手に課金されることはないので安心して利用可能です。
【参考リンク】:About billing for GitHub Actions
GameCI
先ほどビルド→デプロイまでのフローを自動化する
と言いましたが、
この部分はGame CIの力を借ります。
Game CIにはUnityビルド用のワークフロー
がまとめられています。
このUnityビルド用のワークフロー
をGitHub Actionsから呼び出すことで
ビルド→デプロイまでのフローを自動化する
ことが可能となります。
Game CIは無料で利用可能です。聖人君子のような思想で開発/運営されています。
【引用元】:Game CI
GitHub Actions × Game CI
では、実際にGitHub ActionsとGame CIを動かしてみます。
まずは最小構成として
PRがmasterにマージされたタイミングで自動ビルドするサンプル
を作ってみます。
導入までのフローに関しては下記記事が詳しいです。
【参考リンク】:GameCI で Unity の CI 環境を GitHub Actions で構築する
諸々の設定が完了したらymlファイルをAdd fileからCreate new fileを選択し
.github/workflows
配下に追加します。
name: Build
on:
# masterへのPRマージをトリガーとしてワークフローを開始する
pull_request:
branches:
- master
types: [closed]
# 手動実行デバッグ用
workflow_dispatch: {}
jobs:
build:
name: Build my project
runs-on: ubuntu-latest
steps:
# Checkout
- name: Checkout repository
uses: actions/checkout@v3
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
これだけで自動ビルドが完成です。
リポジトリのActionsから成否を確認可能です。
独自のビルドパイプラインを走らせる
通常のUnityのビルドフローだと、Build Settings
からBuildを行います。
先ほどの最小構成の例はBuild Settings
からBuildするフローと同じになります。
一方で、独自のビルドパイプラインを構築した場合は少し方法が異なります。
以下のような最小構成のビルドパイプラインを実行する想定で考えます。
using UnityEditor;
using System.IO;
using System.Linq;
/// <summary>
/// 独自のビルドパイプラインを実行するEditor拡張
/// </summary>
public class CustomBuild : EditorWindow
{
[MenuItem("MyTool/build/dev")]
private static void DevBuild()
{
DoCustomBuildPipeLine();
}
/// <summary>
/// 独自ビルドパイプライン実行
/// </summary>
private static void DoCustomBuildPipeLine()
{
//保存先のパス取得
var path = EditorUtility.SaveFolderPanel("Choose Location", "", "");
//パスが入っていれば選択されたとみなす(キャンセルされたら入ってこない)
if (string.IsNullOrEmpty(path))
{
return;
}
//ビルドパイプライン設定
var buildPlayerOptions = new BuildPlayerOptions
{
scenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray(),
locationPathName = Path.Combine(path, "Custom Build"),
target = BuildTarget.WebGL,
options = BuildOptions.Development
};
//Build
BuildPipeline.BuildPlayer(buildPlayerOptions);
}
}
上記のDevBuildメソッドを呼び出す仕組みに変えたワークフローが以下です。
buildMethod: CustomBuild.DevBuild
を追加しただけです。
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
# Dev Buildのメソッド呼び出し
buildMethod: CustomBuild.DevBuild
ただ、これだと1つ問題があります。
ビルドがこけたときに、ActionのステータスがErrorになりません。
これでは、失敗していても見かけ上は成功したように見えてしまいます。
そこで、ビルドパイプライン側のコードを以下のように変更します。
using UnityEditor;
using System.IO;
using System.Linq;
using UnityEditor.Build.Reporting;
/// <summary>
/// 独自のビルドパイプラインを実行するEditor拡張
/// </summary>
public class CustomBuild : EditorWindow
{
[MenuItem("MyTool/build/dev")]
private static void DevBuild()
{
DoCustomBuildPipeLine();
}
/// <summary>
/// CICDのワークフローから実行する時はこちらを呼び出す
/// </summary>
private static void DevBuildForCICD()
{
DoCustomBuildPipeLine(true);
}
/// <summary>
/// 独自ビルドパイプライン実行
/// </summary>
private static void DoCustomBuildPipeLine(bool isCICD = false)
{
//保存先のパス取得
var path = EditorUtility.SaveFolderPanel("Choose Location", "", "");
//パスが入っていれば選択されたとみなす(キャンセルされたら入ってこない)
if (string.IsNullOrEmpty(path))
{
return;
}
//ビルドパイプライン設定
var buildPlayerOptions = new BuildPlayerOptions
{
scenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray(),
locationPathName = Path.Combine(path, "Custom Build"),
target = BuildTarget.WebGL,
options = BuildOptions.Development
};
//Build
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
var summary = report.summary;
if (isCICD)
{
//成否に応じてUnityEditorの終了プロセスを決定する
EditorApplication.Exit(summary.result == BuildResult.Succeeded ? 0 : 1);
}
}
}
EditorApplication.Exit
に0
を渡すと正常終了、1
を渡すと異常終了とみなされます。
あとはymlファイルをDevBuildForCICD
を呼び出す形で以下のように記述すれば、
独自ビルドパイプラインを呼び出したうえでビルドエラーもしっかりと拾うことができます。
name: Build
on:
# masterへのPRマージをトリガーとしてワークフローを開始する
pull_request:
branches:
- master
types: [closed]
# 手動実行デバッグ用
workflow_dispatch: {}
jobs:
build:
name: Build my project
runs-on: ubuntu-latest
steps:
# Checkout
- name: Checkout repository
uses: actions/checkout@v3
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
# Dev Buildのメソッド呼び出し
buildMethod: CustomBuild.DevBuildForCICD
Netlifyへのデプロイ
ビルドの自動化は実現できたので、デプロイも自動化していきます。
今回手軽に試せるデプロイ先としてNetlifyを選定しました。
GitHub Pagesも候補に挙がりましたが、PrivateリポジトリをGitHub Pagesで公開するためには
Proアカウントを契約する必要があるので見送りました。
GitHub Pagesは、GitHub Free及びOrganizationのGitHub Freeのパブリックリポジトリ、GitHub Pro、GitHub Team、GitHub Enterprise Cloud、GitHub Enterprise Serverのパブリック及びプライベートリポジトリで利用できます。
【引用元】:GitHub Pages について
まずはNetlifyにデプロイするためのサイトIDやアクセストークンを
該当リポジトリのSecretsに設定しておく必要があります。
ワークフローの使い方含め、以下リンクに詳細があります。
【参考リンク】:Netlify Deploy
デプロイのフローを追加したymlファイルは以下です。
name: Build and deploy
on:
# masterへのPRマージをトリガーとしてワークフローを開始する
pull_request:
branches:
- master
types: [closed]
# 手動実行デバッグ用
workflow_dispatch: {}
env:
target : Dev_Build
jobs:
build:
name: Build my project
runs-on: ubuntu-latest
steps:
# Checkout
- name: Checkout repository
uses: actions/checkout@v3
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
# Builder で出力した WebGL ビルドをアーティファクトでダウンロード可能にする
- name: Upload the WebGL Build
uses: actions/upload-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# NetlifyへのDeploy処理
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
# Builder で出力した WebGL ビルドをダウンロード
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# ダウンロードしたビルドファイルをNetlifyへDeploy
- name: Deploy to Netlify
uses: netlify/actions/cli@master
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_FOR_DEV }}
with:
args: deploy --dir=${{ env.target }} --prod
特筆すべきこととして、アーティファクトの取り回しが挙げられます。
アーティファクトとはワークフローの成果物を指します。
ログやテスト結果などもアーティファクトとして利用可能です。
【参考リンク】:Storing workflow data as artifacts
今回はビルド結果のフォルダ一式
をアーティファクトとして利用します。
デプロイまでのワークフローの流れは以下です。
1.ビルドする
2.ビルド結果のフォルダ一式を任意の場所にアップロードする
3.ビルド結果のフォルダ一式をダウンロードする
4.ダウンロードしたビルド結果のフォルダ一式をNetlifyにデプロイする
一度アップロードしているのが少し違和感がありますが、
ビルド結果のフォルダ一式はGame CIのビルドが終了するとCleanUpされてしまうので、
このような手法でないと成功しないのだと思います。
(これ確証はないので間違っていたらコメントください)
アーティファクトの保存期間はデフォルトでは90日間となっており、
リポジトリの設定で変更が可能です。ymlファイルへの記述で個々に変更することも可能です。
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: my-artifact
path: my_file.txt
retention-days: 5
【参考リンク】:Configuring the retention period for GitHub Actions artifacts and logs in your organization
【参考リンク】:Retention Period
Slackへの通知
ここまでのワークフローでも十分に効率化ができましたが、
欲を言えば結果の通知機能がほしいです。
そこで今回はデプロイまでのワークフローの結果の成否
に応じて
Slackへ通知を飛ばす処理を追加してみました。
以下のリポジトリのワークフローを利用します。
設定についても記載があり、Incoming WebHooksの設定を行うだけなので非常に便利です。
【参考リンク】:Slack Notify - GitHub Action
name: CICD workflow
on:
# masterへのPRマージをトリガーとしてワークフローを開始する
pull_request:
branches:
- master
types: [closed]
# 手動実行デバッグ用
workflow_dispatch: {}
env:
target : Dev_Build
jobs:
build:
name: Build my project
runs-on: ubuntu-latest
steps:
# Checkout
- name: Checkout repository
uses: actions/checkout@v3
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
# Builder で出力した WebGL ビルドをアーティファクトでダウンロード可能にする
- name: Upload the WebGL Build
uses: actions/upload-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# NetlifyへのDeploy処理
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
# Builder で出力した WebGL ビルドをダウンロード
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# ダウンロードしたビルドファイルをNetlifyへDeploy
- name: Deploy to Netlify
uses: netlify/actions/cli@master
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_FOR_DEV }}
with:
args: deploy --dir=${{ env.target }} --prod
# Slackへの成功通知処理
NotifySucceed:
if: ${{ success() }}
name: Notify succeed
# ほかのjobの結果を待つ
needs: [build, deploy]
runs-on: ubuntu-latest
steps:
- name: Nofity build and deploy succeed
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: GitHub Actions
SLACK_TITLE: Workflow succeeded
SLACK_MESSAGE: 'https://hogehoge-kento.netlify.app/'
# Slackへの失敗通知処理
NotifyFailure:
if: ${{ failure() }}
name: Notify failure
# ほかのjobの結果を待つ
needs: [build, deploy]
runs-on: ubuntu-latest
steps:
- name: Nofity build or deploy failure
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: GitHub Actions
SLACK_TITLE: Workflow failed
SLACK_COLOR: danger
SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'
needs: [ジョブ名]
でほかのjobの処理の完了を待つことができます。
今回はビルドとデプロイのjobの完了を待ち、
その成否に応じてSlackに通知を送るワークフローをわけています。
メッセージにデプロイ先のリンクを張り付けておくと
そのままSlackからデプロイされたビルドの確認が行えるので便利です。
キャッシュ
ビルドの箇所でLibraryフォルダをキャッシュしていました。
しかし、キャッシュのkeyが同じであれば、キャッシュの箇所に変更があっても、
そのkeyのキャッシュはすでに存在しているとみなされ、
新たにキャッシュが作られることは無い仕様となっていました。
この仕様だと、Libraryフォルダの中身が大きく変わるレベルの変更があった場合、
キャッシュが更新されず、余分なビルド時間がかかってしまいます。
そこで、今回は日付とkeyを紐づけることで、
一定期間ごとにキャッシュが更新される仕組みを導入しました。
該当箇所だけを抜粋したものが以下です。
月を跨いだタイミングでキャッシュが更新される例となっています。
# 年月日を取得 キャッシュのkeyに利用
- name: Set current datetime as env variable
env:
TZ: 'Asia/Tokyo' # タイムゾーン指定
run: echo "CURRENT_DATETIME=$(date +'%Y-%m')" >> $GITHUB_ENV
# キャッシュ keyを月跨ぎで更新することで定期的にキャッシュ自体の更新を行う
- uses: actions/cache@v3
with:
path: Library
key: "Library_${{ env.CURRENT_DATETIME }}"
プロジェクトのアップデート頻度に応じて、
キャッシュの更新頻度を日/月/年と調整可能です。
この辺は他にも良い方法がありそうなので、
プロジェクトの要望に応じて変更するのが望ましいかと思われます。
デバッグ
ビルドパイプラインの修正などを行う際、
任意のブランチに対してワークフローを走らせたいという要求がありました。
そこで、ブランチ名を指定してそのブランチにチェックアウトした状態で
ワークフローを走らせるデバッグ機能を用意しました。(下記画像参照)
該当箇所だけを抜粋したものが以下です。
on:
# Debug用にActionsメニューからGUIでActionを走らせることを許容する
workflow_dispatch:
# 入力欄の定義
inputs:
ref:
description: "ref"
required: false
default: ""
jobs:
# ビルド処理
build:
name: Run the WebGL build
runs-on: ubuntu-latest
steps:
# 作業ディレクトリにUnity プロジェクトの中身をダウンロードしてくる
- name: Check out my unity project.
uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git checkout ${{ github.event.inputs.ref }}
if: ${{ github.event.inputs.ref != '' }} # ref が指定されていたら git checkout
【参考リンク】:GitHub Actionsで特定のコミットをcheckoutしてワークフローを実行する
ワークフロー全文
最後に、出来上がったワークフローを載せて終わりとします。
name: CICD workflow
on:
# masterへのPRマージをトリガーとしてワークフローを開始する
pull_request:
branches:
- master
types: [closed]
# Debug用にActionsメニューからGUIでActionを走らせることを許容する
workflow_dispatch:
# 入力欄の定義
inputs:
ref:
description: "ref"
required: false
default: ""
env:
target : Dev_Build
jobs:
build:
name: Build my project
runs-on: ubuntu-latest
steps:
# 作業ディレクトリにUnity プロジェクトの中身をダウンロードしてくる
- name: Check out my unity project.
uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git checkout ${{ github.event.inputs.ref }}
if: ${{ github.event.inputs.ref != '' }} # ref が指定されていたら git checkout
# Build
- name: Build project
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: WebGL
unityVersion: 2022.1.13f1 # ここは各自プロジェクトに合わせて設定
# 独自のビルドメソッドを呼び出したい場合はここで行う。
# buildMethod: CustomBuild.DevBuild
# Builder で出力した WebGL ビルドをアーティファクトでダウンロード可能にする
- name: Upload the WebGL Build
uses: actions/upload-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# NetlifyへのDeploy処理
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
steps:
# Builder で出力した WebGL ビルドをダウンロード
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.target }}
path: ${{ env.target }}
# ダウンロードしたビルドファイルをNetlifyへDeploy
- name: Deploy to Netlify
uses: netlify/actions/cli@master
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_FOR_DEV }}
with:
args: deploy --dir=${{ env.target }} --prod
# Slackへの成功通知処理
NotifySucceed:
if: ${{ success() }}
name: Notify succeed
# ほかのjobの結果を待つ
needs: [build, deploy]
runs-on: ubuntu-latest
steps:
- name: Nofity build and deploy succeed
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: GitHub Actions
SLACK_TITLE: Workflow succeeded
SLACK_MESSAGE: 'https://hogehoge-kento.netlify.app/'
# Slackへの失敗通知処理
NotifyFailure:
if: ${{ failure() }}
name: Notify failure
# ほかのjobの結果を待つ
needs: [build, deploy]
runs-on: ubuntu-latest
steps:
- name: Nofity build or deploy failure
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: GitHub Actions
SLACK_TITLE: Workflow failed
SLACK_COLOR: danger
SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'
参考リンク
- GithubActionsで環境変数を理解する。
- Effectively Manage GitHub Actions Artifacts to Deploy Releases
- Slack が提供する GitHub Action "slack-send" を使って GitHub から Slack に通知する
- pushしたら自動でUnityビルドが走る環境を手に入れる
- [GitHubActions] 自動でUnityPackageをアップロードしてリリースする
- GitHub Actions ワークフローにおけるジョブ制御
- exit と exit 1 の違い
- GitHub Actions覚え書き
- GitHub Actionsで現在日時を取得する