【Unity + GitHub Actions】Addressable Assetsをビルド、サーバーにアップロードを自動化する

# 実現する事

- GitHub Actionsからビルドの実行
- Addressable Assetsのビルド
- ビルドしたものをサーバーに送信

`Test`か`Release`を入力、`Run workflow`をクリックして実行します。

![スクリーンショット 2021-05-30 0.41.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/0c737476-e8ec-594c-0ba9-3d9c1bb9a643.png)

下記が完了画面です。
Artifactsに保存されているものはサーバーに送信したものと同じものです。

![スクリーンショット 2021-05-30 3.26.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/50ca566d-4767-a000-448d-c2bc3fe62634.png)

# 仕様や注意事項など
- テストビルドで目視で確認、問題無ければ、リリースビルドという流れを想定しています。
- プラットフォームはiOSとAndroidです。(変更化)
- テストとリリースの中身はまったく同じものがサーバーに送信されます。
- サーバーに送信したものは消しません。同じファイル名のものは上書きされます。
- Addressable Assetsのバージョン管理は手動です。


# Addressablesのテスト用スキームの準備

Manage Profilesを開きます。

![スクリーンショット 2021-05-30 0.39.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/12bb6fa1-2676-c3e3-9c2a-29cad59ed3ea.png)

Createでテスト用のスキームを用意します。`RemoteBuildPath`と`RemoteLoadPath`をDefaultと違うものを指定してください。

![スクリーンショット 2021-05-30 0.39.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/5b523b46-996f-0afa-9ecc-0d330c07249b.png)

# ビルドスクリプトを設置しコミット

下記に配置します。
Assets/Editor/AddressableAssetsExporter.cs

配置後、コミットしてください。

```c#
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor;

public static class AddressableAssetsExporter
{
    public static void Export()
    {
        Export("Default");
    }
    
    public static void ExportTest()
    {
        Export("Test");
    }

    static void Export(string profileID)
    {
        var settings = AddressableAssetSettingsDefaultObject.Settings;
        settings.activeProfileId = settings.profileSettings.GetProfileId(profileID);
        AddressableAssetSettings.CleanPlayerContent();
        AddressableAssetSettings.BuildPlayerContent();
    }
}
```

### ※追記※
上記のスクリプトは、現在のものをすべて削除して書き出す設定です。
ダウンロードコンテンツは「Update a Previous Build」でビルドする必要があるようです。
詳しくは、下記記事をご確認ください。
https://light11.hatenadiary.com/entry/2021/01/13/203111

# GitHub Secretsにサーバー情報とUnityライセンスを登録

