ビルドマシンから参照するgit-lfsレポジトリをキャッシュする

## TL;DR

ローカルにベアレポジトリを作成して、それをcloneした後に`lfs.url`を設定すれば帯域使わないので便利！

# 概要

複数台のビルドマシンで何かをビルドする場合、それぞれのワークスペースへcloneするのが一般的だと思います。
その際に複数のワークスペースが同じレポジトリを参照していることも良くあることだと思います。
そのような場合、同じデータを何度も取得することになってしまい通信帯域がもったいないといったことも多いのではないでしょうか。
特に`git lfs`を使用している場合などは転送料金に悩まされることも多いはず。
そこで今回は`git lfs`を使う際に転送料金を~~ケチる~~削減する方法を試してみたいと思います。

# OSS(別解)

私は使ったことがないのですが、[lfscache](https://github.com/saracen/lfscache)といったOSSを導入することも一つの手です。
恐らくは変なことをするよりも躓くことが少なくなるのではないかと思います。

ただ、今回は別の(原始的な)アプローチをしようと思います。

# 今回のアプローチについて

今回はベアレポジトリを使用する方法を使ってみます。
単なるベアレポジトリに関しては詳しい記事が多く出てきますので詳しいことは省きます。
ビルドマシンから参照する方法についても各システムでアプローチが異なるため省きます。

ではこの記事でどういったことを解説するかというと、ベアレポジトリに対して`git lfs`を使用した際の設定方法などです。

## 概要

実は私が手元で`GIT_TRANSFER_TRACE=1`を付けて試す限りでは、ローカルレポジトリのcloneなら何も設定しなくてもローカルレポジトリを参照しにいっているようでした。つまりは特に何かする必要はなさそうです。
ただ、勝手に調整されるのが個人的に~~気に入らない~~不安なので明示的に指定する方法を記載してみます。

git-lfsには挙動を制御するconfigがあります。
この中に`lfs.url`というものがあり、今回はこれを使用します。
この時に`file://`を使えるということがポイントです。

マニュアル:
https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.adoc
https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-standalone-file.adoc

> **Note**
> 作業中は `export GIT_TRANSFER_TRACE=1` しまってもいいと思いますが、この記事中は必要な部分を明示する意味も兼ねてそうしない状態で解説します。

## 設定方法

`.lfsconfig` で設定する方法といつもながらの `.gitconfig` や `.git/config` で設定する方法があります。

[マニュアル](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.adoc#configuration-files) の記載を見ると今回の用途では `.git/config` で設定するのが良さそうです。

### 1. 参照元となるレポジトリをcloneする

こちらは通常通りのcloneで良いと思いますが、前述した通り今回はベアレポジトリにします。
ベアレポジトリの場合、`git lfs fetch`は自動で行われないようなので忘れずに手動で実行しましょう。

```
git clone --bare https://url/to/repository.git
git -C path/to/repository lfs fetch
```

> **Note**
> この時に後述する`GIT_TRANSFER_TRACE=1`を設定しておけば`git lfs`がどういう通信を行っているのかを確認することができます。

> **Note**
> この時は流石にLFS帯域を使用しますのでご了承ください。

### 2. 参照先となるレポジトリをcloneする

1で作成したローカルレポジトリから更にレポジトリをcloneします。
この時に`git lfs` が動いてしまわないように `GIT_LFS_SKIP_SMUDGE=1` を指定しておきます。

`GIT_LFS_SKIP_SMUDGE=1 git clone path/to/repository.git`

> **Note**
> `GIT_TRANSFER_TRACE=1 git clone path/to/repository.git` してみたところ正常にcloneできました。
> 前述の通り通常は何もしなくても自動で調整してくれるようです。

この時にLFS対象のファイルをcatすると以下のような出力がされると思います。
されなければ `GIT_LFS_SKIP_SMUDGE=1` が効いていないのでご注意ください。(帯域を消費しています)

```
$ cat path/to/file
version https://path.to.lfs/spec/v1
oid sha256:hash
size ????
```

### 3. 参照元となるレポジトリをLFSの参照先とする

2でcloneだけしたレポジトリのconfigに`lfs.url`を追加します。
vimで編集する場合は以下のような感じです。(emacsなりnanoなりcodeなりとお好きなエディタをご利用ください)

`vim path/to/repository/.git/config`

```
[lfs]
	repositoryformatversion = 0
    # ↓を追加。参照元となるレポジトリへのパスを設定
    url = file://path/to/repository.git/
```

### 4. git lfs fetchを実行する

これまでで設定ができましたので `git lfs fetch` してみましょう。
この時に `GIT_TRANSFER_TRACE=1` とすることで転送の様子がログに出てくるので確認するとよいでしょう。

`GIT_TRANSFER_TRACE=1 git lfs fetch`

以下のようなログが出ていればローカルファイルが参照されていると思われます。

```
...(前略)...
HH:MM:SS.000000 trace git-lfs: fetch path/to/file [hash]
...(中略)...
HH:MM:SS.000000 trace git-lfs: xfer: Custom adapter worker 0 received response: {"event":"complete","oid":"hash","path":"D:\\path\\to\\repository\\.git\\lfs\\tmp\\lfs-standalone-file-1883205854\\download3252211190"}
...(後略)...
```

ちなみにgithub.comへアクセスした場合は以下のようなログが出ます。
HTTPという文字が出てしまうと帯域を使用しているという判断で良いと思います。

```
...(前略)...
HH:MM:SS.000000 trace git-lfs: fetch path/to/file [hash]
...(中略)...
HH:MM:SS.000000 trace git-lfs: HTTP: POST https://github.com/path/to/repository.git/info/lfs/objects/batch
...(中略)...
HH:MM:SS.000000 trace git-lfs: HTTP: {"objects":[{"oid":"hash","size":1193,"actions":{"download":{"href":"https://github-cloud.githubusercontent.com/alambic/media/161987690/94/c3/hash?{何らかのパラメータ}
HH:MM:SS.000000 trace git-lfs: HTTP: {何らかのパラメータ}","expires_at":"YYYY-MM-DDTHH:MM:SSZ","expires_in":3600}}}]}
...(後略)...
```

### 5. 完成

後はCIなど各々が必要な箇所で使用してください。