白黒羊

Unityプロジェクトで使えるちょっと便利なGit aliasたち

この記事は 2021年Unityアドベントカレンダー の2日目の記事です。
昨日は @RyotaMurohoshi さんの Unity 2021におけるパッケージ関連の変更について でした。
こんなに序盤に参加させていただきながらUnityそのものの話ではなくて恐縮ですが、Unityプロジェクトでよく使うGit alias (エイリアス)をまとめてみました。

環境

Mac OS Big Sur 11.6
Unity Editor 2021.2.2f1
git version 2.33.1

自分の環境がMacなのでMac用のコマンドは全部確認済みです。Windowsのものも付記してありますが一部未確認なので間違っていたらごめんなさい。

前提

(必須)Git管理しているUnityプロジェクトがある
(任意)npmが使える環境

Git aliasとは

おなじみ git addgit pull のようなコマンドと同じように自分で git XXXXX と設定できる便利機能です。
詳しくはググっていただきたいのですが、以下のようなコマンドで「st」という名前のalias(エイリアス)を作ることができました。git st と入力するだけで git status が実行されるようになります。

git config --global alias.st status

上記で --global を指定しているように、作業リポジトリに限らず全体で使いたい場合は、globalオプションを付けるか、以下のように ~/.gitconfig に直接書き込むことができます。

[alias]
    st = status
    br = branch
    graph = log --graph --date=format-local:'%Y/%m/%d %H:%M:%S' --decorate=short --pretty=format:'%Cgreen%h %Creset%cd %Cblue%cn %Cred%d %Creset%s'
    gr = graph
    last = show --stat

上記はUnityとはまったく関係ありませんが、よく使っているaliasたちです。作業効率が非常に上がるのでかなりおすすめです。
もちろん git alias コマンドを使うことも可能ですが、引用符(’)(”)が出てくるとエスケープをしないといけなかったりして書くのが大変になってくるので今後は今のようにすべてファイルに直接書いていく形式で紹介します。

このようによく使う機能を省略したり、毎回使うオプションを全部つけておいた状態でaliasにすることで作業時間を少しでも短縮しようという試みです。

.gitignore自動生成

プロジェクト作成時最序盤に行う.gitignoreをコマンドひとつで自動的にできるようにしてみましょう。~/.gitconfig に書き込んでglobalに設定してみます。

Mac/Windows
[alias]
    ignore = "!f() { curl -LsS https://www.gitignore.io/api/$@ ;}; f"
    ignore-unity = !"git ignore unity,rider > .gitignore"
    # ignore-unity = !"git ignore unity,visualstudio > .gitignore"

Unity+Riderの例を書いてみましたが、VisualStudioを使っている人は3行目のものを、他のIDEとかツールを使っている人は gitignore.io を参照して最強の.gitignoreを作りましょう。

エラー表示などを良い感じにしたい場合は下記の記事がおすすめです。

_gitignoreをコマンド一つで自動生成する – Qiita

Packages/manifest.json をソート

UPM (Unity Package Manager)を利用してパッケージをインストールしたりアップデートすると、そのたびにmanifest.jsonの順番がかわってしまいます。
複数ブランチでそれぞれで別のパッケージをアップデートしてしまったりするとかなりの確率でマージコンフリクトが生じるのですが毎回手動でソートするのは面倒なので、aliasを使って簡単にソートしましょう。

事前準備として sort-package-json が必要です。 npmでインストールしておきます。

https://github.com/keithamus/sort-package-json

npm install --global sort-package-json

インストールできたら、このaliasを追加したいプロジェクトのディレクトリにある.git/config ファイルを開いて以下を書き込みます。

Mac
[alias]
    sort = !zsh -c 'sort-package-json "Packages/manifest.json"'
Windows
[alias]
    sort = !bash -c 'sort-package-json "Packages/manifest.json"'

以下のように組み合わせれば git aa で一括でステージングするときにソートもしてくれて便利です。

Mac
[alias]
    aa = !zsh -c 'git sort && git add -A'
    sort = !zsh -c 'sort-package-json "Packages/manifest.json"'
Windows
[alias]
    aa = !bash -c 'git sort && git add -A'
    sort = !bash -c 'sort-package-json "Packages/manifest.json"'

Unityのunit testをコマンドで実行する

もちろんエディタ内でもunit testの実行は簡単にできますが、push前などに気軽に結果を確認するためにaliasを作ることにしました。
UnityHub経由でUnityをインストールしている人向けなので、そうでない場合は一部書き換える必要があります。

シンプルなやつ

PlayModeとEditMode、Scripting Backend(0:Mono2x/1:IL2CPP)だけを指定するエイリアスです。 git test PlayMode 0 のように使います。
プロジェクトのバージョンと一致するUnityEditorがローカルになかったり、UnityEditorが開かれているままだと失敗します。

Mac

