ぺい

渋谷系アドテクエンジニアの落書き

Goの静的コード解析を一括でいい感じにする(Wercker)

静的コード解析(lint)でコード品質を保つ

f:id:tikasan0804:20170622161758j:plain

Goには静的解析のツールがたくさんあります。結局何使えばいいんだろうとか、いい感じの設定ファイルが欲しいなど、結構何かと困る点があったので、記事にしてまとめて紹介します。

alecthomas/gometalinter

github.com

今回の静的コード解析には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については別記事でそれぞれが何かを紹介します。
いい記事が既にあったので紹介しておきます。

tech.sideci.com

ソースくれ

github.com

lintの準備

Makefileを以下のように作成します

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で常にコードをチェックする仕組みほしいですよね。ということでさくっと実装してみたいと思います。

www.wercker.com

wercker.yml

box: golang
build:

    steps:
        - setup-go-workspace

        - script:
            name: dependencies
            code: make deps

        - script:
            name: lint
            code: make lint

管理したいアプリケーションを追加します f:id:tikasan0804:20170622160525p:plain

今回はGithubのrepoなので、Githubを選択します f:id:tikasan0804:20170622160527p:plain

対象のrepoを選択する f:id:tikasan0804:20170622161624p:plain

Ownerを選びます f:id:tikasan0804:20170622160533p:plain

何で設定ファイルにアクセスするか選択します f:id:tikasan0804:20170622160546p:plain

このアプリケーションはpublicです的な f:id:tikasan0804:20170622160550p:plain

設定が完了したら、コミットしてみてください。lintが走ってコードがチェックされるようになります。 f:id:tikasan0804:20170622160552p:plain

CollaborationにWerckerbotを追加しましょう f:id:tikasan0804:20170718213354p:plain

lintを入れるだけでもコードの品質は結構上がる

私は何か規模大きめ、またはリリース目的のコードには基本的にlintを必ず入れています。理由としては、コードの品質が保たれるのはもちろんですが、チームでやっていた場合に少なくともlintでチェックしている部分はレビューしなくてよかったりもするので、コスト削減にもなったりするので、絶対入れたほうが幸せになれますw