LoginSignup
1
0

More than 1 year has passed since last update.

【Unity】Cloud Build APIを使って最終ビルドの一覧を表示する

Last updated at Posted at 2022-02-11

実現する事

Unity Cloud Buildの最終ビルドをWebページ公開

  • ビルドされたすべてのプラットフォームの最終ビルドを表示
  • WebなのでPC、スマホ問わず確認可能です

スクリーンショット 2022-02-11 18.20.46.png

必要なもの

  • PHPが使えるサーバー
  • Cloud BuildのApi Key
  • Organization ID
  • Project ID

使用上の注意

URLを知っていれば誰でも見れます。
URLの流出に注意が必要です。
対策として、BASIC認証を設定したり、Webでは表示せずアプリ上からダウンロードリンクを取得してアプリからのみリンクを開く事も可能です。

各IDの取得

Unityのコンソールから各値を取得します。

Cloud BuildのApi Key取得

DevOps > Settings > API Settings
スクリーンショット 2022-02-11 16.53.04.png

Organization IDの取得

Projects > Organaization Settings > Organization details
スクリーンショット 2022-02-11 16.52.48.png

Project IDの取得

Projects > Project Settings > Project details
スクリーンショット 2022-02-11 16.54.20.png

ソース

下記、2つのソースをサーバーに設置してindex.htmlにアクセスするだけです。
例えば、サーバーのappディレクトリに設置した場合は、https://xxx.com/app/がURLになります。2つのファイルは同じディレクトリに配置してください。

getLatest.phpの書き換える箇所
取得したIDを設定してください。

field 説明
API_KEY Cloud BuildのApi Keyを設定
ORGANIZATION_ID Organization IDを設定
PROJECT_ID Project IDを設定
FAILURE_MESSAGE 取得失敗時のエラーメッセージを設定
getLatest.php
<?
const API_KEY = '';
const ORGANIZATION_ID = '';
const PROJECT_ID = '';
const FAILURE_MESSAGE = '取得に失敗しました。時間を空けてお試しください。';

// エラーを画面に表示しない
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);

try
{
  $headers =
  [
    'Content-Type: application/x-www-form-urlencoded',
    'Authorization: Basic ' . API_KEY
  ];

  $context = stream_context_create([
    'http' => [
      'method' => 'GET',
      'header' => implode("\r\n", $headers),
      'ignore_errors' => true
    ]
  ]);

  $response = new Responses();
  $response->infos = [];

  // 各プラットフォームの最終ビルド情報の取得
  $buids = file_get_contents('https://build-api.cloud.unity3d.com/api/v1/orgs/' . ORGANIZATION_ID . '/projects/' . PROJECT_ID . '/buildtargets?include_last_success=true', false, $context);

  if (strpos($http_response_header[0], '200') !== false)
  {
    // ビルド情報からシェア情報の取得
    $buidObjs = json_decode($buids, true, 512, JSON_THROW_ON_ERROR);
    for ($i=0; $i < count($buidObjs); $i++)
    {
      $buidObj = $buidObjs[$i];
      $resShare = file_get_contents('https://build-api.cloud.unity3d.com/api/v1/orgs/' . ORGANIZATION_ID . '/projects/' . PROJECT_ID . '/buildtargets/' . $buidObj['buildtargetid'] . '/builds/' . $buidObj["builds"][0]['build'] . '/share', false, $context);

      if (strpos($http_response_header[0], '200') !== false)
      {
        $resShareJson = json_decode($resShare, true, 512, JSON_THROW_ON_ERROR);
        $obj = new Response();
        $obj->platform = $buidObj['platform'];
        $obj->platformName = ConvertToPlatformName($buidObj['platform']);
        $obj->shareUrl = 'https://developer.cloud.unity3d.com/share/share.html?shareId=' . $resShareJson['shareid'];
        $obj->finished = $buidObj['builds'][0]['finished'];
        $obj->build = $buidObj['builds'][0]['build'];
        $response->infos[] = $obj;
      }
      else
      {
        throw new WebAPIException(FAILURE_MESSAGE);
      }
    }
  }
  else
  {
    throw new WebAPIException(FAILURE_MESSAGE);
  }

  // Jsonとして書き出し
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);

}
catch (Exception $e)
{
  echo $e->getMessage();
  exit();
}

