Jenkins+Unity+Windows　久しぶりなのでメモ（バッチモードしんどい）

###インストールの仕方

　参考URL：https://itsakura.com/tool-jenkins-install

　※Widnowsの場合、注意が必要。
　　ProgamFilesフォルダに馬鹿正直にインストールしたところ、
　　workspaceがその中に作られてcloneするまではよかったが
　　内部のUnityプロジェクトが読み書き不可能エリアらしくて開けなかった。
　　

### Global Tool Configuration


◆◆　**Git**　◆◆

　●Gitのexeパス設定（Git実行形式へのパス）
　　私はSourceTree入れてるので、下記パスを入力
　
　　C:\Users\XXXXX\AppData\Local\Atlassian\SourceTree\git_local\bin\git.exe

◆◆　**Unity**　◆◆

　●Jenkins-->プラグインの管理-->利用可能タブ-->Unity3d を入れる（再起動せずにインストール）

　●インストールディレクトリの設定（ディレクトリ名なので、そこまでのパスを設定）
　　C:\Program Files\Unity\Hub\Editor\2018.3.8f1\Editor


### GitHubのリポジトリをクローン

セキュリティ的にはよくないが確認するだけなら、
まずはこれで＠以降を自分のリポジトリにして
ユーザー名とパスワードを設定すればクローンできるっぽい

書式：https://username:password@bitbucket.org/username/repo-name.git

参考URL：https://gist.github.com/n-shinya/5675834

これでもエラーが出る場合、
例えば　returned status code 128

リポジトリURLの下にある認証情報をセットしてCloneする。
↓
認証情報を設定するためには、
Jenkinsの認証情報->System->グローバルドメインを選択
![グローバルドメイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/a9ba0f13-fe93-ed69-31ab-b7c5f63bb37a.png)
↓
追加する。

Cloneが成功すると、なぜか他のジョブでは認証情報を設定しなくても
https://username:password@********.git　

でクローンできた謎ですね。（一旦、Jenkinsの認証を通す必要があるのかな？）

### ビルド手順の追加

　「Widnowsバッチコマンドの実行」を追加して下記を記述

Unityのexeパス -batchmode -quit -projectPath プロジェクトパス -executeMethod クラス名.メソッド名

（注）何回やってもエラーになるので、コマンドプロンプトから実行、でもエラー。
「このアプリはお使いのPCでは実行できませんと表示される」
同じ職場の方にも、調べてもらったら実行はできて、・・・あれってなりました。
インストールしたunityのエグゼファイルのプロパティを見ると、私の方にはデジタル署名のタブがありませんでした。
とりあえずインストールし直しました。

（注１）クラス名とメソッド名は**public static**じゃないと呼べないかも
（注２）名前空間も関係していて、**usingに移動する必要あり**、
　　　　**名前空間ありクラスだとエラー**がでる、
　　　　コマンドプロンプトからバッチモード起動をでエラーが確認できました（Editor.log参照）。

 （注３）

### バッチモードで呼び出し成功例

◆呼び出し側
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -buildTarget プラットフォーム -projectPath C:\Users\XXXXX\Documents\プロジェクトフォルダ -executeMethod BatchTest.OutPutLog

◆呼び出される側

```
using UnityEditor;
using UnityEngine;

public class BatchTest
{
    public static void OutPutLog()
    {
        Debug.Log("123456790");
    }
}

```

◆Editor.log内の内容

```
123456790
UnityEngine.DebugLogHandler:Internal_Log()
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
```



### バッチモードで呼び出しエラー例１（staticではない）
◆呼び出し側
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -projectPath C:\Users\XXXXX\Documents\プロジェクトフォルダ -executeMethod BatchTest.OutPutLog

◆呼び出される側

```
using UnityEditor;
using UnityEngine;

public class BatchTest
{
    public void OutPutLog()
    {
        Debug.Log("123456790");
    }
}
```

◆Editor.log内の内容

```
executeMethod method 'OutPutLog' in class 'BatchTest' could not be found.
Argument was -executeMethod BatchTest.OutPutLog

```

### バッチモードで呼び出しエラー例２（名前空間あり）
◆呼び出し側
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -projectPath C:\Users\XXXXX\Documents\プロジェクトフォルダ -executeMethod BatchTest.OutPutLog

