Goの静的コード解析を一括でいい感じにする(Wercker)
静的コード解析(lint)でコード品質を保つ
Goには静的解析のツールがたくさんあります。結局何使えばいいんだろうとか、いい感じの設定ファイルが欲しいなど、結構何かと困る点があったので、記事にしてまとめて紹介します。
alecthomas/gometalinter
今回の静的コード解析にはalecthomas/gometalinter
を使います。このOSSはgoに存在する様々な静的コード解析ツールを一括で使うことが出来る優れものを使って、CI回していきます。
まずは、go getします。
これ以降、静的コード解析ツールをlintとします。
$ go get -u github.com/alecthomas/gometalinter $ gometalinter --install --update Installing: unconvert gas gosimple ineffassign safesql varcheck dupl goimports golint misspell structcheck unparam unused errcheck gocyclo lll gotype interfacer staticcheck aligncheck deadcode goconst
このパッケージをインストールすると、こんなにlintツールあんのか!wって感じなんですけど、今回はこの中にあるいくつかのパッケージに絞って実践してみたいと思います。
それぞれのlintについては別記事でそれぞれが何かを紹介します。
いい記事が既にあったので紹介しておきます。
ソースくれ
lintの準備
Makefileを以下のように作成します
# 開発者のためのインストールコマンド install: @which gometalinter || go get -u github.com/alecthomas/gometalinter @gometalinter --install --update # Werckerのための依存関係解決コマンド deps: @which gometalinter || go get -v github.com/alecthomas/gometalinter @gometalinter --install --update # lintの実行 lint: @if [ "`gometalinter ./... --config=lint_config.json | tee /dev/stderr`" ]; then \ echo "^ - lint err" && echo && exit 1; \ fi
設定ファイルを以下のように作成します
lint_config.json
{ "DisableAll": true, "Enable": [ "vet", "gofmt", "staticcheck", "goconst", "golint", "goimports", "errcheck" ], "Exclude": [ "ignore" ], "Vendor": true }
軽く意味を解説すると以下のような感じです。(細かいニュアンスは若干違います。)
キー | 説明 |
---|---|
DisableAll | 全てのlinterを使わない設定にする |
Enable | 使用するlinterを指定する |
Exclude | lintしたくないファイルやフォルダを指定する |
Vendor | vendorフォルダを無視する |
他にもたくさんオプションがあるので、気になる方は、gometalinter --help
で調べてみてください。
lint対象コード作成
次に実際に解析するコードを適当に作成します。
フォルダ構成
. ├── Makefile ├── check │ └── main.go ├── ignore │ └── main.go ├── lint_config.json └── main.go
./main.go
package main import ( "github.com/tikasan/golint/check" "github.com/tikasan/golint/ignore" ) func main() { check.Hoge() ignore.Foo() }
./check/main.go
package check import "fmt" // Hoge print hoge <--- GoDocを書かないとエラーになる func Hoge() { fmt.Println("hoge") err := returnErr() // errチェックを行わないとエラーになる if err != nil { fmt.Println(err) } } // nolint <--- これを書くとlintが無視される // https://github.com/alecthomas/gometalinter#comment-directives func NoLint() { fmt.Println("noLint") } // returnErr func returnErr() error { return fmt.Errorf("return %s", "err") }
./ignore/main.go
フォルダごと無視される
package ignore import "fmt" func Foo() { fmt.Println("foo") }
では、lintを実行してみましょう。正しく終わったら、わざと間違えてみたりしてください。今回の例では、他にgoimportsされてない。gofmtされていないコードもエラーとなったりします。
$ make lint
Werckerと連携させる
せっかくlintが動くようになったので、CIで常にコードをチェックする仕組みほしいですよね。ということでさくっと実装してみたいと思います。
wercker.yml
box: golang build: steps: - setup-go-workspace - script: name: dependencies code: make deps - script: name: lint code: make lint
管理したいアプリケーションを追加します
今回はGithubのrepoなので、Githubを選択します
対象のrepoを選択する
Ownerを選びます
何で設定ファイルにアクセスするか選択します
このアプリケーションはpublicです的な
設定が完了したら、コミットしてみてください。lintが走ってコードがチェックされるようになります。
CollaborationにWerckerbotを追加しましょう
lintを入れるだけでもコードの品質は結構上がる
私は何か規模大きめ、またはリリース目的のコードには基本的にlintを必ず入れています。理由としては、コードの品質が保たれるのはもちろんですが、チームでやっていた場合に少なくともlintでチェックしている部分はレビューしなくてよかったりもするので、コスト削減にもなったりするので、絶対入れたほうが幸せになれますw