Bitrise CLIを使ったUnityのモバイル開発におけるビルド環境構築

はじめに

こんにちは。サイバーエージェントのゲーム事業部でUnityエンジニアをしている住田です。

Unityでの開発において、ネックになりがちなのがアプリケーションを配布するまでの一連のビルドフロー環境構築です。Unityにおけるビルド環境構築などのインテグレーションツールといえばJenkinsが有名です。会社規模で開発を行っているプロジェクトだとJenkins専任でメンテナンスを行っているエンジニアがいるということも多いのではないでしょうか。

個人的にJenkins以外でモバイルのローカルビルド環境構築が組みやすいものはないか探していたところ、BitriseというCIのSaaSサービスがCLI版を配布してくれており、モバイル特化のツールで使いやすいという情報を耳にしました。

そこで今回はUnityにおけるビルド環境構築について、Jenkinsではなく、Bitrise CLIを使ったアプローチを紹介します。Bitriseの紹介や基本的な使用方法からUnityプロジェクトへの導入までのフローを順序立てて説明していきます。

※本記事は2019年4月14日に開催された技術書典6にて販売した「UniTips Vol.3」からの転載となります。

Bitrise CLIの導入

Bitriseとは

BitriseとはBitrise社が提供しているCI(Continuous Integration)のSaaSサービスです。

主にモバイルプラットフォームをメインターゲットとして展開されており、ネイティブのiOSやAndroidはもちろんのこと、UnityやReactNativeといったクロスプラットフォームのプロジェクトに対しても使用することができます。

海外のサービスなので基本的には英語になりますが、2018年からは日本向けのカスタマーサービスも進めており、会社規模で連携を深めてツール導入を行っている所もあります。日本向けのブログやTwitter、日本語のドキュメント整備なども活発に行っており、今後の活動にも期待ができるサービスです。

CIのSaaSサービスとして展開しているBitriseですが、前述にもあるようにCLI版の配布も行なっております。同じCIのSaaSサービスであるCircle CIやTravis CIではCLIの配布がなくローカル環境構築ができないため、この点はBitriseのいいところの1つです。今回はそのBitriseのCLI版を使ってローカルでビルド環境を構築したり、テストを回したりする手順を紹介します。

Bitrise CLIのセットアップ

まずはBitrise CLIの導入から説明します。本章はMacOSで環境を構築していくため、Windowsの場合は別途パッケージをダウンロードして導入工程を踏む必要があります。

また、用いるBitrise CLIのバージョンは1.28.0となります。バージョンによっては本章に書かれている情報と違う場合もあるのでご了承ください。

インストールはHomebrewから行うことができます。

$ brew update && brew install bitrise

CLIでバージョンの確認コマンドを入力して、正常にインストールできたか確認を行います。

$ bitrise -v
1.28.0

このようにバージョン情報が表示されれば成功です。次にBitriseの実行のために必要なツールなどをセットアップします。セットアップには次のコマンドを入力します。

$ bitrise setup

セットアップのコマンドを実行するとBitriseのロゴが表示されて、必要なツールなどの導入が始まります。セットアップの完了までは少し時間がかかるので待ちましょう。

完了すると図1のような画面になります。

図1 : Bitriseのセットアップ

図1 : Bitriseのセットアップ

これでBitrise CLIを使う準備は完了です。

Bitrise CLIによるワークフロー構築

YAMLによるスクリプトと実行

Bitriseはbitrise.ymlという名前のYAMLで構成されたスクリプトからインテグレーションのワークフローを構成します。Hello Worldと表示するサンプルがこちらになります。

// ファイルのフォーマットバージョン
format_version: 1.3.1
// ライブラリの参照
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

// 環境変数の定義
app:
    envs:
    - HOGE_KEY: hoge
    - HUGA_TOKEN: huga
    - TEST_PATH: path

// ワークフローの定義
workflows: 
    test: // ワークフローの名前
        steps: // ワークフローを構成するステップ群
            - script@1.1.3: // ステップの名前
            inputs: // ステップに与える引数
            - content: echo "Hello World"

ワークフローはステップという単位のブロックの集まりとして構成されています。ステップはgitのクローン、ビルド処理だったり、テストだったり、そういった処理単位のブロックになります。ワークフローはそれらをシーケンシャルに実行します。

