JenkinsでUnityビルド(Mac,Windowsサーバ不使用)

# 経緯

  - JenkinsでUnityアプリをビルドしたい。
  - jenkinsはLinuxで動いている。
  - Macはない。
  - windowsでビルドしたくてもwindowsサーバはない。
  - そういえば、自分の作業用Windowsマシンならいつでも電源入ってるな…。

# 目次

  1. Windowsでビルドできるようにする
  1. cygwinでsshdを動かす
  1. Jenkinsのジョブ作成
  1. 注意

# Windowsでビルドできるようにする

コマンドラインビルドについてはググればたくさんでてくると思うけど一応。

Assets/Editorの下にスクリプトを作る。
今回はBuildBatch.csとした。

```cs:BuildBatch.cs
using UnityEditor;
using UnityEngine;
using System;
using System.IO;

public class BuildBatch
{
    private struct BuildParameter
    {
        public string outputFileName;
        public BuildTarget targetPlatform;
        public BuildParameter(string outputFileName, BuildTarget targetPlatform)
        {
            this.outputFileName = outputFileName;
            this.targetPlatform = targetPlatform;
        }
    }

    // ビルド対象シーンリスト
    private static string[] sceneList = {
            "./Assets/ほげ.unity",
            "./Assets/ふご.unity",
    };

    //    出力ディレクトリ。個人のローカル環境のフルパスだぞ！
    private static string outputDirectory = ないしょだよ！;

    private static void BuildByParameter(BuildParameter param)
    {
        string outputFileName = param.outputFileName;
        BuildTarget targetPlatform = param.targetPlatform;

        // 実行
        string errorMessage = BuildPipeline.BuildPlayer(
                sceneList,                    //    ビルド対象シーンリスト
                outputFileName,                //    出力先
                targetPlatform,                //    ビルド対象プラットフォーム
                BuildOptions.Development    //    ビルドオプション
        );

        // 結果出力
        if(!string.IsNullOrEmpty(errorMessage))
        {
            Debug.LogError("Error!");
            Debug.LogError(errorMessage);
            throw new Exception();
        }

        Debug.Log("Complete!");
    }

    public static void BuildApk()
    {
        BuildParameter param = new BuildParameter(outputDirectory + @"\android\output.apk", BuildTarget.Android);
        BuildByParameter(param);
    }

    public static void BuildExe()
    {
        BuildParameter param = new BuildParameter(outputDirectory + @"\exe\output.exe", BuildTarget.StandaloneWindows);
        BuildByParameter(param);
    }

    public static void BuildWebGL()
    {
        BuildParameter param = new BuildParameter(outputDirectory + @"\webgl\output", BuildTarget.WebGL);
        BuildByParameter(param);
    }
}
```

シーンとか出力パスとかは適宜読み替えてください。

いちいちコマンド叩きたくないのでbatファイルを作る。

```bat:build_apk.bat
"c:\Program Files\Unity\Editor\Unity.exe" -batchmode -quit -logFile .\build_apk.log -projectPath {Unityプロジェクトのパス} -executeMethod BuildBatch.BuildApk
```

apk以外の場合は、executeMethodのところをBatchBuild.csで定義されている他のビルド関数(BuildExe等)に変更する。
各オプションの意味は↓。

|オプション|内容|
|:--:|:--:|
| batchmode     | バッチモードで実行 |
| quit          | ビルド終了後Unityを終了する |
| logFile       | ビルドログの出力先指定 |
| projectPath   | ビルドターゲットのプロジェクトを指定 |
| executeMethod | ビルド処理を記述したクラス.関数 |

# cygwinでsshdを動かす 

