CI
Unity
CircleCI
docker

CircleCIでUnityのTest&Buildを雰囲気理解で走らせた


CircleCIを使ってみたかった

CircleCIなら制限時間はありますが無料でCI環境が使えて、GitHubから直接連携ができるので楽そう!

またSlack連携なども情報はたくさんあるので応用は色々とできそう!

…Unityで動かす情報すくないな?ってことで触ってみました。

さて今回参考にしたRepositoryは2つです

- GabLeRoux/unity3d-ci-example

- wtanaka/docker-unity3d

上記からunity3d-ci-exampleをforkして

- MizoTake/unity3d-ci-example

を作ってCircleCI連携を試してみたものの、そのままではうまく行かなかったです。

なので、どう解決したのかをunity3d-ci-exampleをベースに話します。


セットアップ

CircleCIやDockerの登録やらなんやらの説明は省きます。

https://circleci.com/

基本的には公式サイトに行ってGitHubやBitBucketの連携登録をすれば簡単にできるかと思います。

https://www.docker.com/

MacやLinuxの方はすんなりセットアップできると思います、Windowsの方はセットアップして起動できてなさそうであればhyper-vという項目をONにしないといけないのでこちらを参考にしてみてください。

unity3d-ci-exampleで最初わからなかったのがREADMEにあるHow to activateという項目…

要するにUnityのLicense認証なのですが少し手間でした。

またこの段階からDockerが必要となります。Docker初心者ですが調べつつ何とかなったのでコマンドも覚えているものは残しておきます。

今回使用するContainerはgableroux/unity3dです。

DockerHubからimageを落として来ないといけないので落としてきます

docker pull gableroux/unity3d

落とせたかどうかは

docker images

で確認できます。

落とせていたら

UNITY_VERSION=2018.2.3f1

docker run -it --rm \
-e "UNITY_USERNAME=username@example.com" \
-e "UNITY_PASSWORD=example_password" \
-e "TEST_PLATFORM=linux" \
-e "WORKDIR=/root/project" \
-v "$(pwd):/root/project" \
gableroux/unity3d:$UNITY_VERSION \
bash

UNITY_VERSIONやUNITY_USERNAME、UNITY_PASSWORDは適宜変えましょう。

何をしているかというとgableroux/unity3dを起動させるときに環境変数として値を渡しているみたいです。

コマンドの処理が終わるとDockerが起動してDocker上のbashが操作できるようになります。

一回Dockerから出たい時は

exit

だけ打てば出れます。再度入る時は入った時と同じコマンドでいいと思います。

次にDocker上で

xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \

/opt/Unity/Editor/Unity \
-logFile \
-batchmode \
-username "$UNITY_USERNAME" -password "$UNITY_PASSWORD"

Unityの起動を行いLicense認証をしようと動きます。

うまくいくと

LICENSE SYSTEM [2017723 8:6:38] Posting <?xml version="1.0" encoding="UTF-8"?><root><SystemInfo><IsoCode>en</IsoCode><UserName>[...]

というxmlがログに現れるのでunity3d.alfという名前でホストのOSに保存しましょう。

次に https://license.unity3d.com/manual をホストのOSで開いてunity3d.alfを選択して送信します。

するとUnity_v2018.x.ulfというファイルがダウンロードされます。

How to activate的には、ここまでで区切りですがCircleCIの場合はもう少しセットアップが必要です。

Unity_v2018.x.ulfというのがUnityのLicenseあるよ!という証明するファイルになるのですが、そのままgit管理してしまうのは危険かもしれません。PlusライセンスやProライセンスだと他の人がアクティベートできてしまうからです。(たぶん)

そこで自分でKEYを決めて暗号化をかけてCircleCI上で復号化して使おうじゃないか!という流れです。

次もホストOSでの処理になります。

export KEY=insert-your-strong-generated-key-here

openssl aes-256-cbc -e -in ci/Unity_v2018.x.ulf -out ci/Unity_v2018.x.ulf-cipher -k $KEY
git add ci/Unity_v2018.x.ulf-cipher
git commit -m "Add encrypted Unity_v2018.x.ulf using openssl aes-256-cbc KEY"

insert-your-strong-generated-key-hereを好きな文字列に置き換えて覚えておいてください。後々CircleCIの環境変数として保存します。

