この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
丹内です。
gitのcommit直前に自動でrubocopによるスタイルチェックが行われるように設定します。
なぜスタイルチェックが必要なのか
GitFlowあるいはGitHub Flowにのっとり複数人で開発する場合、Pull Requestベースのコードレビューが重要です。
Pull Requestでは実装が仕様を満たしているか、クラスやメソッドの設計が適切か、ナレッジを共有できているか、といった観点から、議論を交わし、チームとしてコードを良くしていきます。
その際に、
と言った、コーディング規約についての議論があると、下手をすると宗教戦争になり、生産性が高いとは言えません。
事前にコーディング規約を決めていても、つい油断して規約違反のままコミットし、指摘を受けてしまうこともあるでしょう。
これを防ぐため、スタイルチェックを自動で行うライブラリが、メジャーな各言語には存在します。
Rubyの場合、それがrubocopです。
コミットのたびにスタイルチェックを実施する理由
設定したRubocopは、コミット前に手動で実行するか、あるいはCIにセットするなどの運用が考えられます。
しかし、手動だと忘れるかもしれませんし、pushしてからCIでスタイルチェックに引っかかってまた修正をpushするというのも、勢いをそがれてしまい、個人的には不十分だと思います。
そこで、git commitに連動してスタイルチェックを走らせる設定を行います。
これにより、スタイルチェックを通過したコードのみがコミットされるため、コーディング規約の確実な運用が期待できます。
Railsアプリにpre-commit gemを設定する
サンプルのディレクトリを作成し、適当なクラスを作っておきます。
$ mkdir rubocopsample
$ cd rubocopsample; touch sample.rb
$ git init
$ rbenv exec bundle init
例えば、sample.rbは以下のとおりです。
class Sample
def hoge
end
end
次に、bundlerでpre-commit gemを導入し、初期設定を行います。
gem 'pre-commit' と gem 'rubocop' の2行をGemfileに追記し、bundle installしてください。
そして、pre-commitのファイルを生成します。
$ rbenv exec bundle exec pre-commit install
Installed /Users/tannaiyuki/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/pre-commit-0.23.0/templates/hooks/default to .git/hooks/pre-commit
最後に、.git以下の設定ファイルを編集し、git commitコマンドに連動するようにします。
.git/hooks/pre-commit というファイルを以下のように編集してください。
#!/usr/bin/env sh
# This hook has a focus on portability.
# This hook will attempt to setup your environment before running checks.
#
# If you would like `pre-commit` to get out of your way and you are comfortable
# setting up your own environment, you can install the manual hook using:
#
# pre-commit install --manual
#
cmd=`git config pre-commit.ruby 2>/dev/null`
if test -n "${cmd}"
then true
elif which rvm >/dev/null 2>/dev/null
then cmd="rvm default do ruby"
elif which rbenv >/dev/null 2>/dev/null
then cmd="rbenv exec ruby" <=== 修正前
then cmd="rbenv exec bundle exec ruby" <=== 修正後
else cmd="ruby"
fi
export rvm_silence_path_mismatch_check_flag=1
${cmd} -rrubygems -e '
begin
require "pre-commit"
true
rescue LoadError => e
$stderr.puts <<-MESSAGE
pre-commit: WARNING: Skipping checks because: #{e}
pre-commit: Did you set your Ruby version?
MESSAGE
false
end and PreCommit.run
'
ここで、rubocopを動かしてみます。
$ bundle exec rubocop
Inspecting 2 files
CC
Offenses:
Gemfile:2:8: C: Prefer single-quoted strings when you don't need string interpolation or special symbols.
source "https://rubygems.org"
^^^^^^^^^^^^^^^^^^^^^^
sample.rb:1:1: C: Missing top-level class documentation comment.
class Sample
^^^^^
2 files inspected, 2 offenses detected
Gemfile内の、変数展開の無いダブルクォートが引っかかってしまいました。
もちろんGemfileのダブルクォートをシングルクォートに変えても良いのですが、Gemfileはチェック対象から外すように設定してみましょう。
.rubocop.ymlを作成し、以下の内容を記述します。
AllCops:
Exclude:
- Gemfile
これで再び実行してみます。
$ bundle exec ubocop
Inspecting 1 file
C
Offenses:
sample.rb:1:1: C: Missing top-level class documentation comment.
class Sample
^^^^^
1 file inspected, 1 offense detected
今度はクラスの上にコメントがないと言われました。
修正を忘れたという前提で、このままコミットしてみましょう。
ただ、その前に、pre-commitがrubocopを実行するように設定を行い、動作確認を行います。
$ git config pre-commit.checks "rubocop"
$ bundle exec pre-commit run all
pre-commit: Stopping commit because of errors.
Inspecting 1 file
C
Offenses:
sample.rb:1:1: C: Missing top-level class documentation comment.
class Sample
^^^^^
1 file inspected, 1 offense detected
pre-commit: You can bypass this check using `git commit -n`
良さそうです。では、実際にコミットしてみます。
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: sample.rb
$ git commit -m 'add sample'
pre-commit: Stopping commit because of errors.
Inspecting 1 file
C
Offenses:
sample.rb:1:1: C: Missing top-level class documentation comment.
class Sample
^^^^^
1 file inspected, 1 offense detected
pre-commit: You can bypass this check using `git commit -n`
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: sample.rb
このように、コミットしようとするとpre-commitでrubocopが走り、引っかかったらコミットが中断されるようになりました。
pre-commitの設定ポリシーについて
pre-commit gemはrubocop以外にも様々なhookを設定できます。
$ bundle exec pre-commit list
Available providers: default(0) git(10) git_old(11) yaml(20) env(30)
Available checks : before_all ci closure coffeelint common console_log csslint debugger gemfile_path go jshint jslint json local merge_conflict migration nb_space pry rails rspec_focus rubocop ruby ruby_symbol_hashrockets scss_lint tabs whitespace yaml
Default checks : rubocop
Enabled checks : rubocop
Evaluated checks : rubocop
Default warnings :
Enabled warnings :
Evaluated warnings :
もちろんRSpecも設定できるのですが、その場合はテストに通らなければコミットできない、ということになります。
私はテストはpre-commit hookせず、rubocopだけhookするように設定しています。
理由は、以下のとおりです。
まとめ
pre-commit gemにより、git commitの前にrubocopによるスタイルチェックを行うよう設定し、通らなければコミットできないようにしました。
プロジェクトの初期から設定しておくと、有意義なコードレビューができてとても良いのでオススメです。