Github Actions で UnityPackage をビルドしたら快適だった話

こちらは、[Unity #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/unity2) 10日目の記事になります。

# はじめに

みなさんは Unity 開発で Github Actions を使ってますか？

最近は、[unity-actions](https://github.com/webbertakken/unity-actions) でビルドできるものが増えてきたので、
Android や iOS の AR アプリなどを作る時も積極的に使うようにしてます。

そこで今回は、いつもコピペで使ってしまいそうな便利関数を集めてライブラリ化してみたので、
Github Actions をどのように使ったのかを解説していこうと思います。


（[こちら](https://qiita.com/nmss208/items/9846525cf523fb961b48)の記事にインスパイアされました:bow:）



# 作成したもの
[![TakenokoTech/Unitool - GitHub](https://gh-card.dev/repos/TakenokoTech/UniTool.svg)](https://github.com/TakenokoTech/UniTool)

リリース：https://github.com/TakenokoTech/UniTool/releases
APIドキュメント：https://takenokotech.github.io/UniTool/api/index.html

# CIの構成
![unitool_flowchat (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109303/bace64c0-f0cd-4901-8b39-e9bf8fed9ff1.png)


## Activation
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/activation.yml

[unity-ci](https://unity-ci.com/docs/github/activation) の手順通り Github Actions で Unity を使ったビルドができるようにします。
他サイトでも、やり方は載っているので流れだけ簡単に説明します。

1. アクティベートファイルを Github Actions で取得。（CIのアーティファクトに出力される。）
2. https://license.unity3d.com/manual にアクティベートファイルを登録してライセンスファイルを取得。
3. リポジトリのシークレットにライセンスファイルを登録。

これで、シークレットに登録したライセンスファイルを使えば、Unity を使ってビルドできるようになります。
Activation の CI については、はじめに一回行えば大丈夫です。


## Build
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/build.yml

UnityPackage が各プラットフォームでビルドできるかを確認するパイプラインです。
`targetPlatform` を切り替えて、動作確認すると良いと思います。

```yaml:.github/workflows/build.yml
name: Build

on:
  pull_request: {}
  push: { branches: [main] }

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  build:
    name: Build project
    runs-on: ubuntu-latest
    steps:
      # Checkout
      - name: Checkout repository
        uses: actions/checkout@v2

      # Build
      - name: Build project
        uses: webbertakken/unity-builder@v0.10
        with:
          unityVersion: 2019.3.7f1
          targetPlatform: StandaloneWindows

      # Output
      - uses: actions/upload-artifact@v1
        with:
          name: Build
          path: build
```

## Documentation
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/documentation.yml

今回拘った C# のソースコードから API ドキュメントを作成するパイプラインです。
DocFxを使って `Assets` ディレクトリ配下の C# のコメントを分析してHTMLファイルなどを生成してます。

事前にローカル環境で `docfx init` をして、ドキュメントの[テンプレート](https://github.com/TakenokoTech/UniTool/tree/master/.docs)を作っておく必要があります。
ここで、`src` ディレクトリ配下に C# のソースコードを配置する必要がありますが、
CIで `xcopy` コマンドを使って `Assets` から移動しています。
（シンボリックリンクなどはうまくいかず・・・）

さらに、`csproj` ファイルも必要ですが、ルートのプロジェクトファイルは使わずに
必要最低限だけコンパイル対象にした[ファイル](https://github.com/TakenokoTech/UniTool/blob/master/.docs/src/UniTool.csproj)を用意しました。

```xml:docs/DemoProject.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project>
    <ItemGroup>
        <None Include="Assets\DemoProject\DemoProject.asmdef" />
        <Compile Include="Assets\DemoProject\**\*.cs" />
    </ItemGroup>
</Project>
```

PR では [Netlify](https://www.netlify.com/) へのビルド。main ブランチにマージされたら Github Pages にデプロイしてます。

```yaml:.github/workflows/docs.yml
name: Docs

on:
  pull_request: {}
  push: { branches: [master] }

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  build:
    name: Docs project
    runs-on: windows-latest
    steps:
      # Checkout
      - name: Checkout repository
        uses: actions/checkout@v2
      
      # Install Node
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 12.x
          
      # Install DocFx
      - name: Install
        run: |
          choco install -y docfx
      
      # Build
      - name: Build
        run: |
          cd .docs
          xcopy /e /i /y ..\Assets src\Assets\ 
          docfx metadata
          docfx build
      
      # Upload
      - name: Upload site artifact
        uses: actions/upload-artifact@v1
        with:
          name: docs
          path: .docs/_site
```



## Package
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/package.yaml

[UnityPackageExporter.cs](https://github.com/TakenokoTech/UniTool/blob/master/Assets/UniTool.Editor/UnityPackageExporter.cs) のExportメソッドを実行して UnityPackage に書き出しています。
今回は、`Dist`ディレクトリ配下に`UniTool.unitypackage`を出力するようにしてます。
書き出す対象は、`.cs` `.meta` `.asmdef` のファイルのみにしました。


```yaml:.github/workflows/package.yml
name: Packaging

on:
  pull_request: {}
  push: { branches: [master] }

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  build:
    name: Build UnityPackage
    runs-on: ubuntu-latest
    steps:
      # Checkout
      - name: Checkout repository
        uses: actions/checkout@v2

      # Build
      - name: Build project
        uses: webbertakken/unity-builder@v0.10
        with:
          unityVersion: 2019.3.7f1
          buildMethod: DemoProject.Editor.UnityPackageExporter.Export
```

## Test
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/test.yml

[Codecov](https://codecov.io/) でコードカバレッジを分析できるようにしてます。
コードカバレッジを出力する場合は customParameters に `-enableCodeCoverage` をつけてください。

```yaml:.github/workflows/test.yml
name: TestCov

on:
  pull_request: {}
  push: { branches: [master] }

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  build:
    name: Test project
    runs-on: ubuntu-latest
    steps:
      # Checkout
      - name: Checkout repository
        uses: actions/checkout@v2
      
      # Test 
      - name: Run tests
        uses: webbertakken/unity-test-runner@v1.6
        with:
          unityVersion: 2019.3.7f1
          customParameters: -enableCodeCoverage

      # Coverage
      - name: Coverage Report
        uses: codecov/codecov-action@v1
        with:
          flags: unittests
          file: ./CodeCoverage/workspace-opencov/EditMode/TestCoverageResults_0000.xml
          
      # Coverage
      - name: Coverage Report
        uses: codecov/codecov-action@v1
        with:
          flags: unittests
          file: ./CodeCoverage/workspace-opencov/PlayMode/TestCoverageResults_0000.xml
```


また、途中で [Danger](https://github.com/TakenokoTech/UniTool/blob/master/Dangerfile) を使って、テストが失敗したらレポートされるようにしてます。
[nunit-to-junit.xsl ](https://github.com/TakenokoTech/UniTool/blob/master/Script/nunit-to-junit.xsl)を使用して、NUnit で出力されたテストレポートを JUnit のレポートに変換して実現しました。


## Release
https://github.com/TakenokoTech/UniTool/blob/master/.github/workflows/release.yml

よくあるタグを切ったらリリースするってやつです。
UnityPackage を upload-release-asset で書き出す場合は `application/zip` を指定します。

```yaml:.github/workflows/release.yml
name: Release

on:
  push: { tags: ['v*'] }

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  build:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      # Checkout
      - name: Checkout code
        uses: actions/checkout@v2
      
      # Build
      - name: Build project
        uses: webbertakken/unity-builder@v0.10
        with:
          unityVersion: 2019.3.7f1
          buildMethod: DemoProject.Editor.UnityPackageExporter.Export
      
      # Create Release
      - id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: Update package.
          draft: false
          prerelease: false
      
      # Get Tag
      - id: get-tag
        uses: olegtarasov/get-tag@v2
        with:
          tagRegex: "v(.*)"

      # Upload Release Asset
      - id: upload-release-asset 
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }} 
          asset_path: Dist/DemoProject.unitypackage
          asset_name: DemoProject.${{ env.GIT_TAG_NAME }}.unitypackage
          asset_content_type: application/zip
```

# まとめ
ライブラリについては、また次の機会に紹介しようと思います。
ご覧頂きありがとうございました！