[ここ](https://docs.oracle.com/cd/E26854_01/install.12101/b65084/preinstall_req_cygwin_ssh.htm)を参考にした。
私が実際にやった作業は↓。

```
$ ssh-host-config
コマンドを実行後、次のプロンプトが表示されます。

*** Query: Should privilege separation be used? <yes/no>: yes
*** Query: New local account 'sshd'? <yes/no>: yes
*** Query: Do you want to install sshd as a service?
*** Query: <Say "no" if it is already installed as a service> <yes/no>: yes
*** Query: Enter the value of CYGWIN for the deamon: [] binmode ntsec
*** Info: 'cyg_server' will only be used by registered services.
*** Query: Do you want to use a different name? (yes/no) no
*** Query: Create new privileged user account 'cyg_server'? (yes/no) yes
*** Info: that this password matches the password rules given on your system.
*** Info: Entering no password will exit the configuration.
*** Query: Please enter the password:
***Query: Renter:

Host configuration finished. Have fun!
```

グローバルIPは持っていないからまあ大丈夫だろうけど、同僚のｽｰﾊﾟｰﾊｶｰが怖いので、せめてパスワード認証を無効にする。

```sh:/etc/sshd_conf
#PasswordAuthentication yes
PasswordAuthentication no
```

sshdの起動＆停止は以下のコマンド。

```
$ cygrunsrv --start sshd
$ cygrunsrv --stop sshd
```

# Jenkinsのジョブ作成

Jenkinsサーバ上でパスフレーズなしの鍵を作って、自分の作業用マシンのcygwinに登録。
Jenkinsサーバからsshで接続確認。パスワード・パスフレーズ聞かれずに接続できるはず。

しかし、Jenkinsサーバから

```
$ ssh "～/build.bat"
```

としようとすると実行できない。
cygwinから直接ならできる。
なぜだ。
このあたりで疲れてきたので、原因は調べずに逃げることにした。(ｶｽ
cygwin上でbatを起動するbuild.shを作成した。絶対やりかた間違っている。

```sh:build.sh
#!/bin/bash

if [[ $1 == *a* ]];
then
    ./build_apk.bat;
    cat ./build_apk.log;
fi

if [[ $1 == *e* ]];
then
    ./build_exe.bat;
    cat ./build_exe.log;
    chmod 775 exe/{Unityが出力するexe用のデータディレクトリ} -R;
fi

if [[ $1 == *w* ]];
then
    ./build_webgl.bat;
    cat ./build_webgl.log;
    chmod 775 webgl/output -R;
fi
```
ついでに、第一引数でビルドするプラットフォームを指定できるようにした。

あとはジョブの作成。
gitリポジトリを監視して、更新があったらpullしてきたものを、rsyncで作業用windowsマシンへ転送。
ssh越しにオプション付きでbuild.shを実行する。
できたものはscpでJenkisサーバ上へ逆輸入する。

```shell-session
SRC_USER=Linuxマシンのユーザー(鍵登録したやつ)
TARGET_USER=作業用Windowsマシンのユーザー(cygwin動いてるやつ)
TARGET_IPADDR='192.168.123.45';
TARGET_WORKSPACE=作業用windowsマシン上でのワークスペース(テンポラリ)

cd ${WORKSPACE};

# 作業用windowsマシンに上最新コードをrsyncする。
sudo -u ${SRC_USER} rsync -av --delete ./* ${TARGET_USER}@${TARGET_IPADDR}:${TARGET_WORKSPACE};

# ビルドターゲット
build_option="";
if [ ${EXE} == true ];
then
  build_option="${build_option}e";
fi
if [ ${APK} == true ];
then
  build_option="${build_option}a";
fi
if [ ${WebGL} == true ];
then
  build_option="${build_option}w";
fi

echo $build_option;

# ビルドして成果物を回収
sudo -u ${SRC_USER} ssh ${TARGET_USER}@${TARGET_IPADDR} "cd cygwin上のbuild.shがあるパス; ./build.sh ${build_option};";
sudo -u ${SRC_USER} rsync -av ${TARGET_USER}@${TARGET_IPADDR}:exe出力したやつ/*     exe回収ディレクトリ;
sudo -u ${SRC_USER} rsync -av ${TARGET_USER}@${TARGET_IPADDR}:apk出力したやつ/*     apk回収ディレクトリ;
sudo -u ${SRC_USER} rsync -av ${TARGET_USER}@${TARGET_IPADDR}:webgl出力したやつ/*   webgl回収ディレクトリ;
```

# 注意

作業用WindowsマシンでUnity起動していると多重起動になってビルド失敗します。
致命的。

X使えばLinuxでもビルドできるという噂を聞いた。
試していないけど、Xいれていいサーバならこっちのほうが楽なんだろう。