はじめに
前回のUnity関連の記事から2年以上経過してしまいました。
Unityで作ったiOSアプリにObj-CでPush通知を載せてみた
2年たっても変わらないのは、ipa生成までの時間です。 世間的にもCI周りの知見は充実している中、Unityからipa作成までを一貫した記事がなかったため社内で実施した内容を書いていきます。
ここが良い
(1)artifactがコミット別で取得できる リリース版、開発版と環境を分けた状態で取得が用意になるのはもちろん、 事後に不具合が見つかった場合に実機ベースの検証が必要!となれば どこのコードのコミットが原因かを特定することを技術者じゃないメンバーにもアクションを起こしてもらうこと、 配信サーバへアップロードするといったこともStagesにアップロードするjobを追加することで Gitlab CIの画面上から必要なメンバーにアップロードが可能な環境を用意することができるようになります。
また、制限時間をつけている場合はgitlabのサーバのディスク容量を考えてするべくつけていますが、時間が経過してartifactが消えてしまってもStageからRetryすることで、、
(2)ipa、アーカイブファイルの再取得が可能になる アプリの配信サーバに置いてあるipaファイルがどのコードと結びついているのか確信が持てない Macの入れ替えなどでXcodeアーカイブファイル(これも結構な容量)を入れ替え前のMacから引き継ぐといったことから卒業できます。 前提には、runnerとして動作するMacの開発環境は前回ビルド時と同じ、という条件が必要です。
(3)実装者の作業MacのCPU負荷を下げて作業効率を上げる 人が使役するお道具にとってかなり重要な役割だと思っています。 実際Unityをビルドするにあたって今回のソースはとても簡易な作りにしているにも関わらず、 それでも4分〜10分待たされる自分の作業Macで、合間にやるタスクが限定される、他の作業をすることでビルドの時間も長引くなり滅入るといった気持ち的なところも作業を分離できるのは大きな改善に繋がります。
gitリポジトリの構成
今回の説明で使うのは赤枠で囲っているファイル、フォルダです。
Gitlab CIの設定
.gitlab-ci.yml中に記載したくないパラメータはCI/CD PipelinesのSecret Variablesへ追加します。
USER_NAMEはgitlabCIのrunnerが動いているMac OSX でログインしているユーザ名 PATH_PHRASE → ビルド時に確認されるユーザ確認のダイアログを通過(されてるのかな・・・) DEVELOPMENT_TEAMはipa出力時に必要。対応する値は次のUnityプロジェクトの中で書いています。
.gitlab-ci.ymlの一部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
stages: - build job-unity-build: stage: build before_script: - export LANG=en_US.UTF-8 - security unlock-keychain -p $PATH_PHRASE /Users/$USER_NAME/Library/Keychains/login.keychain - export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer script: - pwd - cd PushOnUnity - /Applications/Unity5.6.1/Unity.app/Contents/MacOS/Unity -batchmode -buildTarget ios -quit -logFile ./build.log -executeMethod BuildClass.BuildiOS - cd ../AppTest - xcodebuild -project Unity-iPhone.xcodeproj -scheme Unity-iPhone clean archive -archivePath build/AppTest.xcarchive DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM | xcpretty - xcodebuild -exportArchive -archivePath build/AppTest.xcarchive -exportPath build -exportOptionsPlist ../exportOptions_enterprise.plist artifacts: expire_in: 1 hrs paths: - AppTest/build/AppTest.xcarchive - AppTest/build/Unity-iPhone.ipa only: - distribute |
Unityプロジェクトでの設定・記述
Editorの下に入れている二つのファイルについて
Xcodeでのプロジェクトの出力設定はコマンドラインから直接コードで変更するのは BuildClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using UnityEditor; using UnityEngine; public class BuildClass { /// <summary> /// iOSのビルドを行います /// </summary> public static void BuildiOS() { var versionString = PlayerSettings.bundleVersion; Debug.Log("Version got: " + versionString); // ビルドフォルダの名前 var buildFolderName = PlayerSettings.productName ; BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions(); buildPlayerOptions.scenes = new[] {"./Assets/aaaaa.unity"}; buildPlayerOptions.locationPathName = "../" + buildFolderName; buildPlayerOptions.target = BuildTarget.iOS; buildPlayerOptions.options = BuildOptions.None; string errorMessage = BuildPipeline.BuildPlayer(buildPlayerOptions); if(!string.IsNullOrEmpty(errorMessage)){ Debug.LogError("[Error!]" + errorMessage); } else { Debug.Log("[Success!]"); } } } |
Xcodeのプロジェクト生成後の設定変更はMyBuildPostprocessor の記述です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; public class MyBuildPostprocessor { [PostProcessBuildAttribute(1)] static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath) { // iOS以外のプラットフォームは処理を行わない if (buildTarget != BuildTarget.iOS) return; string projPath = buildPath + "/Unity-iPhone.xcodeproj/project.pbxproj"; PBXProject proj = new PBXProject(); proj.ReadFromFile(projPath); string teamPrefix = "RX99XXX99L";// チーム名はADCで確認できるPrefix値を設定する:この設定値はダミー string target = proj.TargetGuidByName("Unity-iPhone"); string provisioningProfileName = "プロビジョニングプロファイルのファイル名";// XCode8からProvisioning名で指定できる string codesignName = "iPhone Distribution: XXXXXx"; // 色々設定更新(実際はJenkinsのオプション引数で各種情報を取得) proj.SetBuildProperty(target, "DEVELOPMENT_TEAM", teamPrefix); proj.SetBuildProperty(target, "CODE_SIGN_IDENTITY", codesignName); proj.SetBuildProperty(target, "PROVISIONING_PROFILE_SPECIFIER", provisioningProfileName); proj.AddFrameworkToProject (target, "UserNotifications.framework", false); proj.WriteToFile(projPath); } } |
CODE_SIGN_IDENTITYはXcodeのCode Signing Identityに当たる設定値を記載します。 下記例ではadhoc、release版を対象としているためiPhone Distribution:がPrefixとなる文字列を指定しています。
これを設定しない場合、プロビジョニングファイルと紐づいていない証明書が選ばれることがありました。次のようなエラーです。
❌ Provisioning profile “hogehoge_fujii” doesn’t include signing certificate “iPhone Developer: Taro Tanaka (XXXXXXXX)”.
DEVELOPMENT_TEAM にはApple Developer CenterのMemberShipで確認できる Team IDを使用します。 このサイトにログインした後のURLにもこのTeam IDが使われているのでそちらを利用することもできます。
MyBuildPostprocessor.cs での設定は、このDEVELOPMENT_TEAMの設定を削った状態でもビルドは完了しました。アーカイブ時には先の.gitlab-ci.ymlで記載したコマンドのパラメータから不要なのかもしれません。
PROVISIONING_PROFILE_SPECIFIER はXcode8系から登場した設定値です。UUIDを設定するのでなく、プロファイル名を指定するようになりました。
Certificates, Identifiers & Profiles ではprovisioning Fileの一覧に表示されているNameを使います。
これを設定しない場合、プロビジョニングファイルがない、というエラーが表示されました。
❌ [BCEROR]Unity-iPhone requires a provisioning profile. Select a provisioning profile for the “ReleaseForRunning” build configuration in the project editor.
さいごに
全体を通して、runnerを動かしているMac を横に動作を監視してみて、GUIを使った場合のビルドに比べ、batchmodeでの実行なのでとても静かに実行されています。gitにコミットを行なっている作業マシンでの負担も減りました。
ipaだけでなくarchiveファイルの取得も行なっているのは、adhoc、申請とプロビジョニングファイルの差し替えが可能なように アップローダツールでなく、Xcode経由でアップロードする場合を想定しています。 全部をCIに置き換える作業は、毎年のAppleの仕様変更や、新しいgoogle Playのアプリ仕様対応なども含めると実際難しいところも多く、 まずは人の手が必要な部分での知見をためて、自動化可能なところを切り出すという、かなり地道な作業を元に行なっています。 いろんな企業のCI構成を取り入れられるように、様々な会社のCI活動をもっと見ていきたいなと。次はPG作業以外のCI化を書く予定です。
参考
UnityのiOSアプリをアーカイブまでJenkinsで自動化した話 OS X El Capitan: キーチェーンをロックする/ロック解除する UnityでXcodeの設定を自動化する方法まとめ https://docs.unity3d.com/ScriptReference/Callbacks.PostProcessBuildAttribute.html https://docs.unity3d.com/ScriptReference/iOS.Xcode.PBXProject.html https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildPlayer.html