前述したサンプルスクリプトではひとつのワークフローのみ定義していますが、ワークフローはひとつのYAML内で複数定義することが可能です。スクリプト実行時のコマンドラインの引数でワークフロー名を渡してどのワークフローを実行するかを決定します。

まずは試しにこのサンプルを実行してみましょう。実行するディレクトリにbitrise.ymlという名前でサンプルスクリプトを保存し、コマンドラインで実行します。

$ bitrise run test #bitrise run {ワークフロー名}

正常に実行されるとBitriseのワークフローの実行処理が走り、図2のように出力されます。

図2 : Hello Worldの実行

図2 : Hello Worldの実行

bitrise.ymlでワークフローを記述し、bitrise runでワークフローを指定して実行する。これがBitrise CLIでジョブを実行する基本の流れになります。

では、Hello Worldと出力するサンプルをもとにスクリプトの記法について説明していきます。

まずは最初の二行ですが、これはBitriseのスクリプトを実行するにあたって使用するYAMLのフォーマットと参照するBitriseのライブラリになります。基本的には最新のフォーマットバージョンと、公式がOSSとして公開しているライブラリを指定すれば大丈夫です。

そしてワークフローの記述とは別枠で、ワークフロー内で使用する環境変数を定義することができます。定義した変数については${変数名}とスクリプト内で記述することで使用することができます。またコマンドラインからも環境変数の定義を行うことが可能です。

envman add --key {環境変数のキー} --value {環境変数の値}

ワークフロー内での動的なパス定義などを行う時にはenvmanを用いてキーと値を定義します。

ステップごとのインテグレーション

環境変数の定義の後にworkflowsと宣言されている部分からが実際のワークフローの記述になります。前述にもあるとおり、ワークフローはステップの集まりとして構成されています。

ステップの記述は行うステップの種類とそれに用いる引数で構成されており、その行うインテグレーションはライブラリに定義されています。サンプルで使用しているのはscript@1.1.3という単純に引数に与えられたコマンドを実行するステップです。

Bitriseはさまざまなインテグレーションをステップとして定義しており、引数を与えるだけで実際のコマンドを用いずに、さまざまな処理を実現することができます。AndroidやiOSに向けたビルドやテスト、S3やGitlabといった外部サービスとの連携、またfastlaneといった他のツールとの連携など、モバイル開発におけるさまざまなインテグレーションをサポートしています。全てのステップのインテグレーションについては公式サイトに一覧で載っています。Bitriseを使う際には一度閲覧しておくのを推奨します。

また、本章で解説やサンプルに用いるステップに関して、全て名前の@以下に記載されているバージョンのステップについての解説となります。バージョンの差異に伴ってステップの挙動や引数が変わる場合もあるため、ご了承ください。

Unityのビルドフローの構築

それではBitrise CLIでのUnityプロジェクトに関するワークフローの構築を具体的な例を用いて説明します。今回例として構築するワークフローは次の流れになります。

  1. エディタモードでのテスト
  2. アプリケーションのビルド
  3. 成果物をDeployGateにアップロード
  4. Slackに通知

まずプロジェクトのクローンを行い、エディタモードでのテストを実行します。テストを通過できない場合にはビルドフローを通さずにそのまま失敗通知をSlackに送ります。

テストを通過した場合はビルドを実行し、その成果物をアプリケーションの配布サービスであるDeployGateにアップロードし、その成功通知をSlackに送ります。つまり、Slackに関してはそれまでのワークフローの結果に関係なく実行を行うことになります。

Bitriseにはネイティブアプリ用、XcodeやAndroid Studioといったような開発環境に対する処理は事前にステップとしてライブラリに組み込まれていますが、Unityに関するものは用意されていません。

Unityの環境に関するフローはBitrise側で用意されていないため、Scriptステップからコマンドを組み立てて処理を定義する必要があります。ここで使用するのはHello Worldのサンプルを実行した時にも使用したScriptステップです。

- script@1.1.5:
    inputs:
    - content: |-
        // 任意のコマンド

Scriptステップは単純なユーザー定義の処理を行うステップで、contentという引数に実行したいコマンドを渡すだけです。このUnityに関する処理のようにBitriseのライブラリに組み込まれていないものは全てScriptステップを用いてBitriseに実行させます。

それではこれらのUnityに関する手順について、順を追って説明していきます。

エディタモードでのテストステップ

今回テスト行うにあたって、コマンドラインからでもテストスクリプトを実行できるUnityの機能であるUnity Test Runnerを使用します。

