【UE4】ボタンポチっでiOS/AndroidのDevelopment/Shippingの計4つのバイナリを作ってDeploygateに上げる【Jenkins】

## 概要
[Unreal Engine 4 (UE4) その2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ue4_zwei) の16日目です

みなさんモバイルゲームを複数人で開発している際、バイナリのデプロイはどうしていますか？
iOS/Android, デバッグビルド/リリースビルドの合計4つを手動でビルドし、メンバー全員の端末にインストールする・・・という運用は無理がありますよね？
この手の解決方法でよく聞くのは、jenkinsとdeploygateを用いた自動デプロイです。
これをUE4のプロジェクトでもできるのでしょうか？
ggってみたらUE4 x jenkinsの記事が思ったよりなかったので、これらのやり方を紹介します。

- 注意
    - Jenkins、deploygateの使い方等はあまり説明しません
    - iOSの開発者登録は済んでいて、証明書などは組み込み済みなことを想定しています
    - Path等はご自身の環境によって違いますので、いいかんじに読み替えてください

## やりたいこと
図で表すとこんな感じです。
ビルドボタンをポチらなくてもビルドを走らせることができますが、開発の都合上今回はやってません。
![Graph.001.png](https://qiita-image-store.s3.amazonaws.com/0/55107/68b75f30-bb40-8458-b034-0b1b68773cc4.png)

そして今回のビルドはここの「プロジェクトをパッケージ化」から行うことを想定しています。
(Project Lancher/Unreal Frontendから行うものではありません)
![build.png](https://qiita-image-store.s3.amazonaws.com/0/55107/5437c558-9a62-8cd1-8654-25f6ffca4403.png)

## 用意したもの
- UE4.21.0
- Jenkins 2.149
- Windows10
- Mac Mini(Mojave)(ちなみにBlueprintオンリー && 純正のUE4ならMacはいりません)
- GitHubのアカウント(LFS課金した)
- Deploygateのアカウント

## やりかた
ビルドする際のコマンドがわかればbat/shに組み込めるようになり、jenkinsで運用できるようになります。
そして、ビルドする際のコマンドは「プロジェクトをパッケージ化」を行った際の「アウトプットログ」に書いてあります。
このコマンドは「プロジェクト設定」に書かれているものを反映したコマンドになっています。
![command.png](https://qiita-image-store.s3.amazonaws.com/0/55107/fab1a90a-a3b4-c9be-57b4-f18febf39ef0.png)

そして、「アウトプットログ」に書かれている `AutomationTool` は `Epic Games/UE_4.バージョン番号/Engine/Binaries/DotNET/` にあります。
これさえわかれば勝ったようなものです。
jenkinsに組み込みましょう。

Macで `AutomationTool.exe` を動かすには `mono` が必要です。
予めHomebrew等でインストールしておきましょう。
また、開発をWindowns、jenkinsサーバーをMacとしている場合、MacにもUE4のインストール・iOSの開発者証明書等・AndroidSDK等のインストールもお忘れなく。

Jenkinsの立ち上げ・初回設定は割愛します。ほかの記事を参照してください。

では「フリースタイル・プロジェクトのビルド」でジョブを作成します。
私の場合はパラメーターとして以下を設定できるようにしました。
![スクリーンショット 2018-12-15 15.55.45.png](https://qiita-image-store.s3.amazonaws.com/0/55107/f609e00f-1230-ebb2-61ce-8b8082d54052.png)

Githubの設定もします。
Git LFSを使っている場合はその設定も忘れずに。
![スクリーンショット 2018-12-15 15.56.32.png](https://qiita-image-store.s3.amazonaws.com/0/55107/80fed380-1581-c7c2-e29a-7cd6a56c356f.png)

ビルドにてシェルスクリプトを記述します。
私は以下のように書きました。
![スクリーンショット 2018-12-15 15.56.45.png](https://qiita-image-store.s3.amazonaws.com/0/55107/0fe189bf-8777-cdb1-22b5-39eb25b85cd7.png)

```sh:iOSの場合
mono "/Users/Shared/Epic Games/UE_4.21/Engine/Binaries/DotNET/AutomationTool.exe" -ScriptsForProject="${WORKSPACE}/${project_name}.uproject" BuildCookRun -nocompile -nocompileeditor -installed -nop4 -project="${WORKSPACE}/${project_name}.uproject" -cook -stage -archive -archivedirectory="/Users/ユーザー名/Documents/UnrealPackages/${project_name}" -package -clientconfig=${config} -ue4exe=UE4Editor -pak -prereqs -nodebuginfo -targetplatform=IOS -utf8output

projectName="${project_name}"
if [ ${config} == "Shipping" ]; then
    projectName="${project_name}-IOS-Shipping"
fi

if [ ${upload_deploygate} == true ]; then
    curl -F "token=deploygateのトークン" -F "file=@/Users/ユーザー名/Documents/UnrealPackages/${project_name}/iOS/$projectName.ipa" -F "message=config: ${config}" https://deploygate.com/api/users/deploygateのユーザー名/apps
fi
```

Androidの場合はビルドする際に選択したテクスチャ圧縮形式によってコマンドが若干かわるので注意してください。

```sh:Androidの場合
mono "/Users/Shared/Epic Games/UE_4.21/Engine/Binaries/DotNET/AutomationTool.exe" -ScriptsForProject="${WORKSPACE}/${project_name}.uproject" BuildCookRun -nocompile -nocompileeditor -installed -nop4 -project="${WORKSPACE}/${project_name}.uproject" -cook -stage -archive -archivedirectory="/Users/ユーザー名/Documents/UnrealPackages/${project_name}" -package -clientconfig=${config} -ue4exe=UE4Editor -pak -prereqs -nodebuginfo -targetplatform=Android -cookflavor=テクスチャの圧縮形式名 -utf8output

projectName="${project_name}"
if [ ${config} == "Shipping" ]; then
    projectName="${project_name}-Android-Shipping"
fi

if [ ${upload_deploygate} == true ]; then
    curl -F "token=deploygateのトークン" -F "file=@/Users/ユーザー名/Documents/UnrealPackages/${project_name}/Android_テクスチャの圧縮形式名/$projectName-armv7-es2.apk" -F "message=config: ${config}" https://deploygate.com/api/users/deploygateのユーザー名/apps
fi
```

最後にパイプラインジョブを作成します。
パイプラインによって他のジョブを順番に(並列にもできます)実行します。
![スクリーンショット 2018-12-15 15.58.58.png](https://qiita-image-store.s3.amazonaws.com/0/55107/eacb806f-9c4d-3fb6-99ee-7807d5f165ac.png)

私の場合はパラメーターとして以下を設定できるようにしました。
![スクリーンショット 2018-12-15 15.59.12.png](https://qiita-image-store.s3.amazonaws.com/0/55107/a4208b34-b60e-d720-6896-65ea4fb30ae2.png)

パイプラインスクリプトを記述します。
私は以下のように書きました。
![スクリーンショット 2018-12-15 15.59.23.png](https://qiita-image-store.s3.amazonaws.com/0/55107/4884f143-7642-1fce-1025-2a9e899f1cb2.png)

```groovy:計4つのバイナリを生成しdeploygateにアップロードしslackに通知する
pipeline {
    agent none
    stages{
        stage ('iOS Development') {
            steps {
                build job: 'ビルドジョブ名-ios', parameters: [
                    string(name: 'project_name', value: 'プロジェクト名'),
                    string(name: 'config', value: 'Development'),
                    booleanParam(name: 'upload_deploygate', value: env.upload_deploygate),
                ]
            }
        }
        stage ('iOS Shipping') {
            steps {
                build job: 'ビルドジョブ名-ios', parameters: [
                    string(name: 'project_name', value: 'プロジェクト名'),
                    string(name: 'config', value: 'Shipping'),
                    booleanParam(name: 'upload_deploygate', value: env.upload_deploygate),
                ]
            }
        }
        stage ('Android Development') {
            steps {
                build job: 'ビルドジョブ名-android', parameters: [
                    string(name: 'project_name', value: 'プロジェクト名'),
                    string(name: 'config', value: 'Development'),
                    booleanParam(name: 'upload_deploygate', value: env.upload_deploygate),
                ]
            }
        }
        stage ('Android Shipping') {
            steps {
                build job: 'ビルドジョブ名-android', parameters: [
                    string(name: 'project_name', value: 'プロジェクト名'),
                    string(name: 'config', value: 'Shipping'),
                    booleanParam(name: 'upload_deploygate', value: env.upload_deploygate),
                ]
            }
        }
        stage ('post slack') {
            steps {
                script { 
                    if (env.upload_deploygate == "true") {
                        notification()
                    }
                }
            }
        }
    }
}

def notification() {
    def slack_channel = "#通知するチャンネル名"
    def slack_domain = "https://slack名.slack.com/"
    def slack_token = "slackのトークン"
    def slack_color = "good"
    if(currentBuild.result != "FAILURE") {
        def slack_msg = "新しいバイナリがアップロードされました！deploygateから確認できます！ \n リリースノート: ${env.release_note}"
        slackSend channel: "${slack_channel}", color: "${slack_color}", message: "${slack_msg}", teamDomain: "${slack_domain}", token: "${slack_token}"
    }
}
```

これでパイプラインのjobを実行すればiOS/Android, Development/Shippingの4つのバイナリが生成されて、deploygateへとアップロードされます！

## トラブルシューティング
Q: jenkinsから実行するとmonoが動かせないんだけど・・・
A: 環境変数をjenkinsで設定しましょう
![スクリーンショット 2018-12-15 16.00.12.png](https://qiita-image-store.s3.amazonaws.com/0/55107/f971e413-a239-4ca7-6870-7da6515f7386.png)

Q: MacでWindows版のゲームのビルドをしたい・・・(´・ω・\`)
A: Macではできません・・・(´・ω・\`)
iOS/Android/Windowsというビルドをしたい場合はどうしてもビルドするマシンを2台(Windows/Mac)用意しなければならないらしいです・・・

## まとめ
UE4のプロジェクトををbat/shを使ってバイナリを生成したいときは、UE4上でビルドして「アウトプットログ」を見ると、そこにコマンドがかいてあります :+1:

## おわりに
今回はビルドボタンをポチるようにしましたが、jenkinsの設定をいじれば定期的にビルドしたり、pushがあった時点でビルドを走らせることもできます。
それでは良い開発ライフを！