◆呼び出される側

```
using UnityEditor;
using UnityEngine;

namespace test.batch.test
{
    public class BatchTest
    {
        public static void OutPutLog()
        {
            Debug.Log("123456790");
        }
    }
}

```

◆Editor.log内の内容

```
executeMethod class 'BatchTest' could not be found.
Argument was -executeMethod BatchTest.OutPutLog
 
(Filename: C:\buildslave\unity\build\Runtime/Utilities/Argv.cpp Line: 354)


Aborting batchmode due to failure:
executeMethod class 'BatchTest' could not be found.
Argument was -executeMethod BatchTest.OutPutLog

Exiting without the bug reporter. Application will terminate with return code 1
```


### バッチモードで引数あり呼び出し成功例（シーン名を引数にする）
◆呼び出し側
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -projectPath C:\Users\XXXXX\Documents\プロジェクトフォルダ -executeMethod BatchTest.OutPutLog /scene シーン名


◆呼び出される側

```
using UnityEditor;
using UnityEngine;

/// <summary>
/// バッチモードビルドのテスト用クラス
/// </summary>
public class BatchTest
{
    public static void OutPutLog()
    {
        Debug.Log("123456790");
        GetSceneNameFromArgs();
    }

    /// <summary>
    /// バッチ処理側の引数を抜き出してシーン名を取得する。
    /// </summary>
    /// <returns></returns>
    private static string GetSceneNameFromArgs()
    {
        Debug.Log("GetSceneNameFromArgs");
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; ++i)
        {
            Debug.Log("GetSceneNameFromArgs i=" + i);
            Debug.Log("GetSceneNameFromArgs args[i]=" + args[i]);
            if (args[i] == "/scene")
            {
                Debug.Log("GetSceneNameFromArgs args[i + 1]=" + args[i + 1]);
                return args[i + 1];
            }
        }

        return string.Empty;
    }
}
```

### バッチモードで引数あり呼び出し成功例（クラス名の前に名前空間をつけて、シーン名を引数にする）
◆呼び出し側
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -projectPath C:\Users\XXXXX\Documents\プロジェクトフォルダ -executeMethod test.batch.test.BatchTest.OutPutLog /scene シーン名


◆呼び出される側

```
using UnityEditor;
using UnityEngine;

namespace test.batch.test
{
    /// <summary>
    /// バッチモードビルドのテスト用クラス
    /// </summary>
    public class BatchTest
    {
        public static void OutPutLog()
        {
            Debug.Log("123456790");
            GetSceneNameFromArgs();
        }

        /// <summary>
        /// バッチ処理側の引数を抜き出してシーン名を取得する。
        /// </summary>
        /// <returns></returns>
        private static string GetSceneNameFromArgs()
        {
            Debug.Log("GetSceneNameFromArgs");
            string[] args = System.Environment.GetCommandLineArgs();
            for (int i = 0; i < args.Length; ++i)
            {
                Debug.Log("GetSceneNameFromArgs i=" + i);
                Debug.Log("GetSceneNameFromArgs args[i]=" + args[i]);
                if (args[i] == "/scene")
                {
                    Debug.Log("GetSceneNameFromArgs args[i + 1]=" + args[i + 1]);
                    return args[i + 1];
                }
            }

            return string.Empty;
        }
    }
}

```





### Jenkins側のパラメーターをWindowsバッチコマンド経由で渡すには？

**Jenkins側のパラメーター設定**
![Jenkinsパラメータ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/278240be-565f-8fae-869f-9e7f4c634cf2.png)

**Jenkins側のWindowsバッチコマンド設定**
![Windowsバッチコマンド引数パラーメータあり.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/5b68c0b2-5565-9a1f-db0b-db24930028ac.png)

**Unity側のソース**

**渡ってきた引数パラメーターの取得**

```
/// <summary>
/// バッチ処理側の引数を抜き出して開発用ビルドパラメーターを取得する。
/// </summary>
/// <returns></returns>
private static string GetPathFromArgs()
{
    string[] args = System.Environment.GetCommandLineArgs();
    for (int i = 0; i < args.Length; ++i)
    {
        if (args[i] == "/build")
        {
            return args[i + 1];
        }
    }

    return string.Empty;
}
```