Unity Test Runner

Unityに組み込まれているテストツールで、ランタイムでのテスト、エディタモードでのテストの両方を行うことができます。テストスクリプトは通常のスクリプトを作るのと同様にメニューからテンプレートを作成できます。

詳細な説明は省きますが、エディタ上でWindowを開き、GUIベースでテストコードを選択して走らせることができるため大変便利な機能です。今回はこのUnity Test Runnerをコマンドライン上から実行し、エディタモードでのテストを行います。

テストスクリプトの簡単なサンプルとして、今回はシーン上に規定のオブジェクトがあるかどうかのチェックを行います。実際の開発ケースの場合は依存関係の解決確認だったり、マスタ取得テストだったり、色々な用途で使ってみると便利かと思います。

using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace Tests
{
    public class TestSample
   {
       // A Test behaves as an ordinary method
       [Test]
       public void TestSceneObject()
       {
           // Testというオブジェクトがあるかないかのチェック(今回はシーン上に配置してある想定)
         EditorSceneManager.OpenScene("Assets/Scenes/BuildTest.unity");
       Assert.IsTrue(GameObject.Find("Test") != null);
       }
   }
}

普通にUnityで開発を行う上でこのテストスクリプトを実行したい場合は、WindowのGeneralからUnityTestRunnerの画面を開いて実行します。成功したかどうかはGUI上でわかりやすい形で表示されます。

図3 : UnityTestRunner

図3 : UnityTestRunner

今回はBitrise CLIでこのテストを行いたいため、このUnityTestRunnerをコマンドラインで実行します。

Unityのコマンドライン引数には-runEditorTestsというオプションが用意されており、これを使うことでエディターモードでのUnityTestRunnerのテストを走らせることができます。

-runEditorTestsはエディターモードで全ての登録されているテストを実行するオプションなので、ランタイムのテストなど個別のテストスクリプト指定を行う場合には別途コマンドライン引数の指定や、独自のスクリプトからテスト実行の対応などが必要となります。

- script@1.1.5:
    inputs:
    - content: 
        /Applications/Unity/Unity.app/Contents/MacOS/Unity 
            -nographics #グラフィックの初期化を行わない
            -batchmode #バッチモード
            -logFile #コンソールにログを出力(ここにパス指定でログを書き出す)
            -projectPath "$BITRISE_SOURCE_DIR" 
            -runEditorTests #エディターモードでテストを行う(終了後に自動終了)

これでUnityTestRunnerがコマンドラインで実行され、テストを通過すれば次のステップに進み、テストを通過できなければabortします。

アプリケーションのビルドステップ

テストを通過したらビルドを行います。ビルドする際には、バッチモードでスクリプトを指定し、プラットフォームや成果物の保存先などの必要な情報を与えつつ実行する形になります。

実行するスクリプトについてはBitriseが公式でサンプルを公開しており、それをベースに組み立てることになります。コメントを追記したり、改修を加えたものをGistに上げていますので、これを参考にしてみてください。

サンプルスクリプトではSDKやJDKのパス指定まで行なっていますが、ローカル環境でその周辺の設定がされていれば設定は不要です。ビルド用のスクリプトをAssets直下に置き、コマンドラインでバッチモード実行処理を叩きます。

- script@1.1.5:
    inputs:
    - content: |-
        #!/bin/bash
        # fail if any commands fails
        set -ex

        #load variable
        source ~/.bash_profile

        # Android
        /Applications/Unity/Unity.app/Contents/MacOS/Unity 
            -nographics //グラフィックの初期化を行わない
            -quit //コマンド終了時にUnityを終了する
            -batchmode //バッチモード
            -logFile //コンソールにログを出力(ここにパス指定でログを書き出す)
            -projectPath #{プロジェクトのパス} //プロジェクトのパス
            -executeMethod #BitriseUnity.Build //実行するメソッド
            -buildOutput #{ビルド成果物を書き出すパス} //出力先の指定
            -buildPlatform android //プラットフォーム

        # iOS
        /Applications/Unity/Unity.app/Contents/MacOS/Unity 
            -nographics 
            -quit 
            -batchmode 
            -logFile 
            -projectPath #{プロジェクトのパス}
            -executeMethod BitriseUnity.Build 
            -buildOutput #{ビルド成果物を書き出すパス}
            -buildPlatform ios

