gitのコミットハッシュをアプリに埋め込む

突然ですが、みなさんアプリケーションのバージョン管理はどうしていますか?git などを使っていると、version 1.0.0.1 とか version 1.0.1.1 とか version 2.0.0.0 とか管理しているのは意味ないと思いませんか。

アプリケーションはソースコードから生成されるわけですから、どのソースコードから生成されたかというのが重要な情報であって、バージョンの数値なんてどうでもいいことだと思いませんか?( セールス的にバージョン2.0新発売!などのキャッチフレーズは必要かと思いますけれど... )

はりきってバージョンアップしても、実はバグがあって元に戻さなければならない場合もありますし。

さて、前置きが長くなりすぎました。git のコミットハッシュをアプリケーションに埋め込んだらアプリのバージョン特定がラクになります。リソース文字列ファイルの仕組みをつかってこれを実現しましょう。

まず git でソースコードを管理しましょう。最低一度はコミットしてハッシュやブランチを参照できるようにしておきましょう。

それでは、大まかな流れとしては下記になります。

step0: プロジェクトにリソース文字列ファイル MyResourceStr.strings を登録する。
step1: ビルド寸前に git のコマンドを走らせて、その結果を MyResourceStr.strings に書き込む。
step2: アプリケーションはリソース文字列をとりこんで Text などに表示する。

という感じです。git のコマンドを走らせるのは MacOS の zsh のシェルスクリプトで行います。

シェルスクリプトですから、文字列の変形やテキストファイルへの上書きなど、そういったシェル操作は楽勝です。

step0: プロジェクトにリソース文字列ファイルを登録する

プロジェクトビューにて、File New... を選んで Resource Strings ファイルを追加します。本記事ではファイル名を MyResourceStr.strings とします。ファイルの追加手順がわかりにくい場合は下記の記事を参考にしてください。

リソースとして埋め込まれる文字列を扱う

アプリの多言語対応などをするには、リソース文字列を扱えると便利です。リソース文字列を扱う方法を紹介します。

プロジェクトビューに、リソース文字列ファイル MyResourceStr.strings というファイルが aaa プロジェクトに従属して管理されていることに注目してください。

プロジェクトビューにおいて aaa のフォルダの中に MyResourceStr.strings ファイルがなければドラッグして位置を移動してください。

step1: ビルド寸前に git のコマンドを zsh シェルコマンドで実行する

プロジェクトにビルド寸前で zsh シェルコマンドを実行させる設定をしなければなりません。それでは順を追って説明します。

下記の図のようにウインドウ上部のプロジェクト名のアイコンをクリックして Edit Scheme... を選びます。( 本記事では aaa )

選択したら下記のようなダイアログが表示されますので、そこで Build ... Pre-actions を選びます。何も表示されていないですが、画面の下部に、プラスとマイナスの記号がありますので、それのプラスをクリックします。

プラスの記号をクリックしたら New Run Script Action を選びます。

下記のような画面が出現します。Shell や provide build settings from のような記述や、シェルスクリプトを書き込む場所があります。

Shell のところには #!/bin/zsh と入力します。要はシェルスクリプトを実行させるものを指定すればいいです。PowerShell などでもいいですし、独自のアプリのようなものでもかまいません。

Provide build settings from は、アプリケーションのプロジェクトとします、ここでは aaa です。

下記のシェルスクリプトを貼り付けおわったら右下の "Close" というボタンを押してダイアログを閉じます。

シェルスクリプトについて少々解説します。

4行目、リソース文字列ファイルの名前は間違えないようにしてください。
7,8行目は、XCode が提供してくれるマクロ文字列です。それぞれプロジェクトが配置されているディレクトリのフルパスと、プロジェクト名が取得できます。
17〜20行目、シェルで実行する命令の準備をします。(この時点で実行はしていません)
29、33〜36行目、シェルコマンドを実行しつつファイルに結果をリダイレクトします。
29行目はファイルを新規作成しています。33〜36行目は新規作成されたファイルに内容を追加していることに注目してください。

echo "shell execute start."

# リソース文字列ファイルに合わせてファイル名を自分で決定する.
filename_resouce_str="MyResourceStr.strings"

# プロジェクトのあるディレクトリと、プロジェクト名称をシステムから取得する.
echo $PROJECT_DIR
echo $PROJECT

# ギットリポジトリのあるディレクトリにカレントディレクトリを移動する.
cd $PROJECT_DIR

#出力するリソース文字列ファイルのパス.
filepath_out="${PROJECT_DIR}/${PROJECT}/${filename_resouce_str}"

# MacOS の標準ではミリ秒が取得できない.
myvar0=`date "+%Y_%m%d_%H%M%S"`
myvar1=`git rev-parse --abbrev-ref HEAD`
myvar2=`git show --format="%H" --no-patch`
myvar3=`git show --format="%h" --no-patch`

# いちおう変数の中身をコンソールに出力する.
echo $myvar0
echo $myvar1
echo $myvar2
echo $myvar3

# ひとつめだけファイル新規作成.
printf '// %s\n' $myvar0 > $filepath_out

# コメントにした文字列をメンバにする.ここからはファイル内容追加.
# myvar0 はビルドに失敗したらファイルには以前の値が残るので運用には注意せよ.
printf '"build_date" = "%s";\n' $myvar0 >> $filepath_out
printf '"git_branch" = "%s";\n' $myvar1 >> $filepath_out
printf '"git_hash" = "%s";\n' $myvar2 >> $filepath_out
printf '"git_hash_n7" = "%s";\n' $myvar3 >> $filepath_out

echo "shell execute finish."

step2: リソース文字列をとりこんで Text などに表示する

step1: を経て XCode はビルドの命令を受けると、Pre-actions を実施してからビルドを実行するように設定されました。

ということはビルドの寸前には MyResourceString.strings に適切な git の情報を示す文字列が入っているはずです。それを Text に表示するだけです。

Swiftのソースコードからリソース文字列を参照する場合はファイル名の拡張子が不要なことに注意してください。

import SwiftUI

struct ContentView: View {

    // ここには拡張子を指定する必要がない.
    let FILE_TITLE_RES_STR = "MyResourceStr"

    var body: some View {
        VStack {

            Text("build_date" ,tableName: FILE_TITLE_RES_STR )
                .font( .title )

            Text("git_branch" ,tableName: FILE_TITLE_RES_STR )
                .font( .title )

            Text("git_hash_n7" ,tableName: FILE_TITLE_RES_STR )
                .font( .title )

        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

実行するとこのようになります。

上段の表示がビルド寸前に zsh シェルスクリプトを実行した年月日時分秒です。
中段の表示が git のブランチ名です。
下段の表示が git のコミットハッシュの最初の7文字です。