Go: DepからGo Modulesへの移行

## はじめに
`Go modules`はGo1.13（August 2019）から正式に導入されたGo言語公式の依存パッケージ管理ツールです。`Go modules`以前は、`dep`が依存パッケージ管理のツールとしてはデファクトでした。`Go modules`はGo1.11から使用は可能でしたが、環境変数(ENV GO111MODULE=on)を設定したり色々と手間がかかる事前準備が必要でした。Go1.13からはデフォルトで`Go modules`が組み込まれています。

## そもそもGo Modulesとは何？
Goの新しい依存管理システムである。
モジュールは、一つのユニットとしてバージョン管理されている関連Goパッケージの集まり。1.11からはmoduleモードとGOPATHモードが使えるようになっていましたが、1.13からはmoduleモードがデフォルトでONになっています。Goのオープンソースモジュールのエコシステムをより良く活用できるように、moduleモードがデフォルトで動作するようにしているそうです。
[Golang.org: modules](https://blog.golang.org/modules2019)
[Golang.org: Wiki-Modules](https://github.com/golang/go/wiki/Modules)

## モジュールとは
モジュール対応モードでは，標準ライブラリを除くパッケージを「モジュール（module）」として管理する。 パッケージが git 等のバージョン管理ツールで管理されている場合はバージョン毎に異なるモジュールと見なされる。 つまりモジュールの実体は「パッケージ＋バージョン」ということになる。
ただしコード上ではパッケージとモジュールの間に区別はなく，したがってソースコードを書き換える必要はない。 モジュールはソースコードではなく`go.mod`ファイルで管理される。

## depとGo Modulesの機能差マトリクス
|項目             |dep                            |go modules                   ||
|----------------|-------------------------------|-----------------------------|------|
|実行する場所      |`GOPATH`以下である必要がある      |\*Goプログラムのルートディレクトリ<br>\*GOPATH以下にあるソースコードでも`go.mod`ファイルがあればモジュール対応モードで管理が可能            |    |
|管理ファイル      |`Gopkg.toml`・`Gopkg.lock`     |`go.mod`・`go.sum`            |    |
|Vendoring       |depがvendoring対応のツール       |`go mod vendor`というコマンドでvendoring対応ができる|dockerの場合はvendoring利用がおすすめ    |
|Gitでの管理      |`Gopkg.toml`と`Gopkg.lock`をGit管理する|通常は、`go.mod`と`go.sum`は共にGit管理する| |
|パッケージ管理    |リポジトリの最新リビジョンのみが対象 |リポジトリのバージョンタグまたはリビジョン毎に管理。Semantic Versioningに対応|Semantic Versioningとは、vX.X.Xというようなバージョン番号の定義方法   |
|依存packageの場所|$GOPATH/src以下プログラムルートのvendorディレクトリ|$GOPATH/pkg/mod以下| ＊go modで取得したバイナリなどは$GOPATH/pkg/mod/以下にキャッシュされている<br>＊CIの高速化でビルドキャッシュをするときはこのディレクトリをキャッシュする必要がある<br>＊キャッシュを削除するときはgo clean -cacheコマンドで削除<br>＊go mod vendorコマンドでdepのようにvendorディレクトリに依存関係を保存することができる<br>＊go mod tidyでgo.modから不要な依存関係を削除|

パッケージ管理で、GOPATHの依存がなくなる点くらいで双方の機能差はほぼ無し。
「$GOPATHの呪縛からの解放」という最大のメリットで、何よりも代えがたいポイントのように思えます。

## GOPATHモード(GOPATH mode)とモジュールモード(module-aware mode)
バージョン1.11以降からは、Go言語コンパイラは以下の2つのモードのどちらかで動作するようになっていました。

| モード|       | 
| :--- | :--- | 
| GOPATHモード |バージョン 1.10 までの動作モード。標準ライブラリを除く全てのパッケージの管理とビルドを $GOPATH 以下のディレクトリで行う。パッケージの管理はリポジトリの最新リビジョンのみが対象となる|
| モジュール対応モード|標準ライブラリを除く全てのパッケージをモジュールとして管理する。モジュールの管理とビルドは任意のディレクトリで可能で，モジュールはリポジトリのバージョンタグまたはリビジョン毎に管理される|

### 環境変数 $GO111MODULE
モード切替は`$GO111MODULE`という環境変数で切り替えます。1.12でも`auto`が規定値になっている。 1.13でも引き続き`auto`になっていますが、現在の作業ディレクトリにgo.modファイルが含まれる場合は、goコマンドの`モジュール対応モード`がアクティブになります。 
[リリースノート：1.13](https://tip.golang.org/doc/go1.13)


#### 設定値
| 設定値|       | 
| :--- | :--- | 
| auto|$GOPATH 以下のディレクトリにあるパッケージは GOPATH モードで，それ以外はモジュール対応モードで動作する|
| off|常に GOPATH モードで動作する|
| on|常にモジュール対応モードで動作する|

## depからの移行
プロジェクトルートの`Gopkg.lock`ファイルを自動に読み込んで、moduleの初期設定を行ってくれる設計になっています。（depから移行するこが前提になっていますね :innocent:）
[Migrating to Go Modules](https://blog.golang.org/migrating-to-go-modules) などが参考になると思います。

1. 既存のパッケージに go.mod ファイルを追加する
2. Gopkg.lock ファイルを読んで go.mod ファイルに組み込んでくれる

```shell-session
$ go mod init github.com/my-repo/nice-project
go: creating new go.mod: module github.com/my-repo/nice-project
go: copying requirements from Gopkg.lock
```
＊もちろん、まっさらな状態から`go module`で初期化と依存パッケージのダウンロードを最初から行ってもいいです。

## 補足：Dockerでの設定
dockerで開発している場合は、ビルド前に依存パッケージをダウンロード・インストールする必要があります。ポイントだけをピックアップしたDockerfileのイメージです。

```Dockerfile
# Goのバージョンは1.13以上
FROM golang:1.13-alpine as golang-build

# Goビルドに必要なアプリケーションをインストールする
RUN .....

WORKDIR /go/src/github.com/my-repo/nice-project

# go moduleで依存パッケージを取得してダウンロードする
# ローカルの、go.modとgo.sumをコピー
COPY go.mod go.sum ./
# 依存ライブラリをダウンロードする
RUN go mod download

# Goアプリケーションのビルド
ADD . /go/src/github.com/my-repo/nice-project
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build main.go
```
ビルド前に、go moduleを使って依存ライブラリ・モジュールをダウンロードしておきます。
とても少ない記述量で実現できてしまいます。


## まとめ
depを使っているときはは、CIのビルドの時間を短縮のために`dep ensure -vendor-only=true`として新しいパッケージを追加する際に、Gopkg.lockとGopkg.tomlを更新していたり手間がかかっていました。`modules`でキャッシュディレクトリを設定しておけば、ビルド時間も短縮できるためトリッキーなファイルの運用がなくなりました。1.13がリリースされて４ヶ月位経ちますが、`dep`で苦労していたり、切替をお考えの方がいれば、`modules`への移行の参考になれば幸いです。