Unityゲームの手動デプロイで事故りまくったので自動化した話

これはUnityゲームのデプロイを手動でやったら手元が狂って事故りまくったので、自動化した話です

https://github.com/Clpsplug/ucb-webhook-receiver

## ４つって意外と多いんです

私は[SteamにてUnity製の音楽ゲーム"Twin Horizons"](https://store.steampowered.com/app/1352730/)を出しているが、そのゲームをSteamにアップデートなどで送る時には「(自分で決定できる)特定のディレクトリ」に置いておかなければならない。

レポジトリ側には[git-flow](https://danielkummer.github.io/git-flow-cheatsheet/index.html)を適用しているので、 `base` ブランチにコミットがあれば、Unity Cloud Buildでビルドが走るところまでは自動化できていた。_やっと_ UCBでWindows用のIL2CPPビルドができるようになってよかった。

ビルドされるゲームのバージョンは４つだ。
* macOS Production Build
* macOS Developer Build w/ development tools
* Windows Production Build
* Windows Developer Build w/ development tools

「**Production Build**」はゲームを普通に購入した人が受け取る実行ファイル、「**Developer Build**」は開発者や内部テスター、ステージ制作者向けの実行ファイルだ。

これらがビルドされたら、私は自分で
1. それらをUCBからダウンロードして
1. あらかじめ決めた特定のディレクトリにそれぞれのバージョンを(コピー&ペーストで)配置して
1. 別口でビルドしていたAddressableバンドルを追加する

という作業をしなければならなかった。

## 問題発生

数ヶ月前のことである。テスターの一人が、あるアップデートでゲームが壊れたと言い出したのだ。  

私はログを集めた。Unity上でもテストした。なんなら、ローカルでビルドして試してみた。全く再現しない。というか、彼の受け取ったビルドでそんなことが起こっていること自体あり得ないバグだった。

...と思っていた。

どうやら、ビルドされたゲームをディレクトリに配置していくときに、**誤ってProductionバージョン(の実行ファイル)をdevバージョン(用のアップロードディレクトリ)に配置していたようだ。**

![FACEPALM](https://upload.wikimedia.org/wikipedia/commons/3/3b/Paris_Tuileries_Garden_Facepalm_statue.jpg)
> Alex E. Proimos, [CC BY 2.0](https://creativecommons.org/licenses/by/2.0), via Wikimedia Commons


**本当に逆じゃなくてよかった。**

...よく考えたら、前にも同じような事故をやっていたことに気づいた。その時はテスト用のブランチでテストしていたから、通常のプレイヤーが変なビルドを見ることは回避されていたのだが...

## もうたくさんだ。手動なぞやめてしまおう

(╯°□°)╯︵ ┻━┻

調査してみるとUnity Cloud BuildはWebhookを送信できるらしい。どうにかしてこれを受け取って「ダウンロード→配置」までできればOKだ。

ただ、**どうやって**このWebhookを受け取るかが問題だ。

レンタルサーバーは持っている。ただしそれはWebページ用だ。ここでやろうとしていることをやったものなら、サーバーにプロセスをぶった切られるリスクがある。  
VPSもアカウントはあるが少し高い。静的IPが欲しいからVPNを作るとか、そういう用途でない限りはあまり手を出したくない。

そこで、自分の古いPCからホストできたらどうか、と気づいた。

ちょっと調べてみると[Loophole](https://loophole.cloud/)という無料トンネルサービスがあることを見つけた。この手法でずっと開いておくのはあまり喜ばれたことではないかもしれないが、個人レベルの自動化プロジェクトならしばらくこれでも大丈夫だろう。これで自分のPCでWebhookを受け取る算段ができた。

https://github.com/loophole/cli

あとはどうやってHTTPリクエストに応答しつつ裏でスクリプトを走らせるかを学ぶだけだ。

## どうやってサーバ側のコードを書く？

最初、Apache2を使うことも考えたが、よく考えたらやろうとしていることに対して大袈裟すぎる気がした。Apache2は高機能なwebサーバだ。ただただWebhookを受け取りたいだけの目的には機能が多すぎる。  
そもそも、Jenkinsも同じような理由で使うのをやめていた。もし、自分のマシンでビルドまでやっていたのであればJenkinsを使う理由もあったが、今やりたいのは「ビルドしたゲームのダウンロードとそのゲームを正しい位置に配置する」ことだけだ。   
自分の知っている言語で、あるポートへの通信を監視するだけの軽量なサーバライブラリがあれば十分だった。

調べてみるとPythonで [CherryPy](https://cherrypy.dev/) という軽量Webフレームワークがあることを知った。裏でプロセスを立ち上げてもHTTPリクエストを処理できることも確認できた。今回は、このフレームワークに乗ることにした。

https://github.com/cherrypy/cherrypy

## 何をダウンロードすれば良い？

ビルド成功時のペイロードはこんな形をしている (ほとんどの情報はダミーに置き換えている):

https://gist.github.com/Clpsplug/c62af18ca0428a173584c3a4b99900db

配列 `artifacts` はこれ以外にもデバッグ用のシンボルファイルなどの多くの要素を持っているのだが、重要なのは `primary` キーが `true` のものだ。ヘッドレスビルドなどをしていない限り、これがゲームとなる。

ちなみに、自分で生成したシークレットキーを登録して、 `x-unitycloudbuild-signature` を使ってペイロードの検証も可能である。共通鍵とHMAC-SHA256の２つの手法があるが、今回紹介しているコードでは、HMAC-SHA256を使って検証している。

## 結果

何回かファイルシステムをメチャクチャにしてしまったが、完成品がこちら:

https://github.com/Clpsplug/ucb-webhook-receiver

このプロジェクトは、UCBからWebhookを受け取ったら、ペイロードの指定する成果物をダウンロードし、出力フォルダに配置すると共に、別に追加する必要のあるフォルダ・ファイルもまとめて配置してくれる。

あとはSteamのツールの確認先をこのディレクトリに向ければいいだけだ。もうバイナリの種類を間違うことはないだろう。

ちなみに: 間違ってファイル全消しとかにならないためにも、Dockerコンテナで動かせるようにした。多分こちらの方が安全だと思います

さらにちなみに: 自分の環境以外でテストしていないので、試される方はご注意ください - もし動かない等あればissueを開いていただけると嬉しいです