これでビルドが成功すればbuildOutputで指定されたパスにAndroidであればapk、iOSであればXcodeプロジェクトが出力されます。

XcodeプロジェクトのArchive

iOS向けのアプリケーションを出力するにはXcodeプロジェクトをArchiveしてipaファイルを出力する必要があります。

BitriseにはXcodeでのArchiveを行うステップが用意されているのでそれを利用します。必要な情報はXcodeプロジェクトのパスとスキームです。

- xcode-archive@2.4.21:
    inputs:
    - scheme: Unity-iPhone
    - project_path: "XCodeプロジェクトのパス"

生成されたipaのパスは$BITRISE_IPA_PATHという環境変数に格納されています。

成果物のアップロードや通知を行うステップ

ここまでのステップをワークフローに組み込んだら、外部への成果物のアップロードや通知のステップを構築します。

今回はその成果物をDeployGateにアップロードしてみます。DeployGateへのアップロード処理はBitrise側がステップを用意しているのでそれを利用します。deploygate–upload-app-bitrise-stepというステップをYAMLに記述し、引数にアップロードに必要な変数を設定します。引数については最低限アップロードする成果物のパスとDeployGateにアップロードするためのAPIトークンさえあれば大丈夫です。配布するアプリケーションについて情報を付加したい場合には引数にそれぞれ流し込みます。

- deploygate--upload-app-bitrise-step@1.0.1:
    inputs:
    - api_key: "APIトークン"
    - owner_name: オーナー名
    - app_path: "アプリケーションのパス"
    - visibility: public(公開情報)
    - message: 配布物の説明
    - distribution_key: 配布ページのハッシュ
    - distribution_name: 配布ページの名前
    - release_note: リリースノート
    - disable_notify: (iOSのみ)通知の無効化

Bitriseにはアプリケーションの配布サービスへのアップロードとして、他にもApp Centerなどのいくつかのサービスについてのステップが用意されています。用意されているステップについてはDeployGateと同様に、必要な情報をステップに渡すだけで処理を完結することができます。

アプリケーションの配布ステップが完了したら最後にSlackに通知を送ります。これについてもslackというステップがBitriseのライブラリに用意されているのでこれを使用します。

ここで注意するのが、通知に関してはここまでのステップの結果に関係なく実行したいということです。Bitriseのワークフローは、基本的には前のステップでabortした場合次のステップは実行されないため、前のステップに関係なく実行させるステップの場合は設定が必要になります。

ステップの引数の次にis_always_runという項目の値にtrueを設定することで、ワークフローの結果に関係なく常に実行されるようになります。これは全てのステップ共通のオプションなので、実行されないと困るステップはこの設定を行う必要があります。

 - slack@3.1.2:
    inputs:
    - api_token: APIトークン
    - webhook_url: Slack側で発行しているWebhookのURL
    - channel: チャンネル名または送るユーザー名
    - channel_on_error: 失敗時のチャンネル名または送るユーザー名
    - pretext: 成功時のメッセージ見出し
    - pretext_on_error: メッセージの見出し
    - text: メッセージの内容
    - text_on_error: 失敗時のメッセージ内容
    - from_username: Slackでの通知のユーザー名
    - from_username_on_error: 失敗時のSlackでの通知のユーザー名
    - icon_url: iconの画像のURL
    - icon_url_on_error: 失敗時のiconの画像のURL
    - emoji: アイコンにSlackの絵文字を使う場合の絵文字の名前
    - emoji_on_error: 失敗時のアイコンにSlackの絵文字を使う場合の絵文字の名前
    is_always_run: true

メッセージのカスタム用の色々な引数が用意されていますが、最低限送信に必要なSlackのAPIトークンかWebhookのURLがあれば大丈夫です。基本的にはデフォルトの引数が入力されており、特に考えなくともシンプルな通知としては要件を満たす内容になっています。

ちなみに実際のBitriseフォーマットによるSlackの通知メッセージは記述しているステップのバージョンでは図4のような感じです。ビルドの成功および失敗の場合分けなどもデフォルトで用意されており、引数でカスタマイズすることができます。これらのそれぞれの画像や文言、ボタンなどのUIに関しては引数でカスタマイズが可能です。

図4 : BitriseによるSlack通知

図4 : BitriseによるSlack通知