[alias]
    test = "!f() {  if [ $# != 2 ]; then echo '[error] specify the PlayMode/EditMode and Scripting Backend 0/1:' $*; exit 1; fi;\
                    project_path="/path/to/your/PROJECT";\
                    version=`cat ProjectSettings/ProjectVersion.txt | head -n 1 | tr -d 'm_EditorVersion: '`; mode=$1; backend=$2;\
                    if [ $backend = 0 ]; then backendname=\"Mono\"; else backendname=\"Il2cpp\"; fi;\
                    /Applications/Unity/Hub/Editor/${version}/Unity.app/Contents/MacOS/Unity\
                    -runTests -projectPath ${project_path} -batchmode\
                    -testPlatform $mode -scriptingBackend $backend;\
                    result=$?;\
                    if [ $result != 0 ]; then printf \"\\e[1;31m[${mode} Test (${backendname})]\\e[m test failed\n\";\
                    else printf \"[${mode} Test (${backendname})] All\\e[1;32m Green\\e[m\n\"; fi; return $result; }; f"

Windows

[alias]
    test = "!f() {  if [ $# != 2 ]; then echo '[error] specify the PlayMode/EditMode and Scripting Backend 0/1:' $*; exit 1; fi;\
                    project_path="\\path\\to\\your\\PROJECT";\
                    mode=$1; backend=$2;\
                    if [ $backend = 0 ]; then backendname=\"Mono\"; else backendname=\"Il2cpp\"; fi;\
                    \"C:\\Program Files\\Unity\\Hub\\Editor\\Unity.exe\"\
                    -runTests -projectPath ${project_path} -batchmode\
                    -testPlatform $mode -scriptingBackend $backend;\
                    result=$?;\
                    if [ $result != 0 ]; then printf \"\\e[1;31m[${mode} Test (${backendname})]\\e[m test failed\n\";\
                    else printf \"[${mode} Test (${backendname})] All\\e[1;32m Green\\e[m\n\"; fi; return $result; }; f"

UnityEditorのパスにバージョンが含まれる場合は、Mac用のものと同様にバージョンをProjectVersion.txtから取得して反映する必要があります。

[alias]
    test = "!f() {  if [ $# != 2 ]; then echo '[error] specify the PlayMode/EditMode and Scripting Backend 0/1:' $*; exit 1; fi;\
                    project_path="\\path\\to\\your\\PROJECT";\
                    version=`cat ProjectSettings/ProjectVersion.txt | head -n 1 | tr -d 'm_EditorVersion: '`; mode=$1; backend=$2;\
                    if [ $backend = 0 ]; then backendname=\"Mono\"; else backendname=\"Il2cpp\"; fi;\
                    \"C:\\Program Files\\Unity\\Hub\\Editor\\\\${version}\\Editor\\Unity.exe\"\
                    -runTests -projectPath ${project_path} -batchmode\
                    -testPlatform $mode -scriptingBackend $backend;\
                    result=$?;\
                    if [ $result != 0 ]; then printf \"\\e[1;31m[${mode} Test (${backendname})]\\e[m test failed\n\";\
                    else printf \"[${mode} Test (${backendname})] All\\e[1;32m Green\\e[m\n\"; fi; return $result; }; f"

カテゴリとかカバレッジレポートとか使うやつ

TestCategoryのテストだけを走らせ、ControllerDomainGatewayUtilityView asmdef下のテスト結果だけをカバレッジレポートに含め、ログファイル、HTMLレポート、カバレッジレポートは/path/to/your/PROJECT/Documents/Tests/以下のそれぞれ logsresultscoverageフォルダに書き出すことにするときのバージョンです。
読みにくいので別のエイリアスに分けた方が良さそうですが全部まとめるとこうなります。

Mac

[alias]
    test = "!f() {  if [ $# != 2 ]; then echo '[error] specify the PlayMode/EditMode and Scripting Backend 0/1:' $*; exit 1; fi;\
                    project_path="/path/to/your/PROJECT";\
                    category="TestCategory";\
                    coverage_target="+Controller,+Domain,+Gateway,+Utility,+View"\
                    version=`cat ProjectSettings/ProjectVersion.txt | head -n 1 | tr -d 'm_EditorVersion: '`; mode=$1; backend=$2;\
                    if [ $backend = 0 ]; then backendname=\"Mono\"; else backendname=\"Il2cpp\"; fi;\
                    /Applications/Unity/Hub/Editor/${version}/Unity.app/Contents/MacOS/Unity\
                    -runTests -projectPath ${project_path} -batchmode\
                    -testCategory \"${category}\"\
                    -testResults \"Documents/Tests/results/${mode}${backendname}.xml\"\
                    -logFile \"Documents/Tests/logs/${mode}${backendname}.log\"\
                    -testPlatform $mode -scriptingBackend $backend\
                    -enableCodeCoverage\
                    -coverageResultsPath \"Documents/Tests/coverage/\"\
                    -coverageHistoryPath \"Documents/Tests/coverage/\"\
                    -coverageOptions \"generateAdditionalMetrics;generateHtmlReport;generateHtmlReportHistory;generateBadgeReport;assemblyFilters:${coverage_target};verbosity:verbose\";\
                    result=$?;\
                    if [ $result != 0 ]; then printf \"\\e[1;31m[${mode} Test (${backendname})]\\e[m test failed\nyou can check Documents/Tests/logs/${mode}${backendname}.log\n\";\
                    else printf \"[${mode} Test (${backendname})] All\\e[1;32m Green\\e[m\n\"; fi; return $result; }; f"