Unityライセンスの取得方法は前の記事の[こちら](https://qiita.com/IShix/items/3a6fed53a2a4df24a01e#%E6%89%8B%E9%A0%861-unity%E3%81%AE%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%99%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)をご覧ください。

| Name | 説明 |
|:-:|:-:|:-:|
| FTP_HOST | FTPのホスト名 |
| FTP_PASSWORD | FTPのパスワード |
| FTP_USER | FTPのユーザー名 | 
| UNITY_LICENSE | Unityのライセンス | 

![スクリーンショット 2021-05-30 3.07.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/f3312809-e269-42c3-42b6-cbdcce97f76f.png)


# ymlを配置する

下記をGitHub Actionsに配置
設置方法が分からない場合は、前の記事「[【Unity】Github ActionでUnityPackageの生成からリリースまで全自動で行う方法](https://qiita.com/IShix/items/3a6fed53a2a4df24a01e)」が参考になると思います。

**[変更箇所]**

| 対象 | 説明 | 例 |
|:-:|:-:|:-:|
| LOAD_DIR | [リリース用]スキームのRemoteBuildPath | ServerData |
| BUILD_METHOD | [リリース用]ビルドメソッドをnamespaceから指定 | AddressableAssetsExporter.Export |
| REMOTE_DIR | [リリース用]スキームのRemoteLoadPathのドメインを抜かしたフォルダ名 | AddressableToServerTest |
| LOAD_TEST_DIR | [テスト用]スキームのRemoteBuildPath | ServerDataTest |
| BUILD_TEST_METHOD | [テスト用]ビルドメソッドをnamespaceから指定 | AddressableAssetsExporter.ExportTest |
| REMOTE_TEST_DIR | [テスト用]スキームのRemoteLoadPathのドメイン名を抜かしたフォルダ名 | AddressableToServerTest/Test |

**REMOTE_DIRの例)**
RemoteLoadPath: https://xxx.com/AddressableToServerTest
REMOTE_DIR: AddressableToServerTest

```yml
name: Addressable Assets Build And Deploy

on:
  workflow_dispatch:
    inputs:
      build_mode:
        description: "Build mode : Test / Release"
        required: true

env:
  BUILD_MODE: ${{ github.event.inputs.build_mode }}
  # 本番用
  LOAD_DIR: "ServerData"
  BUILD_METHOD: "AddressableAssetsExporter.Export"
  REMOTE_DIR: "AddressableToServerTest"
  # テスト用
  LOAD_TEST_DIR: "ServerDataTest"
  BUILD_TEST_METHOD: "AddressableAssetsExporter.ExportTest"
  REMOTE_TEST_DIR: "AddressableToServerTest/Test"

jobs:
  check-input:
    name: Check Input
    runs-on: ubuntu-latest
    steps:
      - name: Check Value
        run: |
          if ${{ env.BUILD_MODE != 'Test' && env.BUILD_MODE != 'Release' }}; then
            echo ::error:: "TestまたはReleaseを指定してください。 指定された値: ${{ env.BUILD_MODE }}"
            exit 1
          else
            echo "Valid input value"
          fi

  set-value:
    needs: check-input
    name: Set Value - ${{ github.event.inputs.build_mode }}
    runs-on: ubuntu-latest
    outputs:
      LOAD_DIR: ${{ steps.set-value.outputs.LOAD_DIR}}
      BUILD_METHOD: ${{ steps.set-value.outputs.BUILD_METHOD}}
      REMOTE_DIR: ${{ steps.set-value.outputs.REMOTE_DIR}}
    steps:
      - name: Set Value
        id: set-value
        run: |
          if ${{ env.BUILD_MODE == 'Release' }}; then
            echo "::set-output name=LOAD_DIR::${{ env.LOAD_DIR }}"
            echo "::set-output name=BUILD_METHOD::${{ env.BUILD_METHOD }}"
            echo "::set-output name=REMOTE_DIR::${{ env.REMOTE_DIR }}"
          else
            echo "::set-output name=LOAD_DIR::${{ env.LOAD_TEST_DIR }}"
            echo "::set-output name=BUILD_METHOD::${{ env.BUILD_TEST_METHOD }}"
            echo "::set-output name=REMOTE_DIR::${{ env.REMOTE_TEST_DIR }}"
          fi

  build_and-deploy:
    needs:
      - check-input
      - set-value
    name: Build and Deploy - ${{ matrix.targetPlatform }} - ${{ github.event.inputs.build_mode }}
    runs-on: ubuntu-latest
    timeout-minutes: 15
    strategy:
      fail-fast: false
      matrix:
        targetPlatform:
          - iOS
          - Android
    steps:
      # Checkout
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          lfs: true
      # Cache
      - name: Cache
        uses: actions/cache@v2
        with:
          path: Library
          key: Library-${{ matrix.targetPlatform }}
          restore-keys: Library-
      # Build
      - name: Build - ${{ matrix.targetPlatform }}
        uses: game-ci/unity-builder@v2
        env:
         UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
        with:
          targetPlatform: ${{ matrix.targetPlatform }}
          buildMethod: ${{ needs.set-value.outputs.BUILD_METHOD }}
      # Upload artifacts
      - name: Upload artifacts - ${{ matrix.targetPlatform }}
        uses: actions/upload-artifact@v2
        with:
          name: ${{ matrix.targetPlatform }}_${{ env.BUILD_MODE }}
          path: ./${{ needs.set-value.outputs.LOAD_DIR }}/${{ matrix.targetPlatform }}/*
      # Upload ftp
      - name: Upload ftp - ${{ matrix.targetPlatform }}
        uses: sebastianpopp/ftp-action@releases/v2
        with:
          host: ${{ secrets.FTP_HOST }}
          user: ${{ secrets.FTP_USER }}
          password: ${{ secrets.FTP_PASSWORD }}
          localDir: ${{ needs.set-value.outputs.LOAD_DIR }}
          remoteDir: ${{ needs.set-value.outputs.REMOTE_DIR }}
```

# プラットフォームを変更したい

`build_and-deploy:`の`matrix:` > `targetPlatform:`を変更してください。
[BuildTarget](https://docs.unity3d.com/ja/2018.4/ScriptReference/BuildTarget.html)からプラットフォーム名を取得できます。

例)

```yml
      matrix:
        targetPlatform:
          - StandaloneWindows
          - StandaloneOSX
```

# 各機能の詳細

- [Unityのビルド game-ci/unity-builder](https://github.com/game-ci/unity-builder)
- [FTP アップロード sebastianpopp/ftp-action](https://github.com/sebastianpopp/ftp-action)


# おまけ

Addressable Assetsの対象グループのすべてのアセットがロードできるかのTest Runnerスクリプト。
書いてみたもののAddressable Assetは目視で確認した方が良いと個人的には思っています。

![スクリーンショット 2021-05-30 19.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/980516be-8854-5652-a700-bf5480c1a806.png)

利用する際は、`Play Mode Script`を`Use Existing Build`に変更してください。

![スクリーンショット 2021-05-30 19.40.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/aa817578-1f49-7c07-fe6f-a4d2305bc6f9.png)


AddressableAssetsTest.cs

```c#
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEngine.AddressableAssets;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;

namespace Tests
{
    public class AddressableAssetsTest
    {
        /// <summary>
        /// テスト対象のグループ名
        /// </summary>
        public readonly IEnumerable<string> TargetGroup = new string[]{ "Default Local Group" };

        /// <summary>
        /// 対象グループのすべてのアセットがロードできるかのテスト
        /// </summary>
        /// <returns></returns>
        [UnityTest]
        public IEnumerator Load()
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            var groups = settings.groups.Where(g => TargetGroup.Any(x => x == g.Name));

            foreach (var group in groups)
            {
                var entrys = new List<AddressableAssetEntry>();
                group.GatherAllAssets(entrys, false, true, false);
                foreach (var entry in entrys)
                {
                    var handle = Addressables.LoadAssetAsync<Object>(entry.AssetPath);
                    yield return handle;
                    Assert.IsNotNull(handle.Result);
                }
            }
        }
    }
}
#endif
```

Assembly DefinitionにAddressableが紐づいていない場合は、`Assembly Defiintion Reference`に追加してください。

![スクリーンショット 2021-05-30 4.00.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389614/ee29d4c2-c365-e0f5-fcb1-b3cf86622b40.png)