BuildManifestでUnity Cloud Buildのビルド情報を上手に管理しよう!

  • 8
    いいね
  • 0
    コメント

 この物語はフィクションです。

 非プログラマー 「なんかバグってる!直して!」

 プログラマー  「どれどれ?バージョンは?」

 非プログラマー 「えーっと、ちょっと前のやつ!」

 プログラマー  (ちょっと前っていつのだよ?何回ビルドしたと思ってんだよ!?)

はじめに

 先ほどの物語はフィクションではありますが、モバイルゲームアプリの開発現場において、このような不幸な経験がある方も多いのではないでしょうか?

 解決策の一つはバージョンをゲームアプリ内に表記して、それを報告してもらうことです。(もちろん端末のアプリ管理機能を使って調べてもらうのもありですね。)

 しかし、これはビルドごとにバージョンを適切に更新している必要があります。

 また、もし製品版やデバック版、ステージング版など複数のビルド設定がある場合、不具合が発生したのがどのビルド版なのかを把握する必要もあります。

 前述のフィクションの物語が起きないようにするための、解決方法・対処方法はさまざまなものが考えられますね。

 もしあなたがUnity Cloud Buildを使っているのであれば、私のおすすめは、CloudBuildManifestを用いて、ビルドターゲットとビルド番号をアプリ内に表記するという方法です。

 この投稿では、Unity Cloud BuildのCloud BUild Manifestとそれを用いたTipsを紹介します。

CloudBuildManifestとは?

 CloudBuildManifestについて説明します。

 Unity Cloud Buildを行うと、Assets/__UnityCloud__/Resources以下にUnityCloudBuildManifest.jsonというJSON形式のテキストファイルが自動で置かれます。

 生成されるJSONの例を示します。

{
    "scmCommitId":"abcdefghijklmnopqrstuvwxwz01234567890123",
    "scmBranch":"master",
    "buildNumber":"3",
    "buildStartTime":"1/1/2017 11:11:11 PM",
    "projectId":"cloud_build_example",
    "bundleId":"com.teammurosta.cloud_build_example",
    "unityVersion":"5.5.0f3",
    "xcodeVersion":"6.3.1",
    "cloudBuildTargetName":"default-ios",
}

 それぞれの要素は次の通りです。

  • scmCommitId: Unity Cloud Build によってビルドされた commit、または changelist
  • scmBranch: ビルドされたブランチ名
  • buildNumber: このビルドに関連する Unity Cloud Build 番号
  • buildStartTime: ビルドプロセスが始まったときの UTC timestamp
  • projectId: Unity Cloud Build プロジェクト識別子
  • bundleId: (iOS/Androidのみ) bundleIdentifier は Unity Cloud Build 内で設定されます
  • unityVersion: Unity Cloud Build がビルド作成に使用した Unity のバージョン
  • xcodeVersion: (iOS のみ) ビルドに使用される XCode のバージョン
  • cloudBuildTargetName: ビルドされたプロジェクトビルドターゲットの名前。現在は、プラットフォームに呼応していて、"default-web” “default-ios” “default-android" のいずれか。

 この生成されるJSON(CloudBuildManifest)の要素には、ビルドしたコミットのリビジョン、ビルドに使ったUnityやXcodeのバージョン、ビルド設定であるビルドターゲット名がありますね。

 これらの情報をゲームアプリ内で表示し、それを報告してもらえばいろいろ解決しそうですね。

 私のおすすめは、cloudBuildTargetNamebuildNumberを併記してゲームアプリ内に表示する方法です。

ラッパークラスを作ろう

 CloudBuildManifestを次のようなラッパークラスを使って扱うことをお勧めします。

using System;
using UnityEngine;

[Serializable]
public class CloudBuildManifest
{
    public static CloudBuildManifest Load()
    {
        var json = Resources.Load<TextAsset>("UnityCloudBuildManifest.json");
        if (json == null)
        {
            return null;
        }
        else
        {
            return JsonUtility.FromJson<CloudBuildManifest>(json.text);
        }
    }

    [SerializeField]
    string scmCommitId;
    public string ScmCommitId { get { return scmCommitId; } }