**開発用パラメーターを判定して開発用ビルドオプションを設定する**

```
private static BuildOptions _buildOptions = BuildOptions.None;

public static void WindowsBuild()
{
    var isDevelopment  = GetPathFromArgs();

    if(isDevelopment == "true")
    {
        _buildOptions = BuildOptions.Development;
    }

```

※参照URL：https://fuwafuwac.com/?p=214



### 「Windowsバッチコマンドの実行」を使用エリア内、curlで取得した値を変数に入れてパラメーターで渡す。

```
REM コメント
set CMD=curl URL
set GETVERSION=
for /f "usebackq delims=" %%a in (`%CMD%`) do set GETVERSION=%%a
echo %GETVERSION%

REM スペースを空文字に変換
set VersionStr=%GETVERSION: =%

echo %VersionStr%
```

### ログ出力ありバッチビルド
「Windowsバッチコマンドの実行」を使用（development Build等いろいろパラメーター化した場合の例）

```
"C:\Program Files\Unity2018_3_8_f1\Editor\Unity.exe" -batchmode -quit -logFile .\build.log -projectPath プロジェクトのフォルダパス -executeMethod 名前空間.クラス名.メソッド名
 /DevelopBuild %Jenkinsのパラメータ% /HostType %Jenkinsのパラメータ% /BundleVersionCode %Jenkinsのパラメータ% /Version %Jenkinsのパラメータ% /ServerVersionCode %CURLの結果% /Compiler %Jenkinsのパラメータ% /Signed %Jenkinsのパラメータ%
```


### Windows64ビルドは成功したが、Oculus用のGoビルドはapkが作られなかったので対処法メモ（めっちゃはまった）

エラー内容：**unity jenkins unable to locate android sdk**

参考URL
https://codingtrabla.blogspot.com/2016/03/jenkins-unity3d-plugin-unable-to-locate.html

システム環境変数にAndroidSDKのパスを通す必要があるみたい・・・Windowsだから？

さらに、Jenkins側にも設定が必要っぽい

Jenkinsの管理⇒システムの設定⇒グローバルプロパティ⇒環境変数にチェックをする。

キー：ANDROID_SDK_ROOT（なんでもいい気がする）
値：AndroidSDKのパス
（フォルダまで、わからなかったらUnityのpreferencesを開いてExternalToolsのSDKのパスをパクってくればOK）　
　**C:\Users\ユーザー名\AppData\Local\Android\sdk**

↓
APKが作られたことを確認。




### Jenkins側のUnity関係の設定メモ

Jenkinsの管理⇒Pluginの管理⇒Unity3d plugin　をインストール

Jenkinsの管理⇒Global Tool Configuration⇒Unity3d

名前：わかりやすければなんでもいいはず、UnityExePathとした。
インストールディレクトリ：C:\Program Files\Unity2018_3_8_f1\

※注：Editor\Unity.exe の部分を除外したパスを設定する。

**ジョブ側のUnity記述方法**

ビルド手順の追加⇒Invoke Unity3d Editor

**Unity3d installation name**：さっき設定したUnityExePathを選ぶ
**Editor command line arguments**：-batchmode -quit -logFile C:\Users\ユーザー名\AppData\Local\Unity\Editor\Editor.log -executeMethod 名前空間.クラス名.メソッド名


### クローンするブランチのプルダウン選択
Groovyあんまわからんけど結構便利なので入れておきたい。


参考URL：https://qiita.com/fuku2014/items/8f731db20743c33efddf

**入れられなかった・・・・・Macの時はいけたのに・・**

ワークスペース直下の **jenkins.err.log** によると

```
com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterDefinition executeGroovyScriptAndProcessGroovyValue
重大: Cannot run program "git": CreateProcess error=2, 指定されたファイルが見つかりません。
java.io.IOException: Cannot run program "git": CreateProcess error=2, 指定されたファイルが見つかりません。
```

gitコマンドが認識されていない、

Jenkinsのシステムの設定のシェルの「シェル実行ファイル」を下記にしてもダメ・・・
Windowsの環境変数のシステムパスに追加してもダメ・・・・うーんわからん。

