golintと闘いたいけど心の折れてしまった勇者のための隠しダンジョン

  • 8
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

gopher-dq.png

結論

Pull-Request時にCIで差分lintかけるといい感じ。

背景

Goでの開発においては、型付け言語の利点を最大限に活かしたいところです。
go-vetgolintを通していれば、潜在的バグに悩まされる確率を、劇的に下げることができる・・・実に素晴らしいですよね。

ですが、時にプロダクトの黎明期にあって、「こまいコト気にしてたらやってらんないから、とにかく作る!」と勢いに任せて作るのも大事なことだったりします。

しかしその結果、

  • ふと気づけばソースコードの総量は10万行が見えてくる・・・
  • golint ./... を叩いてみれば、エラーが数千個・・・

なんてこともままあることでしょう。

それでも取り組むべき価値がある

ですが、そんな時でも遅くはありません。
GitHubのPull-RequestとCIツールを組み合わせれば、

今まさに修正した場所に、golintで問題とされるような箇所がないか

をチェックすることができます。
数千のエラーといっぺんに闘うことは難しくても、一度のPRで数十のエラーと戦い続ければ、いずれエラーはゼロになっていくはずです。
そう、バグを潰す気持ちと何ら変わらない、シンプルな作業ゲーと化すことができるのです。

解決策

用意するものはgoghだけ1です。

lint.sh
owner=                    # 対象リポジトリ名(オーナー名)を設定
repos=                    # 対象リポジトリ名を設定
min_confidence=0.9        # 警告のレベル
head=$(git rev-parse --abbrev-ref @) # 現在のブランチを取得
if [ -n "${head}" ]
then
  # 現在のブランチをheadとするpull-requestを探す
  base=$(gogh pr ls --repo ${owner}/${repos} --head ${owner}:${head} --state open --row-format $'{{.Base.Ref}}')
  if [ -z "${base}" ]
  then
    # pull-requestが見つからなければそのまま終了
    echo "pull-request is not found"
  else
    # pull-requestのbaseブランチと、差分のあるファイルの一覧を取得
    tmpfile=$(mktemp)
    git fetch origin ${base}:${base}
    git diff ${base}...${head} --name-only | grep -v > ${tmpfile}

    # golintを実行して、差分のあるファイルに関する部分だけを抽出する
    lint=$(golint --min_confidence=${min_confidence} ./... | grep -F -f ${tmpfile})
    rm ${tmpfile}

    # lintの結果、何かしらの出力があればエラーとする
    if [ -n "${lint}" ]
    then
      echo "${lint}"
      exit 1
    fi
  fi
fi

要は、

  • 対象ブランチ(HEAD)のpull-requestを探す
  • pull-requestの向き先(Base)との差分を(ファイル単位で)抽出
  • lintを実行 2
  • 差分のあるファイルに関するエラーだけを引っ張り出す

という処理の流れになっています。

殆どのCIでは、対象のブランチ名やリポジトリ名は環境変数などで取得できると思いますので、冒頭の変数設定しているところを適宜置き換えれば動くでしょう。

注意点

あくまでも「存在するPull-Requestを元に」動いています。なので、
* git checkout -b new-branch
* 〜ひと通り修正作業〜
* git push -u origin new-branch → CIが動き出す
* プルリクエストを作る
という流れで作業してしまうと、CIが動いている間にPull-Requestが見つからず、するっと通り抜けてしまうことも考えられます。

より厳格に適用したい場合、
* masterブランチ以外のCIでは、必ずPull-Requestが存在し無くてはならない
というルールにしてもいいかもしれません。
その場合はmasterブランチ以外の場合のコードにおいて、
echo "pull-request is not found" の後に、exit 1 と1行追加するだけです。

また、単純に作業の流れを変えて、

  • git checkout -b new-branch
  • git push -u origin new-branch # => CIが動き出す
  • プルリクエストを作る
  • 〜ひと通り修正作業〜
  • git push

としても良いでしょう。ちなみに私は後者で作業しています。

終わりに

以上、大量のlintエラーに心の折れない運用方法のTipsでした。
しかしながら、結局のところ 最初からlinterを気にかける のが正義かもしれません。
「既に心折れとるわ!」という方に、すこしでもlinterを好きになってもらえたら、と思い、記事を書き起こしてみました。

みなさまのlinterライフに光あらんことを・・・!


  1. ステマです。拙作のgithubクライアントです。同様の機能があるツールがアレばそれでもいいですし、cURLjqを組み合わせて頑張ってもいけるはず。 

  2. ここでは、min_confidence=0.9としてlintの基準をやや緩めに設定しています。(参照)。Id => IDなど、辞書ベースが必要な命名規則ってどうなの、という疑問が未だ拭えないのと、割とメジャーなライブラリ(mgoなど)が堂々とこれを破っていることもあり、この規則の適用には慎重になっています。