そして暗号化されたファイルがUnity_v2018.x.ulf-cipherとなります。間違ってもUnity_v2018.x.ulfを追加しないように気をつけてください。

そのためリポジトリとは別のディレクトリで作業してUnity_v2018.x.ulf-cipherのみリポジトリに追加する方が安全かもしれません。


CircleCIに環境変数を登録する

先ほど暗号化に使ったKEYをCircleCIに登録します。

CircleCIとリポジトリを連携するとリポジトリに対して設定が行えるようになります。設定画面にある

名称未設定.png

Enviroment Variablesの項目があり選択すると画像にはないですが右上にAdd VariablesというボタンがあるのでKEYという名前で環境変数を登録しましょう。

セットアップは終わりです。


config.ymlを書くぞ

CicleCIで処理して欲しい項目をyml形式で書くことになります。

保存場所はリポジトリのルートに.circlciフォルダを作って配下にconfig.ymlを追加してください。

今回使用したのは以下のymlです。(unity3d-ci-sampleのものをベースにしました)


config.yml

version: 2

references:
docker_image: &docker_image
docker:
- image: gableroux/unity3d:2018.2.6f1
setup_unity_license_env_var: &setup_unity_license_env_var
command: |
mkdir -p /root/.cache/unity3d
mkdir -p /root/.local/share/unity3d/Unity/
openssl version
openssl aes-256-cbc -md md5 -d -in ./ci/Unity_v2018.ulf-cipher -out /Unity_v2018.ulf -k $KEY
export UNITY_LICENSE_CONTENT=`cat /Unity_v2018.ulf`
echo "$UNITY_LICENSE_CONTENT" | tr -d '\r' > "/root/.local/share/unity3d/Unity/Unity_lic.ulf"
remove_license_file: &remove_license_file
command: |
rm /Unity_v2018.ulf
rm /root/.local/share/unity3d/Unity/Unity_lic.ulf
jobs:
test_editmode:
<<: *docker_image
steps:
# TODO: Add git to unity image so this is not required anymore
# this will prevent following error on 'checkout' step:
# Either git or ssh (required by git to clone through SSH) is not installed in the image. Falling back to CircleCI's native git client but the behavior may be different from official git. If this is an issue, please use an image that has official git and ssh installed.
- run:
command: apt-get update && apt-get install -y git && git --version
- checkout
- run:
<<: *setup_unity_license_env_var
- run:
environment:
TEST_PLATFORM: editmode
command: |
chmod -R 755 ./ci/test.sh
./ci/test.sh
- run:
<<: *remove_license_file
- store_artifacts:
path: '$(pwd)/$TEST_PLATFORM-results.xml'
destination: '$TEST_PLATFORM-results.xml'
test_playmode:
<<: *docker_image
steps:
- run:
command: apt-get update && apt-get install -y git && git --version
- checkout
- run:
<<: *setup_unity_license_env_var
- run:
environment:
TEST_PLATFORM: playmode
command: |
chmod -R 755 ./ci/test.sh
./ci/test.sh
- run:
<<: *remove_license_file
- store_artifacts:
path: '$(pwd)/$TEST_PLATFORM-results.xml'
destination: '$TEST_PLATFORM-results.xml'
build_StandaloneWindows:
<<: *docker_image
steps:
- run:
command: |
apt-get update && apt-get install -y git zip && git --version
- checkout
- run:
<<: *setup_unity_license_env_var
- run:
environment:
BUILD_TARGET: StandaloneWindows
command: |
chmod -R 755 ./ci/build.sh
./ci/build.sh
- run:
<<: *remove_license_file
- store_artifacts:
path: './Builds/'
build_StandaloneOSX:
<<: *docker_image
steps:
- run:
command: |
apt-get update && apt-get install -y git zip && git --version
- checkout
- run:
<<: *setup_unity_license_env_var
- run:
environment:
BUILD_TARGET: StandaloneOSX
command: |
chmod -R 755 ./ci/build.sh
./ci/build.sh
- run:
<<: *remove_license_file
- store_artifacts:
path: './Builds/'
workflows:
version: 2
test_and_build:
jobs:
- build_StandaloneWindows
- build_StandaloneOSX
- test_editmode