C:\Users\ユーザー名\AppData\Local\Atlassian\SourceTree\git_local\bin\sh.exe


色々探した結果
**List Git Branches Parameter Plugin** を使用すると同じ事ができるようだ
https://wiki.jenkins.io/display/JENKINS/List+Git+Branches+Parameter+Plugin


### Jenkinsジョブの表示名を日本語にする。

ジョブを選択⇒設定⇒高度な設定⇒表示用プロジェクト名に任意の文字を入力する。


### Jenkins側ジョブの実行コンソール出力される文字の文字化け修正。

◆文字化け対策（参照URL）
https://tokkan.net/web/jenkins5.html


①システム環境変数に下記を追加する。
　**変数：JAVA_TOOL_OPTIONS**
　**値：-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8**
↓
②PC再起動する（Jenkins側のシステムの再読み込みだけでは反映されず）

### Switch Platformをスクリプトで行う。

```
// Androidにプラットフォームを切り替える。
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
```

### Symbol設定をスクリプトで行う。

```
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, ";"区切りの文字列);
```

### PackageName Or Bundle Identifier の設定をスクリプトで行う。

```
// パッケージ名の設定（Android）
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,packageName);
```


### Slack連携時のメッセージカスタマイズ（各パラメーターも含めて通知する）

基本的にパラメーター名の頭に**$**を付ければ良い

●Jenkins自身が持っているパラメータはこちらを参照
　https://wiki.jenkins.io/display/JA/Building+a+software+project

　ジョブ名が欲しければ $JOB_NAME とすれば良い

●自分たちで作ったパラメーターに関しては**名前**欄に入っている内容を使う

　選択したブランチ名の名前がBranchであれば$Branch と擦ればいい

「ビルド後の処理」⇒「Custom Message」 の欄に下記のように記述すると、
ジョブ番号・ブランチ名付きでメッセージが通知される。

```
ビルド開始
　ジョブ番号：$BUILD_NUMBER
　ブランチ名：$Branch
```


### 呼び出される側

全アセットを検索
https://qiita.com/T_2/items/bbd03ad320fbc03595af

シーン名検索

```
var guids = AssetDatabase.FindAssets("t:Scene", null);
foreach (var guid in guids)
{
    string path = AssetDatabase.GUIDToAssetPath(guid);
    Debug.Log("path=" + path);

    if (path.IndexOf("シーン名.unity") >= 0)
    {
        Debug.Log("Matches");
        break;
    }
}
```


### 動的パラメータが必要なのでGroovyを頑張って動かす（Windows）

目的：GroovyからUnityのバッチモードを使ってEditor拡張をコマンドで呼び出し、
　　　作成されたシーン一覧ファイルの中身を表示する。

①cygwinを入れる（Bestが選択されているので、そのままインストール）
https://cygwin.com/install.html

②システム環境変数を修正。（コントロールパネルかWindowsアイコンの右クリックからいける）
　システム⇒システム情報⇒システムの詳細設定⇒環境変数⇒システム環境変数エリアのPathを編集
　⇒**C:\cygwin64\bin**のようにbinフォルダをパスとして通しておく

　※念のためPC再起動しておく

③Jenkins側の操作に移って⇒Jenkinsの管理⇒システムの設定⇒**シェル**の項目の**シェル実行ファイル**エリアを未入力にする

④Jenkinsの管理⇒プラグインの管理⇒extended choice parameterをインストールする。

⑤任意のジョブを作り、ビルドパラメーターの追加から、extended choice parameter　を選択

⑥適当なパラメーター名を入力して、シェルコマンドを実行してみる。
動いた！！

下図、参照
![Groovy1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/8cfbc351-b5b1-a38e-581e-36de7ab92f2e.png)



（注）シェルの内容確認だけであれば、Jenkinsの管理⇒スクリプトコンソール　を開いて入力してみると、実行内容の確認ができる。

下図参照
![2019-07-19 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/d26d9ac4-ef00-8538-b25a-fa17000682fd.png)

![2019-07-19 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/e28a4bbf-63b1-8c25-0756-b6c80e9f0a3e.png)

⑦シーンリストを作るEditor拡張の作成

