- 概要
- 動作環境
- 備考
- GCPのサービスアカウント対応手順
- CronによるGoogle SheetsからのStringTableCollectionの定期更新
- おまけ、GitHub ActionsでSheets連携をCronジョブ対応
- 拡張パッケージ (Example含む)
- 雑感
概要
今回も前回に引き続きUnity Localizationに関するTipsです。
GCPのサービスアカウントでプライベートSheetsと連携する方法を紹介します。
Unity Localization標準のOAuth認証を使用すれば一応プライベートSheetsにアクセスすることは可能ですが、Cronジョブなどで定期更新に対応したいときに不都合があります。
そんな問題をGCPのサービスアカウントを用いて解決したいと思います!
おまけでCronジョブでGoogle SheetsからStringTableCollectionを定期更新するTipsも紹介します。
前回の記事
良かったら合わせてどうぞ!
動作環境
- Unity 2021.3.6f1
- Unity 2020 でも可
- Unity Localization 1.3.2
備考
今回紹介する機能は拡張パッケージとしても用意しています。
記事の最後にGitHubへのリンクを記載していますため、よろしければどうぞ。
GCPのサービスアカウント対応手順
サービスアカウントの作成
サービスアカウントの認証用JSON形式の鍵をダウンロード
以下のようなJSON形式の鍵がダウンロード出来ます。
JSON文字列は後述するクラスに渡すために使用します。
{ "type": "service_account", "project_id": "xxx-workspace", "private_key_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "private_key": "-----BEGIN PRIVATE KEY-----\n...", "client_email": "unity-localization-example@xxx.xxx.gserviceaccount.com", "client_id": "121212121212121212121", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "..." }
※アクセス権限に使用する鍵のため、厳重に管理してください
対象のSheetsにサービスアカウントのアクセス権限を付与
Pullのみの場合は閲覧権限、Pushも行う場合は編集権限をサービスアカウントに与えてください。
GCPのサービスアカウント向けSheetsServiceProviderを作成
1.3.2時点のUnity LocalizationにはGCPのサービスアカウント連携用クラスが用意されていなかったため用意します。
いずれ公式から提供される気がしますが、今回は実装します。
public class ServiceAccountSheetsServiceProvider : IGoogleSheetsService { private readonly string _serviceAccountKeyJson; private readonly string _applicationName; public ServiceAccountSheetsServiceProvider( string serviceAccountKeyJson, string applicationName) { _serviceAccountKeyJson = serviceAccountKeyJson; _applicationName = applicationName; } public SheetsService Service => GetSheetsService(); private SheetsService GetSheetsService() { var credential = GoogleCredential.FromJson(_serviceAccountKeyJson); var initializer = new BaseClientService.Initializer { HttpClientInitializer = credential, ApplicationName = _applicationName, }; var sheetsService = new SheetsService(initializer); return sheetsService; } }
serviceAccountKeyJson
には、先程作成したサービスアカウントのJSON文字列を渡すapplicationName
には GoogleSheetsService (ScriptableObject) に設定したApplicationName
を指定
GCPのサービスアカウント向けSheetsServiceProviderを使用してPull or Push
- 先程作成した
ServiceAccountSheetsServiceProvider
を用いてPull or Pushを実施- ※以下の実装は、前回の記事で紹介したコードを
ServiceAccountSheetsServiceProvider
に入れ替えたもの
- ※以下の実装は、前回の記事で紹介したコードを
// 対象のStringTableCollectionを取得 var collection = AssetDatabase.LoadAssetAtPath<StringTableCollection>("Assets/<path to StringTableCollection>"); // GoogleSheetsExtensionをStringTableCollectionから取得 var sheetsExtension = collection.Extensions.OfType<GoogleSheetsExtension>().FirstOrDefault(); // *前回の記事と異なり、ここでServiceAccountSheetsServiceProviderを使用 var serviceProvider = new ServiceAccountSheetsServiceProvider( serviceAccountKeyJson: "<GCPサービスアカウントのJSON形式の鍵の文字列>", applicationName: "<GoogleSheetsService (ScriptableObject) に設定したApplicationName>"); // Google Sheetsアクセス用インスタンスを生成 var sheets = new GoogleSheets(serviceProvider); // ※必ずSpreadSheetIdをGoogleSheetsインスタンスに指定してください! sheets.SpreadSheetId = sheetsExtension.SpreadsheetId; // 対象のStringTableCollection内の全言語(全Locale)のPullを実施 sheets.PullIntoStringTableCollection( sheetId: sheetsExtension.SheetId, collection: collection, columnMapping: sheetsExtension.Columns);
上記の方法を使用することで、GCPのサービスアカウントでPull及びPushが可能となります。
以上がGCPのサービスアカウントを使用した連携方法となります。
CronによるGoogle SheetsからのStringTableCollectionの定期更新
さて、今回の実質的な本題に移りたいと思います!
今回、GCPサービスアカウントを用いた本当の理由はCron(定期ジョブ)を使ったStringTableCollection更新に対応させるためです!
そもそもなぜOAuthが使えないのか
Cron対応なしで運用する場合はそもそもGCPサービスアカウント対応は不要です。
概要で少し触れましたがプライベートのSheetsには標準のOAuth認証でも連携可能です。
Unity Localization標準のOAuthの問題は、主に以下の2点です。
- 認証にUnityエディタ上でのUI操作が必要
- batchモード(CLI)での認証が困難
- 認証後に生成される認証ファイルが
Library/Google/GoogleSheetsService
以下で管理Library
フォルダはUnityにおけるキャッシュフォルダでもあるため、クリーンビルドを行うCIなどと相性が悪い
というわけで、
batchモードで認証を完結させるために、JSON形式の鍵文字列を渡すだけで認証が可能なServiceAccountSheetsServiceProvider
を用意した感じです。
鍵文字列をServiceAccountSheetsServiceProviderに渡す方法
サービスアカウントの鍵はアクセス権限を握るセンシティブなデータファイルなので、鍵の文字列をgitリポジトリ内で管理することはタブーです!
セキュリティを考慮して使用後はキャッシュとして残らない方法と取りましょう。
1. 環境変数で渡す
CIとも相性がよくジョブプロセス単位で管理可能なため、筆者としても環境変数を利用する方法がオススメです!
const string EnvironmentGoogleServiceAccountKey = "UNITY_LOCALIZATION_GOOGLE_SERVICE_ACCOUNT_KEY"; var serviceAccountKeyJson = Environment.GetEnvironmentVariable(EnvironmentGoogleServiceAccountKey); var serviceProvider = new ServiceAccountSheetsServiceProvider( serviceAccountKeyJson: serviceAccountKeyJson, applicationName: "<GoogleSheetsService (ScriptableObject) に設定したApplicationName>");
2. ジョブ中のみ鍵ファイルを生成
環境変数を使用できない場合は、ジョブ中のみJSON形式の鍵ファイルを生成する対応が一つの選択肢になります。
以下はbashを用いて環境変数からJSON鍵ファイルを復元する方法です。
$ echo "${UNITY_LOCALIZATION_GOOGLE_SERVICE_ACCOUNT_KEY}" > "<path to key>/service-account-key.json"
あとは、上記鍵ファイルからJSON文字列を取り出して渡すだけです。
const string JsonKeyPath = "<path to key>/service-account-key.json"; string serviceAccountKeyJson = File.ReadAllText(keyJsonPath); var serviceProvider = new ServiceAccountSheetsServiceProvider( serviceAccountKeyJson: serviceAccountKeyJson, applicationName: bundle.SheetsServiceProvider.ApplicationName);
ジョブの成否に関わらず、不要になったら生成した鍵ファイル削除を忘れずに!
$ rm "<path to key>/service-account-key.json"
余談、現時点の game-ci/unity-builder では独自の環境変数をメソッドに渡せない
GitHub ActionsでGameCIを使っている人は結構いるかと思いますが、
残念ながらgame-ci/unity-builder
のbuildMethod
でUnity側のメソッドを叩くときに独自の環境変数を渡せないようでした。。。 (知っている方がいたらぜひ教えてください!)
そんな経緯もあって、「ジョブ中のみ鍵ファイルを生成」という方法を紹介しました。
Pull or Push
あとは「GCPのサービスアカウント向けSheetsServiceProviderを使用してPull or Push」と同様に生成したserviceProvider
を使用するだけです。
// Google Sheetsアクセス用インスタンスを生成 var sheets = new GoogleSheets(serviceProvider); // ※必ずSpreadSheetIdをGoogleSheetsインスタンスに指定してください! sheets.SpreadSheetId = sheetsExtension.SpreadsheetId; // 対象のStringTableCollection内の全言語(全Locale)のPullを実施 sheets.PullIntoStringTableCollection( sheetId: sheetsExtension.SheetId, collection: collection, columnMapping: sheetsExtension.Columns);
おまけ、GitHub ActionsでSheets連携をCronジョブ対応
サンプルを用意したので、よければ参考にして下さい。
- pull-localization.yml (GitHub Actionsのワークフロー)
- ExampleLocalizationSynchronizationMenu.cs (Unity側のメソッド)
- コード内のregion "Service Account Key from Environment Variable" を参照
大まかな流れは以下です。
- GitHub ActionsのSecrets (
GOOGLE_SERVICE_ACCOUNT_KEY_JSON_BASE64
) から鍵文字列をデコードしてJSONファイルを生成- 鍵文字列のBase64対応は必須ではないですが、環境依存な文字(かっこやカンマなど)があっても対応しやすいので入れています
- Unity側のメソッド
PullAllLocalizationTablesFromTempKeyJson
をgame-ci/unity-builder
でbuildMethod
経由で実行game-ci/unity-builder
のbuildMethod
の使用方法はGameCIのドキュメントを参照
- Pull完了後は生成した鍵ファイルを削除
- Commit, Push して Pull Request
ちなみに、サンプルでは以下のようなプルリクエストが発行されます。
拡張パッケージ (Example含む)
使用方法はREADME.mdの "Feature" を参照
雑感
Unity Localization第2弾でした。
また時間が空いてしまいました。。。
UnityとGitHub Actionsを活用したワークフローに関するノウハウも結構溜まってきたので、機会があれば紹介したいですね。
次回の記事は決まってませんが、状況が落ち着いたらまた何か書きます。
それでは~