fastlane で Unity のビルドと配布を自動化する

# はじめに
普通の Unity プロジェクトの開発中は、以下のサイクルが何度も繰り返されることと思います。

1. iOS 用の buildNumber をインクリメント
2. iOS 用の Xcode プロジェクト書き出し
3. iOS 用 ipa 作成
4. ipa を fabric の beta で配布
4. Android 用の bundleVersionCode をインクリメント
5. Android 用 apk 書き出し
6. apk を fabric の beta で配布
7. slack で開発チームに報告

面倒ですね。fastlane でラクしましょう。

この記事は fastlane で Unity のビルドを行う部分を中心に書いていますので、fastlane の基本的な部分はこちらの記事などをどうぞ。

- [fastlaneを使って継続的デリバリーを実践する](http://qiita.com/gin0606/items/a8573b582752de0c15e1)
- [これから始めるfastlane](http://qiita.com/econa77/items/d81a392d6b195cfb7316)

# HOW?

## 1. ビルドスクリプトの準備
Assets/Editor 以下に、こんな感じでビルド用のスクリプトを配置します。

```csharp:AppBuilder.cs
using UnityEditor;
using UnityEngine;

public class AppBuilder{

	[MenuItem ("Build/Android向けビルド")]
	static void buildForAndroid(){
		bumpBuildNumberForAndroid ();
		string errorMessage = BuildPipeline.BuildPlayer(GetAllScenePaths (), "/Users/plasticstraw/Desktop/game.apk", BuildTarget.Android, BuildOptions.None);
		if( !string.IsNullOrEmpty( errorMessage ) )
			Debug.LogError( "[Error!] " + errorMessage );
		else
			Debug.Log( "[Success!]" );
	}

	[MenuItem ("Build/iOS向けビルド")]
	static void buildForIOS(){
		bumpBuildNumberForIOS ();
		// 出力パス。絶対パスで指定すること。また、最後にスラッシュを入れないこと。PostBuildProcess に渡る path が通常ビルドと異なってしまい、思わぬバグを引き起こすことがあります。
		string path = "/Users/plasticstraw/Desktop/game";
		string errorMessage = BuildPipeline.BuildPlayer(GetAllScenePaths(), path, BuildTarget.iOS, BuildOptions.Il2CPP);
		if( !string.IsNullOrEmpty( errorMessage ) )
			Debug.LogError( "[Error!] " + errorMessage );
		else
			Debug.Log( "[Success!]" );
	}

	static void bumpBuildNumberForIOS(){
		string str = PlayerSettings.iOS.buildNumber;
		int num = int.Parse (str);
		num++;
		PlayerSettings.iOS.buildNumber = num + "";
	}

	static void bumpBuildNumberForAndroid(){
		PlayerSettings.Android.bundleVersionCode += 1;
	}

	static string[] GetAllScenePaths(){
		string[] scenes = new string[EditorBuildSettings.scenes.Length];
		for( int i = 0; i < EditorBuildSettings.scenes.Length ; i++ ) {
			scenes[i] = EditorBuildSettings.scenes[i].path;
		}
		return scenes;
	}
}

```

Unity のメニューに「Build 」が追加されているので、そこからそれぞれのビルド処理が期待通りに動くかテストできます。


## 2. Fastfile を記述

Unity には、コマンドラインから任意のスクリプトを実行する機能が備わっています。
fastlane を使って、上記で作成したビルドスクリプトを呼んでやることでビルドを自動化できます。

```ruby:Fastfile
desc "ビルドして Beta で配布します。"
lane :beta do
	buildForIOS
	buildForAndroid
	distributeByCrashlytics
    slack(
		default_payloads: [],
		message: "<!channel>: Beta で配布したよ",
    )
end

desc "iOS用ビルド"
lane :buildForIOS do
	path = File.expand_path(File.dirname(__FILE__)) +"/../"
	sh("/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -quit -logFile ./build.log -projectPath '#{path}' -buildTarget ios -executeMethod AppBuilder.buildForIOS")
	gym(
		project: "/Users/plasticstraw/Desktop/game/Unity-iPhone.xcodeproj",
	)
end

desc "android用ビルド"
lane :buildForAndroid do
	path = File.expand_path(File.dirname(__FILE__)) +"/../"
	sh("/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -quit -logFile ./build.log -projectPath '#{path}' -buildTarget android -executeMethod AppBuilder.buildForAndroid")
end

desc "配布"
lane :distributeByCrashlytics do
	# ios
    crashlytics(
        groups: 'team'
    )

	# android
	crashlytics(
		apk_path: "/Users/plasticstraw/Desktop/game.apk",
		groups: 'team'
		ipa_path: false, # デフォルトでiOSの値が入っており、apk_pathを指定しても正常に動作しないので値を削除
		crashlytics_path: false, # デフォルトでiOSの値が入っており、android向けだと正常に動作しないため、値を削除してfastlaneに任せる
	)
end

```

## 3.実行

あとはターミナルから `$ fastlane beta` 一発で、もろもろビルドしてもろもろ配布してくれます。おめでとうございます。

これを実行するためには Unity で該当のプロジェクトを開いている状態ではダメで、かつビルドで贅沢にCPUを食われるため、CIに組み込むなり、別のマシンでリポジトリをクローンしてから実行するなりがオススメです。