Golang - Go Modulesで開発環境の用意する

# はじめに
今まで以下の流れで開発環境を用意しました。

- [Golang - VSCodeで開発環境を用意する](https://qiita.com/so_heee_/items/e08daf20c75107eaebcb)
- [Golang - DockerとVSCodeで開発環境を用意する](https://qiita.com/so_heee_/items/1c2e50d3ed6e5ab3845f)
- [Golang - ディレクトリ構成と独自パッケージのimportについて](https://qiita.com/so_heee_/items/01e19c5e1fe177059efd)

GoModulesの存在を知り、以下の理由からもう一度開発環境を見直すことにしました。

- バージョン管理のデファクトスタンダードになる（なってる？）GoModulesを理解したかった
- GoModulesを使用することで、設定に苦しんだGOPATH関連の制限から解放されそうだった

# Go Modules
GoModulesは、Go1.11から導入され始めたGoの新しいバージョン管理システムになります。
詳細については、以下を参照いただくのが良いと思います。
GoModulesの概要から、今までのバージョン管理との違いまで把握できます。

- [Go 1.11 Modules](https://github.com/golang/go/wiki/Modules)
- [Using Go Modules](https://blog.golang.org/using-go-modules)
- [Go Modulesの概要とGo1.12に含まれるModulesに関する変更](https://budougumi0617.github.io/2019/02/15/go-modules-on-go112/)

## GOPATH mode から module-aware mode
今まで（**GOPATH mode**）は標準pkg以外を全部 `$GOPATH` 以下のディレクトリで管理する仕様となっており
プロジェクトも `$GOPATH/src` 配下に作成する必要がありました。この制約で上記の開発環境を用意する際にかなり苦しみました。

しかし、**module-aware mode** では標準pkg以外の全てのパッケージをモジュールとして扱い、モジュールの管理やビルドが任意のディレクトリで可能になりました。（$GOPATH/src にプロジェクトを配置しちゃダメというわけではなそう）

# 環境構築
go（1.10以降）とVSCodeはインストール済みの状態から始めたいと思います
私はgo(1.13.1)、VSCode（1.38.1）でやっています

## 環境変数

```bash:.zshrc（Shellによる）
export PATH=$GOPATH/bin:$PATH
export GOPATH=$HOME/go
export GO111MODULE=on
```

※GOPATHはプロジェクトと違うディレクトリで試したかったので `$HOME/go` にしています

`GO111MODULE` はGOPATH modeとmodule-aware modeを切り替えるための環境変数です。
Go 1.13からデフォルトが `on` になる予定だったようですが、どうやら `auto` がデフォルトになっているそうです。
そのため環境変数に上記を設定します。

- `on` ：Go modules を使う (module-aware mode)
- `off` ：$GOPATH を使う (GOPATH mode)
- `auto` ：$GOPATH/src の外に対象のリポジトリがあり、 go.mod が存在する場合は module-aware mode、そうでない場合は GOPATH mode

## ディレクトリの作成

以下の構成でディレクトリを作成します

参考：[Modules#quick-start](https://github.com/golang/go/wiki/Modules#quick-start)

```bash
$  tree -F GolangProjects
GolangProjects
└── github.com/
    └── so-heee/
        └── modules_example/
```


## モジュールの初期化

以下のコマンドでモジュールを初期化します
初期化すると `go.mod` ファイル（モジュールを管理するファイル）が出来ます

```bash
$ go mod init github.com/so-hee/modules_example
go: creating new go.mod: module github.com/so-hee/modules_example
```

```
└── modules_example
    └── go.mod
```

## サンプルプログラムの作成

```go:hello.go
package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
```

## ビルドと実行
作成したファイルをビルドすると、依存パッケージである `rsc.io/quote` がダウンロードされます


```bash
$ go build -o hello
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
```

GOPATHに設定したディレクトリにパッケージがダウンロードされています

```bash
$ tree -F go -L 3 -d
go
└── pkg
    └── mod
        ├── cache
        ├── golang.org
        └── rsc.io
```` 

また `go.sum` ファイル（依存モジュールのチェックサムの管理をしてるファイル）が作成されています
ビルドしたファイルを実行してみます

```bash
$ ./hello
Hello, world.
```

# VSCodeでの環境設定

上記のフォルダをVSCodeで開きます

## 拡張機能インストール

**Cmd + Shif + x** でExtensionMarketplaceを開いてGoをインストール

<img width="321" alt="スクリーンショット 2019-09-22 20.03.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/ae177d64-7eea-60a8-4e52-f3ca44517938.png">

先ほど作成したhello.goのファイルを開くとツールのパッケージをインストールするか聞かれるのでインストールしましょう

![スクリーンショット 2019-10-01 16.15.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/b6341666-67e8-03f2-c088-eff9e601d3a6.png)

聞かれない場合がもしあった場合は、**Cmd + Shift + P** でコマンドパレットを表示
`GO: Install/Update Tools` で全て選択してインストールしましょう

## 独自パッケージの作成

新規でパッケージを作成し、mainでimportしてみます

```go:modules_example/calc/calc.go
package calc

// Sum は引数a, bを合算して返却します
func Sum(a, b int) int {
	return a + b
}
```

```go:hello.go
package main

import (
	"fmt"

	"github.com/so-heee/modules_example/calc"
	"rsc.io/quote"
)

func main() {
	fmt.Println(quote.Hello())
	fmt.Println(calc.Sum(1, 2))
}
```

問題なくimportできたので、実行してみます。F5でデバッグしてみます

```bash
Hello, world.
3
```

# Dockerを使った開発環境の設定


## dockerのインストール
Homebrewでdockerをインストールします（Homebrewの使い方は省略します）

```bash                                                                                                                                     
$ brew cask install docker
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
==> Updated Formulae
dbmate                kops                  mosquitto            rabbitmq

==> Satisfying dependencies
==> Downloading https://download.docker.com/mac/stable/38240/Docker.dmg
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'docker'.
==> Installing Cask docker
==> Moving App 'Docker.app' to '/Applications/Docker.app'.
🍺  docker was successfully installed!

$ docker --version
Docker version 19.03.2, build 6a30dfc
```

## VSCodeにRemote-Containersのインストール
**Cmd + Shif + x** でExtensionMarketplaceを開いて `Remote-Containers` を検索してインストールします

![スクリーンショット 2019-09-26 12.36.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/57636340-0a91-cb79-34dd-5d2a3798bf9c.png)

## 設定ファイルの作成

左下の矢印アイコンを選択します

![スクリーンショット 2019-09-26 12.33.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/ccb29f5e-2b03-802a-ffe3-89e433b619ce.png)

今度は `Remote-Containers: Add Development Container Configuration Files` を選択します
何の開発環境を作るか聞かれるため`Go`を選択します

![スクリーンショット 2019-09-26 17.33.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/738afa4c-e2d4-fe36-6c43-694386d375dd.png)

選択すると先ほどのサンプルであった`.devcontainer`ディレクトリが作成され
その下に`devcontainer.json`と`Dockerfile`が入っています

![スクリーンショット 2019-10-01 17.03.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/bf1380ad-43e5-c947-40f0-8574364f11ea.png)

## Dockerコンテナの作成

左下の矢印アイコンを選択します

`Remote-Containers: Open Folder in Container` を選択し、フォルダを選択します
![スクリーンショット 2019-09-26 12.33.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/1fbc17d1-2a96-1660-7af7-c645f67075c6.png)

ビルドが開始されます

![スクリーンショット 2019-09-26 13.56.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/a38a9787-098c-83c1-adaf-91c1711f1784.png)

![スクリーンショット 2019-09-26 17.41.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/d66b23eb-d6d3-845c-7c9b-6b0e83dda659.png)
>ビルド中にdetailsを押すと、ターミナルにビルドの状況が表示されます

完了するとエクスプローラーに表示されます

![スクリーンショット 2019-10-01 17.13.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151022/627df042-a950-e78b-9cb6-27ad1ab350df.png)


## コンテナ上でGOのプログラムを実行

ターミナルタブを開いて、右側のプルダウンを `1:bash` にします
hello.goを実行してみます

するとコンテナ上のGOPATH（devcontainer.jsonのgo:gopath）に依存パッケージがダウンロードされます
ローカルの時と同じですね。

```bash
root@4f93101644d1:/workspaces/GolangProjects/github.com/so-heee/modules_example# go run ./hello.go 
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
こんにちは世界。
3
```
※こんにちは世界になるのは別の問題だと思うので今回は無視します（F5実行は問題ありません）

## 課題（依存先モジュールの編集する時）
githubで公開すると、基本的にそちらを参照してしまうため、ローカルで修正しても動作が変わりませんでした。。。
対応策としてgo.modを一時的に書き換えてreplaceディレクティブを使う方法や、go mod vendor でvendorを書き出してしまってそれを編集するなどがあるようですが、そもそも開発スタイルが正しいのか不安が残ります。
こちら解決策ご存知の方、もしくは別の方法をとってる方などいましたら是非ご教示ください！！

参考：[最近のGo Modulesプラクティス ~ ghqユーザーの場合も添えて](https://songmu.jp/riji/entry/2019-03-28-go-modules.html)

# 参考
- [Go 1.11 Modules](https://github.com/golang/go/wiki/Modules)
- [Using Go Modules](https://blog.golang.org/using-go-modules)
- [Go Modulesの概要とGo1.12に含まれるModulesに関する変更](https://budougumi0617.github.io/2019/02/15/go-modules-on-go112/)
- [Go Modules](https://qiita.com/propella/items/e49bccc88f3cc2407745)
- [Golang開発環境を作成する(module対応) - Ubuntu16.04/VSCode](https://qiita.com/ochipin/items/cae787d75ae91247c722)
- [Go modules support in Visual Studio Code](https://github.com/Microsoft/vscode-go/wiki/Go-modules-support-in-Visual-Studio-Code)