前回のあらすじ
前回はログイン認証の部分まで最低限開発する事ができました。
前回の記事から一ヶ月経過してしまい、そろそろ次のステップに進まないといけません。今日はこれまでやってきた事と、この一ヶ月の間でGo言語の勉強会にいくつか参加し得た知見、またGo言語における良いコードを書けるようになるためにはどうすれば良いか、参考文献を色々調べていったのでそれを共有したいと思います。
gooseをyamlファイル経由でコマンド実行できるようにする(Reject)
現在、私のプロジェクトでは、bitbucketからforkされたこちらのgooseを使っています。
https://github.com/pressly/goose
こちらはコマンド実行のみをサポートしているため、YAML経由でマイグレーション実行ができるようにPRを作成していました。
...結論を言うとRejectされました。理由はこちらです
https://github.com/pressly/goose/pull/118
しかし、今回のPRからパッケージ管理ツールはGo言語のオフィシャルチームが開発しているdep
を使う事がこれからは一択かなと思い、パッケージ管理ツールをgomからdepに置き換えることにしました。
depのオフィシャルドキュメントについてはこちら→https://golang.github.io/dep/
depのインストール
環境を再編成するために、まずはdepをインストールします。
% goenv exec go get -u github.com/golang/dep/cmd/dep
その次はプロジェクトで利用するパッケージの一元管理ファイルGopkg.toml
,Gopkg.lock
ファイルを生成します。
# 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
で必要なパッケージのソースを直接インストールします。
% dep ensure
その後、dep ensureで一括インストールします。しかし、dep ensureでは、srcを取得するのみでパッケージをインストールしてくれる訳ではありません。
詳しくはこちら→https://dev.classmethod.jp/go/dep/
以下、クラスメソッド株式会社のdepの記事から引用
実行ファイルをインストールしたい場合はパッケージのインストールディレクトリ(mainパッケージが置かれているディレクトリ)に移動し(cd vendor/github.com/user/thing/cmd/thing) go install . でインストールする必要があります。
gooseをインストールする場合は、以下のようにします。
% cd vendor/github.com/pressly/goose/cmd
% goenv exec go install .
以上より、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
また、docker-compose.ymlではsrc/配下のソースコードをホストのものとマウントとして共有し、vendorディレクトリをコンテナ専用のボリュームとして格納したいので以下のように定義しました。
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を実行します。
% 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オプションで直接ソースを書き換えるようにします。
% 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
フォーマットルール以外のルールにおけるコメント追加
機械的に修正できない部分によるGo言語のコードルールは以下のようなものがあります。
https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d
こちらの細かなルールはコメントとして残して気づかせて欲しい派(?)なため、reviewdog
と言うツールをインストールしてコードレビューしてもらいます。
reviewdogについてはこちら→https://github.com/haya14busa/reviewdog
そろそろTravis CIを入れても良い頃だったので導入することにしました。
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
reviewdogのサンプル例では.travis.ymlに直接トークンを入力していますが、ここでは.travis.ymlの方に保存しておきます。
以上により一旦reviewdogの導入は成功しました。
なお、ちょうどこの作業を行うにあたって、TypeName.IsAlias
のundefined methodエラーにあたってしまったので、Goのバージョンを1.11.0 -> 1.11.2に更新しました。
詳しくはこちら→https://github.com/golang/go/issues/28291
まとめ
とまぁ、色々書きましたがプロジェクトの進行はあんまり進んでいません汗
とは言え、ここ最近色々なGo言語の勉強会に参加させていただいて何となくですがGo言語による開発をしやすくなるための知見を学ぶ事ができました。
そろそろ次の機能を追加しようと考えてた矢先だったため、これからどんどんコードを書いていこうと思います。
それでは。
参考文献
https://dev.classmethod.jp/go/dep/
https://github.com/golang/dep/issues/796
Effective Go
https://gist.github.com/knsh14/0507b98c6b62959011ba9e4c310cd15d
https://github.com/haya14busa/reviewdog
シリーズ
Part2 ← Part2.5