    [SerializeField]
    string scmBranch;
    public string ScmBranch { get { return scmBranch; } }

    [SerializeField]
    string buildNumber;
    public string BuildNumber { get { return buildNumber; } }

    [SerializeField]
    string buildStartTime;
    public string BuildStartTime { get { return buildStartTime; } }

    [SerializeField]
    string projectId;
    public string ProjectId { get { return projectId; } }

    [SerializeField]
    string bundleId;
    public string BundleId { get { return bundleId; } }

    [SerializeField]
    string unityVersion;
    public string UnityVersion { get { return unityVersion; } }

    [SerializeField]
    string xcodeVersion;
    public string XCodeVersion { get { return xcodeVersion; } }

    [SerializeField]
    string cloudBuildTargetName;
    public string CloudBuildTargetName { get { return cloudBuildTargetName; } }
}

  Cloud Buildでなく、エディタ上での実行やUnity場合、CloudBuildManifest.Load()nullを返すことに気を付けてください。

 cloudBuildTargetNamebuildNumberをこんな感じで表示します。

var buildManifest = CloudBuildManifest.Load();
buildInfoText.text = buildManifest == null
    ? "--------------"
    : string.Format("{0} #{1}", buildManifest.CloudBuildTargetName, buildManifest.BuildNumber);

ScriptableObjectも生成される

 前の節では、CloudBuildManifestという自作クラスを生成しました。実は組み込みでCloud Build Manifestを司るクラスが存在します。

 UnityEngine.CloudBuild.BuildManifestObjectというScriptableObjectを継承したクラスです。

 このクラスはすこし癖があります。UNITY_CLOUD_BUILDディレクティブ内でしか使えないのです。

 このBuildManifestObjectを利用したコードを次に示します。

using UnityEngine;
using UnityEngine.UI;
#if UNITY_CLOUD_BUILD
using UnityEngine.CloudBuild;
#endif

public class ManifestExample : MonoBehaviour
{
    [SerializeField]
    Text text;

    void Start()
    {
#if UNITY_CLOUD_BUILD
        var manifest = Resources.Load<BuildManifestObject>("UnityCloudBuildManifest.scriptable");
        text.text = manifest.ToJson();
#endif
    }
}

 UNITY_CLOUD_BUILDというディレクティブに注目してください。これはCloud Buildで有効になります。

 BuildManifestObjectクラスのScriptableObjectが、Assets/__UnityCloud__/Resources以下にUnityCloudBuildManifest.scriptableという名前で生成されます。

 BuildManifestObjectは、Cloud Buildでないと使えないクラスですので、UNITY_CLOUD_BUILDディレクティブとともに使う必要があるという点に注意してください。

 ドキュメントによると、BuildManifestObjectには次のメンバがあるようです。

namespace UnityEngine.CloudBuild
{
    public class BuildManifestObject : ScriptableObject
    {
        // Tries to get a manifest value - returns true if key was found and could be cast to type T, false otherwise.
        public bool TryGetValue<T>(string key, out T result);

        // Retrieve a manifest value or throw an exception if the given key isn't found.
        public T GetValue<T>(string key);

        // Sets the value for a given key.
        public void SetValue(string key, object value);

        // Copy values from a dictionary. ToString() will be called on dictionary values before being stored.
        public void SetValues(Dictionary<string, object> sourceDict);

        // Remove all key/value pairs
        public void ClearValues();

        // Returns a Dictionary that represents the current BuildManifestObject
        public Dictionary<string, object> ToDictionary();

        // Returns a JSON formatted string that represents the current BuildManifestObject
        public string ToJson();

        // Returns an INI formatted string that represents the current BuildManifestObject
        public override string ToString();
    }
}

 公式ドキュメントには

JSON TextAsset はまだ書き出されていないため、BuildManifestObject は、Unity Cloud Build によってに呼び出されるエクスポート前メソッドのオプショナルパラメーターとしても使用されます。

 とあります。

 ビルド前処理などのEditor拡張のコードなどでは、Textアセットではなく、こちらのBuildManifestObjectを使う必要があるようです。

さいごに

 CloudBuildManifestを用いれば、

 「あれ、これどのバージョンだっけ!?」

 ということが避けられそうですね!