function ConvertToPlatformName($current)
{
    switch ($current)
    {
      case 'ios':
        return 'iPhone';
      case 'android':
        return 'Android';
      default:
        return $current;
    }
}

class Responses
{
  public $infos;
}

class Response
{
  public $platform;
  public $platformName;
  public $shareUrl;
  public $build;
  public $finished;
}

class WebAPIException extends Exception
{
  public function __construct($msg){
    parent::__construct($msg, 800);
  }
}
index.html
<!doctype html>
<html lang="jp">
<head>
  <meta charset="utf-8">
  <title>Download App</title>
  <meta name="description" content="ダウンロード">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="robots" content="noindex"><!-- クローラ-の巡回拒否 -->
  <style type="text/css">
    body, input, textarea
    {
      font-family: "游ゴシック Medium", "Yu Gothic Medium", "游ゴシック体", "YuGothic", "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", "Meiryo", "verdana", sans-serif;
    }
    h1
    {
      text-align: center;
      font-size: 25px;
      margin-bottom: 35px;
    }
    a
    {
      color: #1669a6;
      text-decoration: none;
    }
    ul, ol
    {
      background: #fcfcfc;
      padding: 0.5em 0.5em 0.5em 2em;
      border: solid 3px gray;
    }
    ul li, ol li
    {
      line-height: 1.5;
      padding: 0.5em 0;
    }
    #wrap
    {
      max-width: 350px;
      margin-left: auto;
      margin-right: auto;
      padding: 2em;
    }
    #wrap p
    {
      font-size: 13px;
    }
    #loading
    {
      position: absolute;
      left: 0; top: 0;
      width: 100%; height: 100%;
      background: white;
      z-index: 2147483647;
    }
    #loading p
    {
      text-align: center;
      margin: 0;
      position: absolute;
      top: 50%;
      left: 50%;
      margin-right: -50%;
      transform: translate(-50%, -50%)
    }
    #loading span
    {
      font-size: 3em;
      display: inline-block;
      background-color: #dcdcdc;
      margin: 0;
      animation: loading .7s infinite alternate;
    }
    #loading span:nth-child(2) { animation-delay: .1s; }
    #loading span:nth-child(3) { animation-delay: .2s; }
    #loading span:nth-child(4) { animation-delay: .3s; }
    #loading span:nth-child(5) { animation-delay: .4s; }
    #loading span:nth-child(6) { animation-delay: .5s; }
    #loading span:nth-child(7) { animation-delay: .6s; }
    #loading span:nth-child(8) { animation-delay: .7s; }
    #loading span:nth-child(9) { animation-delay: .8s; }
    #loading span:nth-child(10) { animation-delay: .9s; }
    @keyframes loading
    {
      0% { transform: scale(1.2); }
      100% { transform: scale(0.8); }
    }
  </style>
</head>

<body>
  <div id="loading"><p><span>N</span><span>O</span><span>W</span><br><span>L</span><span>O</span><span>A</span><span>D</span><span>I</span><span>N</span><span>G</span></p></div>
  <div id="wrap">
    <h1>ダウンロード</h1>
    <ul id="results"></ul>
    <p id="mobileMsg">※iPhoneはSafari、AndroidはChromeで開いてください。</p>
  </div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(function()
{
    $.ajax('getLatest.php',
      {
        type: 'get',
        dataType: 'json'
      }
    )
    // [検索成功]
    .done(function(data)
    {
      let $results = $("#results");

      if(data == null
        || data.infos == null
        || data.infos.length <= 0)
      {
        $results.append($("<li>").text("ビルドがありません。"));
        $("#mobileMsg").css("display", "none");
        return;
      }

      let isMobile = false;
      for (let i = 0; i < data.infos.length; i++)
      {
        let info = data.infos[i];
        let date = new Date(info.finished);
        let li = $("<li>");
        let a = $("<a>").attr("href", info.shareUrl).text(info.platformName + " #" + info.build + " (" + date.toLocaleDateString() + "更新)");

        $results.append(li.append(a));
        if(info.platform == "ios" || "android")
        {
          isMobile = true;
        }
      }

      if(!isMobile)
      {
        $("#mobileMsg").css("display", "none");
      }

      $("#loading").fadeOut(200);
    })
    // [検索失敗]
    .fail(function(XMLHttpRequest, textStatus, errorThrown)
    {
      $("#loading > p").html("取得の失敗<br>[" + XMLHttpRequest.responseText + "]<br>" + textStatus + "<br>" + errorThrown.message);
    });
});
</script>
</html>