Windows

[alias]
    test = "!f() {  if [ $# != 2 ]; then echo '[error] specify the PlayMode/EditMode and Scripting Backend 0/1:' $*; exit 1; fi;\
                    project_path="\\path\\to\\your\\PROJECT";\
                    category="TestCategory";\
                    coverage_target="+Controller,+Domain,+Gateway,+Utility,+View"\
                    mode=$1; backend=$2;\
                    if [ $backend = 0 ]; then backendname=\"Mono\"; else backendname=\"Il2cpp\"; fi;\
                    \"C:\\Program Files\\Unity\\Hub\\Editor\\Unity.exe\"\
                    -runTests -projectPath ${project_path} -batchmode\
                    -testCategory \"${category}\"\
                    -testResults \"Documents/Tests/results/${mode}${backendname}.xml\"\
                    -logFile \"Documents/Tests/logs/${mode}${backendname}.log\"\
                    -testPlatform $mode -scriptingBackend $backend\
                    -enableCodeCoverage\
                    -coverageResultsPath \"Documents/Tests/coverage/\"\
                    -coverageHistoryPath \"Documents/Tests/coverage/\"\
                    -coverageOptions \"generateAdditionalMetrics;generateHtmlReport;generateHtmlReportHistory;generateBadgeReport;assemblyFilters:${coverage_target};verbosity:verbose\";\
                    result=$?;\
                    if [ $result != 0 ]; then printf \"\\e[1;31m[${mode} Test (${backendname})]\\e[m test failed\nyou can check Documents/Tests/logs/${mode}${backendname}.log\n\";\
                    else printf \"[${mode} Test (${backendname})] All\\e[1;32m Green\\e[m\n\"; fi; return $result; }; f"

e[1;31m[ とかの謎の文字列は色をつけるためのものです。テストが通ったときは緑、通らなかったときは赤にしてみました。おしゃれ。

https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc87b2124

Unity Cloud Build と連携してPRの準備をする

Unity Cloud Buildで、testという名前のブランチにpushされたらビルドするという設定をしておきます。

先ほど作った test alias を使って il2cpp と mono それぞれで Play/Edit mode のテストをします。失敗したらそこで抜けます。
git restore ProjectSettings/ProjectSettings.asset; は、PlayMode のテストによって、 runInBackground: 01 になってしまうのを元に戻しています。元々 1 で使っている人は必要ありません。

その後 local の test ブランチを削除して remote の masterとマージしてforce push します。それがうまくできれば、元のブランチの名前で remoteにpushします。testブランチの自動ビルドがうまくいけばPRをレビューしてマージできます。

毎回 testブランチを強制削除したあとに force push という少々乱暴な方法なので、もう少し良い方法を探しています。

Mac

[alias]
    prep = "!f() {  git test PlayMode 0; if [ $? != 0 ]; then exit 1; fi;\
                    git test EditMode 0; if [ $? != 0 ]; then exit 1; fi;\
                    git test PlayMode 1; if [ $? != 0 ]; then exit 1; fi;\
                    git test EditMode 1; if [ $? != 0 ]; then exit 1; fi;\
                    current=`git symbolic-ref --short HEAD`;\
                    git branch -D test;\
                    git restore ProjectSettings/ProjectSettings.asset;\
                    git clean -f Assets/InitTestScene*;\
                    git ch test &&\
                    git merge $current -m \":twisted_rightwards_arrows: ${current}\" &&\
                    git push -f origin test &&\
                    git checkout $current &&\
                    git push $1; }; f"

Windows

[alias]
    prep = "!f() {  git test PlayMode 0; if [ $? != 0 ]; then exit 1; fi;\
                    git test EditMode 0; if [ $? != 0 ]; then exit 1; fi;\
                    git test PlayMode 1; if [ $? != 0 ]; then exit 1; fi;\
                    git test EditMode 1; if [ $? != 0 ]; then exit 1; fi;\
                    current=`git symbolic-ref --short HEAD`;\
                    git branch -D test;\
                    git restore ProjectSettings/ProjectSettings.asset;\
                    git clean -f Assets/InitTestScene*;\
                    git ch test &&\
                    git merge $current -m \":twisted_rightwards_arrows: ${current}\" &&\
                    git push -f origin test &&\
                    git checkout $current &&\
                    git push $1; }; f"

あといくつかよく使うエイリアスがあるのですが、Unityには関係なさすぎるのでまた別の記事にまとめます。

良いGit aliasライフを!