apk/ipaをコマンドラインから UnityCloudBuildにアップ


※24/01/18追記
　iOS16以上から UnityCloudBuild上でビルドしていないと
　iPhone/iPadにインストール出来なくなっています…。
　UnityForum 22/10/11に質問あがってましたが 未だ解決せず [[UnityForum](https://forum.unity.com/threads/unable-to-install-app-with-share-link-on-ios-16.1347269/)]
　私は諦めて Firebase AppDistributionを使用することにしました。

※19/07/12追記
　いつの間にか変わって無料でも出来るぽい [[公式](https://unity3d.com/jp/get-teams)]

※19/06/26追記
　2018年1月くらいから UnityCloudBuild、有料化したんですね(Advanced・Proのみ)
　知りませんでした… [[公式](https://unity3d.com/jp/teams)]

※この記事を書いてる途中(2019/4/17)に ふとググったら去年10月[[公式](https://github.com/Unity-Technologies/FPSSample/blob/master/Assets/Scripts/Networking/Deploy/Editor/BuildUpload.cs)]に…  :innocent:

それはさておき、2017年12月15日に

>Unity Cloud Buildがローカル環境で作ったビルド成果物をアップロードできるようになった

という[記事](https://qiita.com/RyotaMurohoshi/items/3d293fd6551959c53414)を見かけました。

しかし、WebからじゃなくCLIからアップロードしたいと思い、
きっと誰かが良い記事書いてくれるのでは…と思ってましたが
びっくりするくらい簡単なDeployGate[^appleSend]があるせいか、
はたまた そもそも興味持たれないのか 待てど暮らせど見つからず[^waitDay] 
[^appleSend]: iOSのみならTestFlight、apkのみならGooglePlayテスト配信とかもありますね
[^waitDay]: （1週間待っただけですが）

CloudBuildのページあたりを見ていましたらAPIドキュメント発見

**CloudBuild API Document** (v1.0.0時点)
https://build-api.cloud.unity3d.com/

　よし作ってみるか！

　…なるほど、まったくわからん :innocent:

とまでは行きませんでしたが
ChromeDeveloperで挙動やログを追いかけて なんとか出来ました [^birth]
どれだけの方が利用するかわかりませんが、とりあえず公開

[^birth]: 実際作ったのは2017年12月28日。1年以上寝かしてました。

## 前提
- 私の環境: MacOSX10.14.4(iMac4K)
- 成果物は apk / ipa のみしかアップロードチェックしていません
- [unity DashBoard](https://developer.cloud.unity3d.com/projects/)
にて アプリ名登録まで済んでいるものとします
- bashシェルスクリプトで順番に説明
- json解析に[jq](https://stedolan.github.io/jq/download/)を使用
- 置換にgsedを使用
- curl等、色々間違ってたらすいません…
- ですので最低限のみ記載

以下スクショの文字列は例ですので DashBoard眺めてメモってください。
![スクリーンショット 0031-04-17 18.06.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94354/bc45d50b-3220-ecb3-0343-c526565b1c7c.png) PROJECT_ID(ここではTest)と UPID(ここでは3dc…)をメモ

![スクリーンショット 0031-04-17 18.06.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94354/26f9de44-7d4a-0ec6-0ebf-9fda95eaa4ef.png) Testの下の ORG_ID(ここではdeveloper…)をメモ

成果物ファイル名は アスキーコード 0x20〜0x40 辺りを使ってましたら
%20〜%40 に変換してください（URLEncode?)

## 初期設定

```sh
API_KEY= #DashBoard - プロジェクト - Develop - Settings - Cloud Build - APIキーに書いてある
PROJECT_ID= #DashBoard - プロジェクト名
ORG_ID= #DashBoard - プロジェクト名の下に書いてある
UP_ID= #DashBoard - プロジェクト - プロジェクト名の横に書いてあるUPID
FILE_NAME= #成果物ファイル名 xxxx.ipa 等をURLEncode
EXT= #成果物の拡張子(つまりapkかipa)

FILE_SIZE= #成果物のファイルサイズ `wc -c < 成果物ファイル名` とか

if [ "${EXT}" = "apk" ]; then
	PLATFORM=android
	TYPE_NAME=".APK file"
	TYPE_NAMEe=".APK%20file"
elif [ "${EXT}" = "ipa" ]; then
	PLATFORM=ios
	TYPE_NAME=".IPA file"
	TYPE_NAMEe=".IPA%20file"
else
	exit 1
fi

BUILD_API_DOMAIN="https://build-api.cloud.unity3d.com/api/v1"
ARTIFACT_API_DOMAIN="https://build-artifact-api.cloud.unity3d.com/api/v1"
BUILD_TARGET_ID="_local"
LOG=`mktemp --tmpdir=./`

function endJob {
	echo $1
	rm ${LOG}
}
```

## ビルドエリア生成して ビルド番号取得
```sh
curl -X POST -H "Content-Type: application/json" -H "Authorization: Basic ${API_KEY}" --data-binary "{\"platform\": \"${PLATFORM}\", \"label\": \"${COMMENT}\"}" ${BUILD_API_DOMAIN}/orgs/${ORG_ID}/projects/${PROJECT_ID}/buildtargets/${BUILD_TARGET_ID}/builds -o ${LOG} -s
BUILD_NO=`cat ${LOG} | jq .[0].build`
STAT=`cat ${LOG} | jq .[0].buildStatus`
if [ "${STAT}" != "\"success\"" ]; then
	endJob "BuildArea - Error: ${STAT}"
	exit 1
fi
```

## アーティファクト生成
```sh
curl -X POST -H "Content-Type: application/json" -H "Authorization: Basic ${API_KEY}" --data-binary "{\"name\":\"${TYPE_NAME}\",\"primary\":true,\"public\":false,\"files\":[{\"filename\":\"${FILE_NAME}\",\"size\":${FILE_SIZE}}]}" ${ARTIFACT_API_DOMAIN}/projects/${UP_ID}/buildtargets/${BUILD_TARGET_ID}/builds/${BUILD_NO}/artifacts -o ${LOG} -s
STAT=`cat ${LOG}`
if [ "${STAT}" != "Created" ]; then
	endJob "Generate Artifact - Error: ${STAT}"
	exit 1
fi
```

## アップロード
```sh
hc=$( curl -d "" -X POST -H "Upload-Length: ${FILE_SIZE}" -H 'Tus-Resumable: 1.0.0' -H 'Content-Type: application/offset+octet-stream' -H "Authorization: Basic ${API_KEY}" ${ARTIFACT_API_DOMAIN}/projects/${UP_ID}/buildtargets/${BUILD_TARGET_ID}/builds/${BUILD_NO}/artifacts/${TYPE_NAMEe}/upload/${FILE_NAME} -o ${LOG} -w '%{http_code}\n' -s )
if [ $hc != 201 ]; then
	STAT=`cat ${LOG}`
	endJob "Upload Post - Error: $hc\n${STAT}"
	exit 1
fi

hc=$( curl -X PATCH -H 'Upload-Offset: 0' -H "Content-Length: ${FILE_SIZE}" -H 'Tus-Resumable: 1.0.0' -H 'Content-Type: application/offset+octet-stream' -H "Authorization: Basic ${API_KEY}" --data-binary @"${1}" ${ARTIFACT_API_DOMAIN}/projects/${UP_ID}/buildtargets/${BUILD_TARGET_ID}/builds/${BUILD_NO}/artifacts/${TYPE_NAMEe}/upload/${FILE_NAME} -o ${LOG} -w '%{http_code}\n' -s)
if [ $hc != 204 ]; then
	STAT=`cat ${LOG}`
	endJob "Upload Patch - Error: $hc\n${STAT}"
	exit 1
fi
```

## シェアリンク作成
```sh
# GET:既に生成済みのShareID取得、POST:ShareID生成、DELETE:ShareID削除
curl -d "" -X POST -H "Content-Type: application/json" -H "Authorization: Basic ${API_KEY}" ${BUILD_API_DOMAIN}/orgs/${ORG_ID}/projects/${PROJECT_ID}/buildtargets/${BUILD_TARGET_ID}/builds/${BUILD_NO}/share -o ${LOG} -s
STAT=`cat ${LOG} | jq .shareid | gsed 's/"//g'`
if [ "${STAT}" == "null" ]; then
	cat ${LOG}
	endJob "ShareLink - failed"
	exit 1
fi

SHARE_URL="https://developer.cloud.unity3d.com/share/${STAT}"

#ここらへんで SHARE_URL をチャット等に送る

endJob "success"
```

出来上がったSHARE_URLをチャットやメール等に流せばOKかと。
Android・iOSからダウンロード・インストール・実行出来ました。

みんな幸せにな〜れ :beers:

規約違反とかありましたら記事消します…。