",;"をいちいちつけてるのは、";"でSplitして配列にし、末尾が,で終わるリストにするため。

sceneNames = sceneNames.Substring(0, sceneNames.Length - 2);　は　最終行は","無しにして保存したいため
そうしないとGroovy側のDelimiterを","にして複数行状態にする時、空白の行ができる。

```
using System.IO;
using System.Text;
using UnityEditor;

namespace aaa.bbb.ccc.EditorExtension
{
    public static class BuildExtension
    {
        private const string SCENE_FILENAME = "scenelist.txt";

        private const string CHECK_SCENE_STRING = "フィルタリングする文字列";

        public static void WriteBuildScene()
        {
            // シーンファイルのみのguidを取得
            var guids = AssetDatabase.FindAssets("t:Scene", null);

            string sceneNames = string.Empty;

            foreach (var guid in guids)
            {
                // アセットパスに変換
                string path = AssetDatabase.GUIDToAssetPath(guid);

                // 拡張子なしのファイル名を抜き出す
                string sceneName = Path.GetFileNameWithoutExtension(path);

                if(sceneName.IndexOf(CHECK_SCENE_STRING) <0)
                {
                    continue;
                }

                sceneNames = sceneNames + sceneName + ",;";
            }

            sceneNames = sceneNames.Substring(0, sceneNames.Length - 2);

            // 文字コードを指定
            Encoding enc = Encoding.GetEncoding("utf-8");

            // ファイルを開く
            StreamWriter writer = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), SCENE_FILENAME), false, enc);

            foreach(var scene in sceneNames.Split(';'))
            {
                // テキストに書き込む
                writer.WriteLine(scene);
            }

            // ファイルを閉じる
            writer.Close();
        }
    }
}


```


⑧Jenkins側のGroovyスクリプトエリアに下記を記述する。

（注）**めっちゃはまった**
**def e=**でコマンド文を受け取らないと、Unityが起動しっぱなしになるためわざと変数で受ける。
２重起動のエラーJenkinsのコンソールに表示される。

※**WORKSPACE**の環境変数が使えなかったので、フルパス記述・・・・やり方はあると思うけど時間切れ

ジョブ内の
　**extended choice parameter**⇒**Choose Source for Value**⇒**Groovy Script**

```
String a="C:/Program Files/Unity2018_3_8_f1_LiveEdit/Editor/Unity.exe"
String b="C:/Users/XXXXXX/Desktop/Jenkins/workspace/ワークスペースフォルダ"

String d = "C:/Users/XXXXXX/Desktop/Jenkins/workspace/ワークスペースフォルダ" + "/scenelist.txt"

def e = "$a -batchmode -quit -projectPath $b -executeMethod aaa.bbb.ccc.EditorExtension.BuildExtension.WriteBuildScene".execute()

def f = new File("$d")
def h = f.getText()

return h
```

⑨**extended choice parameter**のにてパラメーター設定

パラメーター名を任意のものにして、チェックボックスで複数選択できるようにする。

※Delimiterを","にする、スペースでやってみたが変換されないのであえて","にする。
　ここもなんかありそう。

![extended_param.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17904/3c08441d-c525-5d6d-94a1-eaa9b9fa0d29.png)


⑩ジョブ側で複数シーンをパラメーターとして送る。

Windowsバッチコマンドの実行に下記を記述・・・これで動きました。

```
"C:\Program Files\Unity2018_3_8_f1_LiveEdit\Editor\Unity.exe" -batchmode -quit -logFile ./build.log -buildTarget Android -projectPath %WORKSPACE% -executeMethod aaa.bbb.ccc.EditorExtension.MakeAssetBundleScene.MakeSceneFromBatch /scene %AssetBundleScenes%
```


---------------------------
◆参考にしたページ
https://codeday.me/jp/qa/20181216/50076.html
https://issues.jenkins-ci.org/browse/JENKINS-33708?focusedCommentId=263259&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-263259

◆プロセス系コマンドメモ

http://hironemu.hatenablog.com/entry/20090408/1239163437
https://www.atmarkit.co.jp/ait/articles/1808/16/news026.html

```
WMIC PROCESS WHERE "Name LIKE '%Unity%'" GET /FORMAT:LIST
```