これが

コメント 2019-02-07 233248.jpg

こうなります。

少し分解して解説を入れます。

setup_unity_license_env_varだけを見ます。

# なんでこれ追加したんだったか…無くて怒られたような…

mkdir -p /root/.cache/unity3d
mkdir -p /root/.local/share/unity3d/Unity/
openssl version
# 暗号化したファイルを復号化する処理
openssl aes-256-cbc -md md5 -d -in ./ci/Unity_v2018.ulf-cipher -out /Unity_v2018.ulf -k $KEY
export UNITY_LICENSE_CONTENT=`cat /Unity_v2018.ulf`
# 復号化した中身をファイルとしてUnity配下に保存
echo "$UNITY_LICENSE_CONTENT" | tr -d '\r' > "/root/.local/share/unity3d/Unity/Unity_lic.ulf"

復号化した中身をファイルとしてUnity配下に保存をしないと自分の場合動きませんでした。unity3d-ci-exampleにはなかったのですがwtanaka/docker-unity3dにはその記述があり書いてみたらライセンスが通りました。。。先駆者たちに感謝っ・・・・!圧倒的感謝っ・・・・!

あとはunity3d-ci-exampleのciフォルダ配下の.shに権限与えて動かしているだけです。

.shの中身はunityをコマンドで動かしているだけなので色々といじれるとは思います。

編集したり調べる際にはコマンド引数のリファレンスにだいたい乗っているので確認してみてください。

Testは基本的に成功知れていればいいと思いますがBuildは成果物がダウンロードできないと意味がないのでダウンロードする用意をします。

CircleCIにはArtifactsという機能がありArtifactsに保存したい成果物を登録しておけば長期的に保存ができるものになります。

unity3d-ci-sampleもBuildフォルダというものを作っておりフォルダごと保存するようになっています。

jobの詳細画面?でArtifactsが確認できます。

コメント 2019-02-07 231417.jpg

フォルダを成果物としているのでかなりバラバラになっています。(-customBuildPathの環境変数登録してなくてそのままなのはスルー)

1つ1つダウンロードしてられないのでzipにまとめます。


config.yml

build_StandaloneWindows:

<<: *docker_image
steps:
- run:
command: |
apt-get update && apt-get install -y git zip && git --version
- checkout
- run:
<<: *setup_unity_license_env_var
- run:
environment:
BUILD_TARGET: StandaloneWindows
command: |
chmod -R 755 ./ci/build.sh
./ci/build.sh
- run:
<<: *remove_license_file
- store_artifacts:
path: './Builds/'

一部抜粋ですが、apt-getでgitのついでにzipも一緒にインストールしておきます。最後の方にartifactsに登録しているのがわかると思います。

そしてbuild.shの一部を編集します。


build.sh

if [ $UNITY_EXIT_CODE -eq 0 ]; then

echo "Run succeeded, no failures occurred";
cd $BUILD_PATH
zip archive -r .
cd /root/project
elif [ $UNITY_EXIT_CODE -eq 2 ]; then
echo "Run succeeded, some tests failed";
elif [ $UNITY_EXIT_CODE -eq 3 ]; then
echo "Run failure (other failure)";
else
echo "Unexpected exit code $UNITY_EXIT_CODE";
fi

成功したときにartifactsに登録するディレクトリに移動してzip処理をします。そして後処理があるのでディレクトリをもとの位置に戻ります。

上記の編集を加えると

コメント 2019-02-07 231229.jpg

archive.zipになっているのがわかります。archive.zipを落として解答すればビルド結果が確認できるかと思います。


まとめ

CircleCIで一旦UnityのTestとBuildを動かす話はここまでです。Sampleをあげてくれている方がおりますがそのまま動かない場合があったので今回記事に残しておきます。

そのうちAndroidやiOSのビルドもCircleCIでやりたいと思ってはいます…ネイティブのビルド自体はfastlaneとかでやっている人をみるので応用すればいけるかな?と思ってますがDocker知識が地味に必要そうなのでやる気次第ですぐにやるかもしれない…しないかもしれない…

CircleCIやDockerは完全に初心者でやっているのでもっと効率のいい書き方とかあるよって話があれば教えて欲しいです!