表示する内容をカスタマイズしたい!

Unity Cloud Build API
サイトで簡単に取得テストできます。ここでテストしてカスタマイズ内容が決まったらgetLatest.phpを修正してください。

Unity Cloud Build APIの概要

SwaggerベースのREST APIです。
端的に説明すると、特定の決まったURLにアクセスする事で情報を取得したり、ビルドを走らせたりする事ができます。

現在の取得の流れ

getLatestSequence.png

適応方法

テストで取得したい内容が確定するとURLが確定します。そのURLにアクセスする事で情報が取得できます。
getLatest.phpのURLを置き換えてください。

'https://build-api.cloud.unity3d.com/api/v1/orgs/' . ORGANIZATION_ID . '/projects/' . PROJECT_ID . '/buildtargets?include_last_success=true

テスト手順

1. サイトトップにAPI Keyを入力

スクリーンショット 2022-02-11 19.19.42.png

2. 左のリストから実行したい内容を選択

※スクロールでも可
スクリーンショット 2022-02-11 19.24.51.png

3. 項目を入力してリクエスト

下記は、読み替えてください。

サイト上 実際の名称
orgid Organization ID
projectid Project ID

スクリーンショット 2022-02-11 19.26.52.png

Webでは表示したくない

index.htmlを削除

アプリ内(Unity上)から取得

下記コードをAssets配下に保存

CloudBuildHelper.cs
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public static class CloudBuildHelper
{
    [Serializable]
    public class BuildInfos
    {
        public BuildInfo[] infos;

        public override string ToString()
        {
            var msg = $"{GetType().Name} -\n";
            if (infos.Length > 0)
            {
                foreach (var info in infos)
                {
                    msg += $"{info}\n";
                }
            }
            else
            {
                msg += "ビルドがありません。";
            }
            return msg;
        }
    }

    [Serializable]
    public class BuildInfo
    {
        public string platform;
        public string platformName;
        public string shareUrl;
        public int build;
        public string finished;

        public override string ToString() => JsonUtility.ToJson(this, true);
    }

    public static IEnumerator GetLatestBuildInfos(string uri, Action<BuildInfos> successAction, Action<string> failedAction)
    {
        using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result == UnityWebRequest.Result.Success
                && !string.IsNullOrEmpty(webRequest.downloadHandler.text))
            {
                var obj = JsonUtility.FromJson<BuildInfos>(webRequest.downloadHandler.text);
                if (obj != default
                    && obj.infos.Length > 0)
                {
                    foreach (var info in obj.infos)
                    {
                        if (DateTime.TryParse(info.finished, out DateTime dateTime))
                        {
                            info.finished = dateTime.ToString("yyyy/MM/dd");
                        }
                    }
                    successAction?.Invoke(obj);
                }
                else
                {
                    failedAction?.Invoke($"Error: ダウンロードした内容を展開できませんでした。管理者にお問合せください。\n{webRequest.downloadHandler.text}");
                }
            }
            else
            {
                failedAction?.Invoke($"Error: {webRequest.error}");
            }
        }
    }
}

下記の形で利用できます。
第一引数にgetLatest.phpのURLを設定してください。

StartCoroutine(CloudBuildHelper.GetLatestBuildInfos(
    "https://xxxxx.com/getLatest.php",
    response =>
    {
        // 成功した場合
        // 下記の形で取得できます。
        // response.infos[0].platform;
        // response.infos[0].platformName;
        // response.infos[0].shareUrl;
        // response.infos[0].build;
        // response.infos[0].finished;

        // [確認用] Json形式で一覧書き出し
        Debug.Log(response);
    },
    error =>
    {
        // 失敗した場合
    }));

Console: [確認用] Json形式で一覧書き出し

{
    "platform": "ios",
    "platformName": "iPhone",
    "shareUrl": "https://developer.cloud.unity3d.com/share/share.html?shareId=xxxxx",
    "build": 44,
    "finished": "2022/02/10"
}
{
    "platform": "android",
    "platformName": "Android",
    "shareUrl": "https://developer.cloud.unity3d.com/share/share.html?shareId=xxxxx",
    "build": 39,
    "finished": "2022/02/10"
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0