GASから.assetを作ってプルリクを投げる
たまにやってる自分のQiita転載です。
GASから.assetを作ってプルリクを投げる - Qiita
Google Spread Sheetで文言とか持ってくるの…めんどいな
Google Spread Sheetで文言を管理することがあると思います。もちろん、Excel等もあると思いますが今回はSpread SheetとGASの連携による話になります。
Google Spread Sheetで文言管理するのはいいけど文字を打って写したり、GASを使ってファイルにしてDriveに保存してダウンロードする。などの手順を踏むかと思います。今回はそれすらもめんどくさくなったエンジニアの話です。
そんなこんなで、GASもapiを叩くことができるならGitHubのapi叩けばよくね!?って思ったら最高に用途にあったライブラリを作っている方がいらっしゃいました。 GitHub API を GAS でいい感じで叩くためのライブラリを作った (未完)
最高です。GAS用のライブラリなのでSpread Sheetと直結です。
先に.gsが見たい方はこちら https://github.com/MizoTake/CreateAssetsFromGAS
UnityのSampleリポジトリはこちら https://github.com/MizoTake/CreateAssetsFromGASUnitySample
以降は解説に入りますが、GASのファイル作成やUnityのファイル作成など基礎的なところには触れていませんのでご了承ください。
導入する
GASのライブラリ何それおいしいの?ってところから自分は始まりました。おいしかったです(楽さ加減が)

GASを開いたらリソースという項目があるのでクリックするとライブラリという項目が出るのでクリックします。
すると

テキストフィールドに今回使用するGitHubのライブラリキーとなる1F4yn329GjHKdcXu9nm0uBZHFo40NGRUF8dfZCTHM1KjXpOXYr2BzIIcJを入力します。(GitHubのREADME.mdにもあります)
あとは、バージョンを最新に設定すれば終わりです。
Unityで.assetを作る
要するにScriptableObjectです。
等を参考に作ってみてください。 今回はSampleのプロジェクトを作成したので、それをベースにお話しします。 https://github.com/MizoTake/CreateAssetsFromGASUnitySample/blob/master/Assets/Message.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Message : ScriptableObject
{
public Talk[] flow;
[System.Serializable]
public class Talk
{
public int id;
public string name;
public string message;
}
}

Signboardということで看板に話しかけた時に表示するメッセージだと仮定しておきます。
Inspectorには上図のように表示されます。 IdとかMessageは適当に入れています。
これで、.assetにプルリクを送る準備ができました。 何故先に作っておくかと言うと.metaを作っておきたいからです。
GASから.assetを作る
.assetを作るためにはScriptableObjectのテキスト情報を知らねばならないのでテキストエディタで開きます。 https://github.com/MizoTake/sLowZoo/blob/master/Assets/sLowZoo/Data/MessageSection/Signboard.asset
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3db36c1731914ec4496d0d83b3360bc7, type: 3}
m_Name: Signboard
m_EditorClassIdentifier:
flow:
- id: 0
name:
message: "\u770B\u677F\u3060\u3088"
- id: 0
name:
message: "\u770B\u677F\u304B\u3082\u3057\u308C\u306A\u3044"
- id: 0
name:
message: "\u304A\u3084\u2026\u770B\u677F\u306E\u69D8\u5B50\u304C\u2026"
Sceneとかの構成と同じくyamlのようです。ここから編集が必要な情報はflow:以降の文字列になると思います。
ただし、日本語はutf16に変換されているようなのでgasでも変換処理が必要そうです。
処理の一部抜粋をしつつ少し解説します。
まず、.assetのflow:前の文字列を取得します。gasの文字列操作で頑張るとなんとかなります。
var githubUrl = 'https://raw.githubusercontent.com/MizoTake/CreateAssetsFromGASUnitySample/master/Assets/Signboard.asset'
// プルリク対象のテキスト情報をgithubからrawで持ってくる
var txtBlob = UrlFetchApp.fetch(githubUrl)
// "flow:"という文字列を検索し、indexを取得
var index = txtBlob.getContentText().lastIndexOf('flow:')
// flow: + 改行コード = 6とし、flow:以降の文字列を取得
var deleteTarget = txtBlob.getContentText().slice(index + 6)
// ScriptableObjectの参照情報である部分をheaderとして取得
var header = txtBlob.getContentText().replace(deleteTarget, '')
上記のコードでScriptableObjectのflow:までの文字列を取得できたので、中身となるidなどをSpread Sheetから取得し作成します。
まず使用するSpreadSheetはこちらです。
会話