ここまで説明してきたステップをワークフローに記述することでスクリプトの完成となります。ここまでのステップをまとめたAndroidのビルドフローのスクリプトを後述したので、もしよければ参考にしてみてください。iOSであればこれをもとにXCodeでのArchiveステップを組み込めば大丈夫です。

format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

workflows:
    build_android:
        steps:
            # エディタモードでのテスト
            - script@1.1.5:
                inputs:
                - content: |-
                    /Applications/Unity/Unity.app/Contents/MacOS/Unity 
                    -nographics 
                    -batchmode
                    -logFile
                    -projectPath "プロジェクトのパス" 
                    -runEditorTests 

            # UnityプロジェクトのAndroidビルド
            - script@1.1.5:
                inputs:
                - content: |-
                    /Applications/Unity/Unity.app/Contents/MacOS/Unity 
                    -nographics
                    -quit 
                    -noUpm 
                    -batchmode 
                    -logFile 
                    -projectPath "Unityプロジェクトのパス" 
                    -executeMethod BitriseUnity.Build 
                    -buildOutput "APKのパス" 
                    -buildPlatform android

            # DeployGateへのアップロード
            - deploygate--upload-app-bitrise-step@1.0.1:
                inputs:
                - app_path: "APKのパス"
                - visibility: public
                - api_key: "DeployGateのAPIトークン"

            # Slack通知
            - slack@3.1.2:
                inputs:
                - api_token: "APIトークン"
                is_always_run: true

後はこのスクリプトを実行して、テストフロー、ビルドフロー、アップロードや通知のフローが正常に行われれば成功です。

XCodeの連携やアップロードなど面倒な手順の多いビルドフローですが、Bitriseがさまざまなインテグレーションをステップとして用意してくれているためとても構築がスムーズです。

Jenkinsとの比較

前述したように、Unityのプロジェクトの開発におけるビルド環境構築といえばJenkinsです。まだまだBitrise CLIに関しては情報が少ないのが現状ですが、個人的に使ってみた上でJenkinsと比較して感じた点を挙げていきます。

モバイル特化ツールの使いやすさ

Jenkinsとは違う点のひとつが、Bitriseがモバイル特化のインテグレーションツールだということです。

Bitriseでは多くのモバイル向けインテグレーションがビルトインで用意されており、オプションなども多く、Jenkinsと比べてもとても便利です。特にXcode周りではテストや署名周りのライブラリが充実しており、これはJenkinsと比較した際のメリットのひとつかなと思います。

Unityという本筋からは逸れてしまいますが、React Nativeといった比較的新しいクロスプラットフォームなモバイル開発環境もサポートしており、今後のモバイル開発環境に対するライブラリ拡張にも期待ができると感じました。

また、Slackをはじめとした通知系ステップも充実しており、プロジェクトの開発体制に合わせて柔軟にそれらのインテグレーションを回すのにもとても便利です。

図5 : 通知インテグレーション

図5 : 通知インテグレーション

応用的な学習が難しい

Bitriseは新しいサービスということもあり、ネットに情報も少なく、具体的なケースを交えたものとなればUnity関連では調べてもほぼ出てこないのが現状です。何かに詰まった時、大抵のケースでは他のツールで同様のケースがあるかを調べ、それをBitriseの問題に置き換えて対処することが多いです。

一方Jenkinsを用いたUnityのビルド環境構築は多くの技術者がすでに試しているため、大抵よく詰まるような部分は誰かが原因と対処法を文書化した知見が多く出回っています。

そういったことから、試行錯誤を重ねて自分のやりたいことに沿ったカスタマイズを進めていく際、Jenkinsの方がBitriseよりも工数が少ないのは明らかです。もう少しBitriseが広まり、知見が世に多く出回ればこういった工数も多少は抑えられるとは思いますが、現状は手探り感が強いです。

おわりに

今回はJenkinsではなくBitrise CLIを利用したローカルでのビルド環境の構築についてお話しました。

Bitriseは若いサービスかつOSSということもあり活発的で、今後の成長に期待ができるツールではないかなと感じています。本来の使用用途であるSaaSとしてのCI環境構築サービスもGUIが整っており、ネイティブでモバイル開発をしている方ならかなり便利なサービスに仕上がっています。

今後もBitriseに関しては個人開発で使いつつ動向を見守り、サービスの向上に期待していきたいと思っています。Unityやモバイルアプリを開発する方にとって、本章が少しでもビルド環境構築のお役に立てば幸いです。

【そのほかの記事をみる】

おすすめ記事