gitlab の private repository を go get する

## TL;DR :pencil:

1.  `read_api`と`read_repository`権限のついた、gitlab [Personal Access Token](https://gitlab.com/profile/personal_access_tokens)を作る
1.  `~/.netrc`を作成

    ```bash:~/.netrc
    machine gitlab.com
      login yourname@gitlab.com
      password yourpersonalaccesstoken
    ```
1.  `go get`する

    ```bash
    GO111MODULE=on GOPRIVATE=gitlab.com/group go get gitlab.com/group/subgroup/hoge.git@v1.0.0
    ```
1. container image や CI 内でやりたい場合ば、`.netrc`を`go get`する環境に渡してあげる


## Private repository の `go get` :surfer: 

go module が導入され、モジュールの依存関係を go.mod で管理することもメジャーになってきました。そんな中、自チームで作成した private な git repository を `go get` して、他のアプリでも使いたいという場面もあるかと思います。Github で管理された priavte repository でこれを実現する方法は、ググればたくさん出てくるのですが、GitLab でやる方法はまだまだマイナーです。

そこで、gitlab.com で管理している go の private git repository を `go get` する方法をご紹介します！

### Local環境に `go get` する

まず、 private repository を手元のローカル環境に `go get` することを考えます。が、その前に目的のモジュールがgitlabのsubgroup配下にあるかどうか確認してください。

対象モジュール(hoge)が、例えば`gitlab.com/group/subgroup/hoge`のように、subgroupに属している場合、`go get`対象となるパッケージ名は`gitlab.com/group/subgroup/hoge.git`になります。

```bash
go get gitlab.com/group/subgroup/hoge.git
```

これを`.git`無しで実行すると、`gitlab.com/group/subgroup.git`というリポジトリが見つからないというエラーが発生します。
githubだとgroupまたはユーザの階層の直下がリポジトリ階層になるので、`subgroup`の部分がリポジトリ扱いされるのではないかと推測しています。明示的に`.git`とすると、そこをリポジトリとして認識してくれます。

次に、gitlab private repositoryを`go get`する方法ですが、[公式にissue](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/65681)が切られているので、まずはこれをチェック。
注目すべきは最後のコメントで、

1.  `read_api`と`read_repository`権限のついた、gitlab [Personal Access Token](https://gitlab.com/profile/personal_access_tokens)を作る
2.  `~/.netrc`を下記の内容で作る
    
    ```bash
    machine gitlab.com
      login yourname@gitlab.com
      password yourpersonalaccesstoken
    ``` 

3.  `go get --insecure` を使って対象のモジュールをダウンロードする
4.  `.gitconfig`の`insteadOf`は使わない

とのこと。

なおGo 1.13以降の場合、[`GOPRIVATE`という変数が追加](https://golang.org/cmd/go/#hdr-Module_configuration_for_non_public_modules)されており、3番目の`--insecure`オプションではなく、こちらを用いた方が良さそうです。

ここまでで、privateなgo git repositoryをlocalに`go get`できたことかと思います。`go get`したモジュールは`$GOPATH/pkg/mod/gitlab.com/group/subgroup`配下に指定したバージョンごとに`hoge.git@v1.0.0`のようなディレクトリ名で格納されているはずです。

### Container image 内で `go get` する

続いて、Container image内で`go get`する場合を考えます。この場合、実際に実行するコマンドは`go get`ではなく、`go build`や`go test`かもしれませんが、やりたいことはモジュールのダウンロードなので`go get`と同じです。

goで開発している場合、[マルチステージビルド](https://docs.docker.com/develop/develop-images/multistage-build/)をすることが一般的かと思います。そうなると、マルチステージビルドの、ビルド環境側で下記のように`.netrc`を設定してあげる必要があります。

```dockerfile:Dockerfile
FROM golang:1.14 AS builder

ENV GO111MODULE=on
ENV GOPRIVATE=gitlab.com/group

ADD . /go/src/gitlab.com/group/subgroup/fuga
WORKDIR /go/src/gitlab.com/group/subgroup/fuga

ARG NETRC
RUN echo "${NETRC}" > /root/.netrc
RUN go build -o /bin/fuga .


## Product
FROM alpine:3.12

COPY --from=builder /bin/fuga /bin/
CMD [ "/bin/fuga" ]
```

`.netrc`ファイルをボリュームマウントや`ADD`、`COPY`などして渡してあげることも可能ですが、今回は`ARG`で`NETRC`変数にファイルの中身を渡しています。従って、`docker build`コマンドは下記のようになります。

```bash
docker build -t fuga --build-arg NETRC="$(cat ~/.netrc)" .
```

### GitLab CI環境で `go get` する

では、CIでimageをビルドする際にはどのように`.netrc`を渡せばいいでしょうか。個人のアクセストークンを使用したくはありません。

こちらも、[gitlabの公式に答えが用意されていました](https://docs.gitlab.com/ee/user/project/new_ci_build_permissions_model.html#dependent-repositories)。gitlabにはCI内で使用できるトークンが用意されており、下記の形で`.netrc`も設定できます。

```bash
echo -e "machine gitlab.com\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc
```

これを応用してあげれば、CI内で`go get`したい場合には上記のトークンでモジュールをダウンロードできます。また、docker-in-dockerなど、CIのなかで`docker build`したい場合や、`/kaniko/executor`したい場合には、下記のようにして`--build-arg`に渡すことができます。

```bash
--build-arg NETRC="$(echo -e "machine gitlab.com\n  login gitlab-ci-token\n  password ${CI_JOB_TOKEN}")"
```

## まとめ :ramen: 

gitlabの、subgroupを持つgoのprivate repositoryで、publicなモジュール同様に`go get`する方法をご紹介しました。あくまで、`2020/09/01`時点（go 1.14）で調べたものですので、今後また変わってくると思います。なかなか面倒くさいですもんね :rolling_eyes: