OpenUPMにパッケージを公開する＆リリースの自動化をする

# はじめに

この記事ではUnityのパッケージを[OpenUPM](https://openupm.com/)に公開する方法を解説します。

こんな人におすすめです。

- PackageManagerで自作パッケージを管理・公開したい
- Assetsフォルダ下にパッケージが多すぎるから整理したい


# 公開手順

以下は実際に運用しているリポジトリなので、もし詰まったときは参考にしてみてください。

https://github.com/mackysoft/Vision


## 1. package.jsonを作成・記述

パッケージのルートフォルダにしたいフォルダに`package.json`ファイルを作成・記述します。

例として、僕のパッケージの`package.json`を挙げます。

```json:package.json
{
	"name": "com.mackysoft.vision",
	"displayName": "Vision",
	"version": "1.1.4",
	"unity": "2019.4",
	"description": "CullingGroup API for everyone.",
	"keywords": [ "Vision", "CullingGroup" ],
	"license": "MIT",
	"dependencies": {}
}
```

以下は特に重要な項目です。

|項目|説明|
|:--|:--|
|name|パッケージのアドレスになる（[命名規則](https://docs.unity3d.com/Manual/cus-naming.html)）|
|version|この値が更新されるとOpenUPMに新しいバージョンのパッケージが発行される。リリース時以外は手を出してはいけない。|
|unity|パッケージの使用に必要なUnityの最小バージョン|

## 2. Open UPMにパッケージを追加する

[Package Add](https://openupm.com/packages/add/)にアクセスし、OpenUPMにパッケージを追加する手続きを行います。

https://openupm.com/packages/add/

以下はOpenUPMにパッケージを追加する大まかな手順です。

1. 自分のリポジトリのアドレスを入力して`Go`をクリックする。
2. パッケージの情報を入力して、`Verify package`をクリックする。
3. OpenUPMのリポジトリをForkして、Pull Requestする。
4. Pull Requestすると、OpenUPMが勝手に色々やってくれる。

この手順が完了すると、OpenUPMを介して自分のパッケージをインストールできるようになります。
さらに[このようなページ](https://openupm.com/packages/com.mackysoft.vision/)が作成されます。

OpenUPMにパッケージを公開する手順は以上になります。

# （おまけ）リリースの自動化

ここからはやらなくても構いませんが、やっておくと以下のことを自動で行えるようになります。

- `package.json`のversionを更新
- リポジトリにリリース（ドラフト）とタグを作成
- unitypackageのエクスポートおよびアップロード

気になった方は、やってみるといいかもしれません。

## 1. unitypackageをエクスポートするスクリプトを記述する

GitHub Actions上でunitypackageをエクスポートするためのスクリプトを記述します。
（パッケージ名などは自身の物に合わせてください）

```cs:UnityPackageExporter.cs
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;

namespace MackySoft.Vision.Editor.PackageTools {

	/// <summary>
	/// It is used to create the Vision package. It is not used by the user.
	/// </summary>
	public static class UnityPackageExporter {

		// The name of the unitypackage to output.
		const string k_PackageName = "Vision";

		// The path to the package under the `Assets/` folder.
		const string k_PackagePath = "MackySoft";

		// Path to export to.
		const string k_ExportPath = "Build";

		const string k_SearchPattern = "*";
		const string k_PackageToolsFolderName = "PackageTools";

		public static void Export () {
			ExportPackage($"{k_ExportPath}/{k_PackageName}.unitypackage");
		}

		public static string ExportPackage (string exportPath) {
			// Ensure export path.
			var dir = new FileInfo(exportPath).Directory;
			if (dir != null && !dir.Exists) {
				dir.Create();
			}

			// Export
			AssetDatabase.ExportPackage(
				GetAssetPaths(),
				exportPath,
				ExportPackageOptions.Default
			);

			return Path.GetFullPath(exportPath);
		}

		public static string[] GetAssetPaths () {
			var path = Path.Combine(Application.dataPath,k_PackagePath);
			var assets = Directory.EnumerateFiles(path,k_SearchPattern,SearchOption.AllDirectories)
				.Where(x => !x.Contains(k_PackageToolsFolderName))
				.Select(x => "Assets" + x.Replace(Application.dataPath,"").Replace(@"\","/"))
				.ToArray();
			return assets;
		}

	}
}
```

- `Build/Vision.unitypackage`を作成
- パスに`PackageTools`が含まれるファイルは除外

[Scripting API: AssetDatabase.ExportPackage](https://docs.unity3d.com/ScriptReference/AssetDatabase.ExportPackage.html)

## 2. release.yamlを記述する

GitHub Actions を利用し、パッケージを半自動でリリースするワークフローを記述します。
（パッケージ名などは自身の物に合わせてください）

`package.json`内の`version`を書き換えることで、OpenUPMに新しいバージョンのパッケージが発行される仕組みになっています。

```yaml:release.yaml
name: Release

on:
  workflow_dispatch:
    inputs:
      tag:
        description: "tag: git tag you want create. (sample 1.0.0)"
        required: true

env:
  GIT_TAG: ${{ github.event.inputs.tag }}

jobs:
  update-packagejson:
    runs-on: ubuntu-latest
    env:
      TARGET_FILE: ./Assets/MackySoft/MackySoft.Vision/package.json
    outputs:
      sha: ${{ steps.commit.outputs.sha }}
    steps:
      - uses: actions/checkout@v2
      - name: Output package.json (Before)
        run: cat ${{ env.TARGET_FILE}}

      - name: Update package.json to version ${{ env.GIT_TAG }}
        run: sed -i -e "s/\(\"version\":\) \"\(.*\)\",/\1 \"${{ env.GIT_TAG }}\",/" ${{ env.TARGET_FILE }}

      - name: Check update
        id: check_update
        run: |
          cat ${{ env.TARGET_FILE}}
          git diff --exit-code || echo "::set-output name=changed::1"
      - name: Commit files
        id: commit
        if: steps.check_update.outputs.changed == '1'
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git commit -m "feat: Update package.json to ${{ env.GIT_TAG }}" -a
          echo "::set-output name=sha::$(git rev-parse HEAD)"
      - name: Check sha
        run: echo "SHA ${SHA}"
        env:
          SHA: ${{ steps.commit.outputs.sha }}

      - name: Create Tag
        if: steps.check_update.outputs.changed == '1'
        run: git tag ${{ env.GIT_TAG }}

      - name: Push changes
        if: steps.check_update.outputs.changed == '1'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
          tags: true

  build:
    needs: [update-packagejson]
    strategy:
      matrix:
        unity: ["2020.3.4f1"]
        include:
          - unityVersion: 2020.3.4f1
            license: UNITY_LICENSE
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - run: echo ${{ needs.update-packagejson.outputs.sha }}
      - uses: actions/checkout@v2
        with:
          ref: ${{ needs.update-packagejson.outputs.sha }}

      - name: Export unitypackage
        uses: game-ci/unity-builder@v2
        env:
          UNITY_LICENSE: ${{ secrets[matrix.license] }}
        with:
          projectPath: .
          unityVersion: ${{ matrix.unityVersion }}
          targetPlatform: StandaloneLinux64
          buildMethod: MackySoft.Vision.Editor.PackageTools.UnityPackageExporter.Export
          versioning: None

      - name: check all .meta is commited
        run: |
          if git ls-files --others --exclude-standard -t | grep --regexp='[.]meta$'; then
            echo "Detected .meta file generated. Do you forgot commit a .meta file?"
            exit 1
          else
            echo "Great, all .meta files are commited."
          fi
        working-directory: .

      # Store artifacts.
      - uses: actions/upload-artifact@v2
        with:
          name: Vision.unitypackage
          path: ./Build/Vision.unitypackage

  create-release:
    needs: [update-packagejson, build]
    runs-on: ubuntu-latest
    steps:
      # Create Releases
      - uses: actions/create-release@v1
        id: create_release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.GIT_TAG }}
          release_name: ${{ env.GIT_TAG }}
          commitish: ${{ needs.update-packagejson.outputs.sha }}
          draft: true
          prerelease: false

      # Download(All) Artifacts to current directory
      - uses: actions/download-artifact@v2
      
      # Upload to Releases(unitypackage)
      - uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./Vision.unitypackage/Vision.unitypackage
          asset_name: Vision.unitypackage
          asset_content_type: application/octet-stream
```

https://qiita.com/makihiro_dev/items/4e2d83ad0d268b43f4a4

## 3. リリースする

https://github.com/mackysoft/Vision/actions/workflows/release.yaml

1. リポジトリの`Actions > Release ワークフロー`に移動する。
2. `Run workflow`ドロップダウンをクリックする。
3. `tag`にリリースするバージョンを入力する。（例：`1.1.3`）
4. `Run workflow`ボタンをクリックする。

これで先ほど書いた`release.yaml`のワークフローが走り、

- `package.json`のversionを更新
- リポジトリにリリース（ドラフト）とタグを作成
- unitypackageのエクスポートおよびアップロード

これらを自動で行ってくれます。


# おわりに

この記事の方法で実際に運用しているリポジトリです。
分からないことがあった場合は参考にしてみてください。

https://github.com/mackysoft/Vision


# 参考
- [Package Managerに自作パッケージを追加する2019年版](https://note.com/fuqunaga/n/n683474b8a663)
- [Adding UPM Package](https://openupm.com/docs/adding-upm-package)
- [UniTask/.github/workflows/build-release.yml](https://github.com/Cysharp/UniTask/blob/master/.github/workflows/build-release.yml)