【Go】Go言語Web開発プロジェクト Part2.5 - 箸休めにdepによるパッケージ管理改善とgolintなどで良いGoのコードを書いていく -

## 前回のあらすじ

前回はログイン認証の部分まで最低限開発する事ができました。
前回の記事から一ヶ月経過してしまい、そろそろ次のステップに進まないといけません。今日はこれまでやってきた事と、この一ヶ月の間でGo言語の勉強会にいくつか参加し得た知見、またGo言語における良いコードを書けるようになるためにはどうすれば良いか、参考文献を色々調べていったのでそれを共有したいと思います。

## gooseをyamlファイル経由でコマンド実行できるようにする(Reject)

現在、私のプロジェクトでは、bitbucketからforkされたこちらのgooseを使っています。
[https://github.com/pressly/goose](https://github.com/pressly/goose)
こちらはコマンド実行のみをサポートしているため、YAML経由でマイグレーション実行ができるようにPRを作成していました。
...結論を言うとRejectされました。理由はこちらです
[https://github.com/pressly/goose/pull/118](https://github.com/pressly/goose/pull/118)

しかし、今回のPRからパッケージ管理ツールはGo言語のオフィシャルチームが開発している`dep`を使う事がこれからは一択かなと思い、パッケージ管理ツールをgomからdepに置き換えることにしました。

depのオフィシャルドキュメントについてはこちら→[https://golang.github.io/dep/](https://golang.github.io/dep/)

### depのインストール

環境を再編成するために、まずはdepをインストールします。

```bash
% goenv exec go get -u github.com/golang/dep/cmd/dep 
```

その次はプロジェクトで利用するパッケージの一元管理ファイル`Gopkg.toml`,`Gopkg.lock`ファイルを生成します。

```go:Gopkg.toml
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#   name = "github.com/x/y"
#   version = "2.4.0"
#
# [prune]
#   non-go = false
#   go-tests = true
#   unused-packages = true

required = ["github.com/pressly/goose/cmd/goose"]

[[constraint]]
  name = "github.com/lib/pq"
  version = "1.0.0"

[[constraint]]
  name = "github.com/yosssi/ace"
  version = "0.0.5"

[prune]
  go-tests = true
  unused-packages = true
```

基本的には、direct dependency(プロジェクトのコードから直接されている)依存関係を`[[constraint]]`に記載します。
今回の場合、gooseに関してはそうではないため、`required`で必要なパッケージのソースを直接インストールします。

```bash
% dep ensure
```

その後、dep ensureで一括インストールします。しかし、dep ensureでは、srcを取得するのみでパッケージをインストールしてくれる訳ではありません。
詳しくはこちら→[https://dev.classmethod.jp/go/dep/](https://dev.classmethod.jp/go/dep/)

以下、クラスメソッド株式会社のdepの記事から引用

```
実行ファイルをインストールしたい場合はパッケージのインストールディレクトリ(mainパッケージが置かれているディレクトリ)に移動し(cd vendor/github.com/user/thing/cmd/thing) go install . でインストールする必要があります。
```

gooseをインストールする場合は、以下のようにします。

```bash
% cd vendor/github.com/pressly/goose/cmd
% goenv exec go install .
```

以上より、Dockerfileを以下のように更新しました。

```docker:Dockerfile
FROM golang:1.11.0

RUN apt update

# PostgreSQLのClientをインストール
RUN apt install -y postgresql-client

WORKDIR /go/src/dairy_report
ADD src/dairy_report/Gopkg.toml Gopkg.toml
ADD src/dairy_report/Gopkg.lock Gopkg.lock
# depだけgo getで取得
RUN go get -u github.com/golang/dep/cmd/dep

RUN dep ensure --vendor-only
WORKDIR /go/src/dairy_report/vendor/github.com/pressly/goose/cmd/goose/
RUN go install .

WORKDIR /go/src/dairy_report
```

Dockerコンテナでdep ensureする場合は、`--vendor-only`をオプションとして追加する必要があります。
詳しくはこちら→[https://github.com/golang/dep/issues/796](https://github.com/golang/dep/issues/796)

また、docker-compose.ymlではsrc/配下のソースコードをホストのものとマウントとして共有し、vendorディレクトリをコンテナ専用のボリュームとして格納したいので以下のように定義しました。

```yaml:docker-compose.yml
version: '3'
volumes:
  vendor:
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: go run main.go
    volumes:
      - ./src/dairy_report/:/go/src/dairy_report/
      - vendor:/go/src/dairy_report/vendor
(中略)
```

docker-composeの設定は終わりました。次はコード整形とレビューについてのお話です。

## フォーマット関連の整形は自動的に修正してもらったり、コメントレビューしてほしい

筆者はRails歴が長いため、コード整形やプロジェクトのローカルルールなどによるコード違反なものは、rubocopを利用して修正・あるいはコメントを残してもらっています。
本当はフォーマットも一貫性のあるコードを開発者が自分で気づかなきゃいけないと思っていますが、発見するのは結構大変なもので、フォーマット以外にも自分で気づいて直さなきゃいけないコードもあったりします。よって、ここ最近はコード修正の本質ではない(?)部分は機械的に直してもらった方が良いと言うのが僕の思想です。機械的に見つけてもらうことにより、ある程度負担が減り、`読み手を意識したコードを書くための修正`にかけられる時間が増えていきます(このあたりはリーダブルコードやプログラミング作法を読んでようやく意識できるようになってきました)。

ということで、フォーマットくらいは機械的に修正してもらおうとそういうツールがないか調べたら、なんとGoではデフォルトで`gofmt`というものが存在し、それにコードを喰わせることにより自動的に修正されます。

goenv経由でgofmtを実行します。

```bash
% goenv exec gofmt -d main.go                                                                                                                                                   (git)-[feature/add_login]
diff -u main.go.orig main.go
--- main.go.orig	2018-11-11 00:22:28.000000000 +0900
+++ main.go	2018-11-11 00:22:28.000000000 +0900
@@ -1,12 +1,12 @@
 package main
 
 import (
+	"database/sql"
 	"fmt"
+	_ "github.com/lib/pq"
+	"github.com/yosssi/ace"
 	"log"
 	"net/http"
-	"github.com/yosssi/ace"
-	"database/sql"
-	_ "github.com/lib/pq"
 )
 
 func login_handler(w http.ResponseWriter, r *http.Request) {
@@ -33,7 +33,7 @@
 		fmt.Println("username:", r.FormValue("username"))
 		fmt.Println("password:", r.FormValue("password"))
 
-		user := User{ r.FormValue("username"), r.FormValue("password")}
+		user := User{r.FormValue("username"), r.FormValue("password")}
 		db, err := sql.Open("postgres", "host=db user=dairy_report password=dairy_report dbname=dairy_report_development sslmode=disable")
 		if err != nil {
 			fmt.Println(err)
```

-dオプションで差分を取ることができます。この場合ですと、まだ標準出力に表示されただけなので、さらに-wオプションで直接ソースを書き換えるようにします。

```bash
%  goenv exec gofmt -w main.go
% git diff
diff --git a/src/dairy_report/main.go b/src/dairy_report/main.go
index 75bfcc5..35375ca 100644
--- a/src/dairy_report/main.go
+++ b/src/dairy_report/main.go
@@ -1,12 +1,12 @@
 package main
 
 import (
+       "database/sql"
        "fmt"
+       _ "github.com/lib/pq"
+       "github.com/yosssi/ace"
        "log"
        "net/http"
-       "github.com/yosssi/ace"
-       "database/sql"
-       _ "github.com/lib/pq"
 )
 
 func login_handler(w http.ResponseWriter, r *http.Request) {
@@ -33,7 +33,7 @@ func login_handler(w http.ResponseWriter, r *http.Request) {
                fmt.Println("username:", r.FormValue("username"))
                fmt.Println("password:", r.FormValue("password"))
 
-               user := User{ r.FormValue("username"), r.FormValue("password")}
+               user := User{r.FormValue("username"), r.FormValue("password")}
                db, err := sql.Open("postgres", "host=db user=dairy_report password=dairy_report dbname=dairy_report_development sslmode=disable")
                if err != nil {
                        fmt.Println(err)
```

なお、ここのフォーマットルールは以下のルールに基づいています。
[http://go.shibu.jp/effective_go.html](http://go.shibu.jp/effective_go.html)

### フォーマットルール以外のルールにおけるコメント追加

機械的に修正できない部分によるGo言語のコードルールは以下のようなものがあります。
[https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d](https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d)

こちらの細かなルールはコメントとして残して気づかせて欲しい派(?)なため、`reviewdog`と言うツールをインストールしてコードレビューしてもらいます。
reviewdogについてはこちら→[https://github.com/haya14busa/reviewdog](https://github.com/haya14busa/reviewdog)

そろそろTravis CIを入れても良い頃だったので導入することにしました。

```yaml:.travis.yml
env:
  global:
    - REVIEWDOG_VERSION="0.9.11"
sudo: false
language: go
go:
  - 1.11.2
install:
  - go get -u golang.org/x/lint/golint
  - mkdir -p ~/bin/ && export export PATH="~/bin/:$PATH"
  - curl -fSL https://github.com/haya14busa/reviewdog/releases/download/$REVIEWDOG_VERSION/reviewdog_linux_amd64 -o ~/bin/reviewdog && chmod +x ~/bin/reviewdog

script:
  - golint ./... | reviewdog -f=golint -reporter=github-pr-review
```

Github Tokenについては以下の設定を行います。
![スクリーンショット 2018-11-11 02.27.28.png](https://qiita-image-store.s3.amazonaws.com/0/144991/40e4529b-8439-de2e-4bbb-775ca3b56f5a.png)

reviewdogのサンプル例では.travis.ymlに直接トークンを入力していますが、ここでは.travis.ymlの方に保存しておきます。

![スクリーンショット 2018-11-11 02.28.41.png](https://qiita-image-store.s3.amazonaws.com/0/144991/6f1f45b2-9a75-8a70-f00d-fcf116d29abb.png)

以上により一旦reviewdogの導入は成功しました。

![スクリーンショット 2018-11-11 03.05.03.png](https://qiita-image-store.s3.amazonaws.com/0/144991/ff04e8dd-d251-1654-27cd-efb12551faa5.png)

なお、ちょうどこの作業を行うにあたって、`TypeName.IsAlias`のundefined methodエラーにあたってしまったので、Goのバージョンを1.11.0 -> 1.11.2に更新しました。
詳しくはこちら→[https://github.com/golang/go/issues/28291](https://github.com/golang/go/issues/28291)

## まとめ

とまぁ、色々書きましたがプロジェクトの進行はあんまり進んでいません汗
とは言え、ここ最近色々なGo言語の勉強会に参加させていただいて何となくですがGo言語による開発をしやすくなるための知見を学ぶ事ができました。
そろそろ次の機能を追加しようと考えてた矢先だったため、これからどんどんコードを書いていこうと思います。
それでは。

## 参考文献

[https://dev.classmethod.jp/go/dep/](https://dev.classmethod.jp/go/dep/)
[https://github.com/golang/dep/issues/796](https://github.com/golang/dep/issues/796)
[Effective Go](http://go.shibu.jp/effective_go.html)
[https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d](https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d)
[https://github.com/haya14busa/reviewdog](https://github.com/haya14busa/reviewdog)

## シリーズ

[Part2](https://qiita.com/himrock922/items/299ddde2ca3417ae6895) ← Part2.5