これを踏まえて以下の処理を使用します。
var targetName = 'Signboard'
var path = 'Assets/'
var sheet = SpreadsheetApp.openById('1KsmJTKSRGXMd1YdtJDLPzkp6xvDskaXRezc7pyq2WVQ').getSheetByName(targetName)
var fileName = targetName + '.asset'
var result = ""
// idの列を取得
var idRange = sheet.getRange(2, index('id'), sheet.getLastRow(), 1).getValues()
// nameの列を取得
var nameRange = sheet.getRange(2, index('name'), sheet.getLastRow(), 1).getValues()
// messageの列を取得
var messageRange = sheet.getRange(2, index('message'), sheet.getLastRow(), 1).getValues()
idRange.map(function(id, idIndex) {
id.map(function(item, index) {
if (item != "" && typeof item !== undefined) {
// yamlのformatに合わせる
var name = ''
if(nameRange[idIndex] != '') {
// escape関数でutf16に変換
name = "\"" + escape(nameRange[idIndex]).split('%').join('\\') + "\""
}
result += " name: " + name + "\n"
result += " message: \"" + escape(messageRange[idIndex]).split('%').join('\\') + "\"\n"
}
});
});
function index(target) {
var contentTitles = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
return contentTitles.indexOf(target) + 1
}
以上までやるとheaderとresultを結合すれば、.assetの中身がgasで作れちゃいます。
%YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInternal: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 3db36c1731914ec4496d0d83b3360bc7, type: 3} m_Name: Signboard m_EditorClassIdentifier: flow: - id: 1 name: message: "\u770B\u677F\u3060\u3088" - id: 2 name: message: "\u770B\u677F\u304B\u3082\u3057\u308C\u306A\u3044" - id: 3 name: message: "\u304A\u3084\u2026\u770B\u677F\u306E\u69D8\u5B50\u304C\u2026" - id: 4 name: message: "\u770B\u677F\u3067\u3059\u3088"
結合結果です。
あとは、ライブラリさんに頼ってプルリクを出すだけです。
Githubにプルリクを出す
冒頭で紹介したライブラリを使用するには
- UserName
- MailAddress
- Personal access tokens
が必要となります。
tokenは調べると情報は出てくるので割愛します。
そして、tokenを扱うことを前提としているので.gsに貼り付けることは基本的にしないようにしたいのでGASの機能であるユーザープロパティを使用します。
プロジェクトのプロパティから設定できるように見えるのですが、自分は何故か反映されずソースコードからであれば反映されたのでメソッドを用意しています。
// add user property from code
function setup() {
var prop = PropertiesService.getUserProperties();
prop.setProperty('NAME', '');
prop.setProperty('EMAIL', '');
prop.setProperty('GITHUB_TOKEN', '');
}
これに必要な情報を張り付けて1度実行すれば以降は入力する必要はありません。
次にpush対象とするリポジトリを設定します。
function setupGitHub() {
var prop = PropertiesService.getUserProperties()
var option = { name:prop.getProperty('NAME') , email:prop.getProperty('EMAIL') }
return new GitHubAPI.GitHubAPI('MizoTake', 'CreateAssetsFromGASUnitySample', prop.getProperty('GITHUB_TOKEN'), option)
}
のように設定すれば大丈夫です。MizoTakeやCreateAssetsFromGASUnitySample部分はご自身の設定に置き換えてください。 ちなみにMizoTakeのところはOrganization名でも大丈夫です。
さて、コミットを作っていきましょう。 以降は、基本的にGitHubのAPI情報を参考に見ていると「あーね」ってなる気がします。 https://developer.github.com/v3/
// 対象となるfileのpath, fileのテキスト情報, githubライブラリで使用しているクラスのインスタンス
function addCommitData(filePath, blobData, github) {
var txtBlob = UrlFetchApp.fetch(githubUrl + filePath)
var index = txtBlob.getContentText().lastIndexOf('flow:')
// flow: + 改行コード = 6
var deleteTarget = txtBlob.getContentText().slice(index + 6)
var header = txtBlob.getContentText().replace(deleteTarget, '')
// .assetのテキスト情報
var blob = github.createBlob(header + blobData)
// GitHub apiに沿った配列を作成
commitData.push({
'path': filePath,
'mode': '100644',
'type': 'blob',
'sha': blob['sha']
})
}
これでcommitDataという配列にcommitする情報を与えていきます。
function createCommit(github) {
var branch = github.getBranch(branchName)
var pTree = github.getTree(branch['commit']['commit']['tree']['sha'])
var origin = pTree['tree']
// 現在のtree情報をbaseとして新しいtree情報を与えます
var data = {
'base_tree': pTree['sha'],
'tree': commitData
}
var tree = github.createTree(data)
// commitのメッセージを決めれます。
var commit = github.createCommit('new GSS Data', tree['sha'], branch['commit']['sha'])
var result = github.updateReference(branchName, commit['sha'])
}
コミットを作ったのでプルリクに紐づけます。
// プルリクが既にあるとエラーのためめんどくさいからtry catch
function createPullRequest(date, github) {
try {
const previousDay = new Date(date.getTime() + 24 * 60 * 60 * 1000)
// プルリクのタイトルを日付に設定
var title = Utilities.formatDate(previousDay, Session.getScriptTimeZone(), "yyyy/M/d")
// masterをマージ対象とする
return github.createPullRequest(title, branchName, 'master')
} catch (error) {
Logger.log(error)
}
}
これで成功するとプルリクが作れちゃいます!

注意点としては
- プッシュするbranchは先に作っておくこと(branchを作るapiまでカバーしていないため)
- treeの情報を間違えると色んなデータが消えたりしたことになるので配列は確認をして扱っていくこと
UnityEditorで確認する
gitでbranchを自動プルリクのbranchに切り替えてpullをします。 そして、UnityEditorに戻ると

4つ目の項目まで表示されています!
これでファイルを作ってプルリクを投げる自動化は、一区切りです。
おまけ: 定期的に自動でコミットをさせていく

編集を押すと現在のプロジェクトのトリガーという項目があるので選択します。

トリガーを追加とあるので選択します。

上図のように設定すると毎日午前0時~1時の間に自動的にpushToGitHubという関数を実行します。
これをすることで、毎日自動的にコミットがされることになります。何もしなくてもプルリクなりコミットが作られます。便利
所感
ここまで自動化すると結構便利な気がしています。 これはUnityに限らず色んな静的なファイルに有効だと思うので色々応用するのは良い気がしています。