pre-commitによる静的解析を実施してみた!
はじめに
チーム開発をしていると、こんなことありませんか?🤔
- 「コミットしたけど、CIで静的解析に引っかかって落ちた…」
- 「ローカルで静的解析をかけ忘れてた…」
- 「レビューで指摘される前に気づきたかった…」
僕のチームでも静的解析を行なっていますが、正直ローカルで実行し忘れて、のちのプルリクエストレビューで指摘を受けることがよくありました。
その結果、余計な修正コミットが増えたり、レビューの手戻りが発生してしまいます。
そこで、pre-commitを使い「コミットの前にミスを発見する」仕組みを導入してみました。
この記事では、pre-commitの魅力を紹介しつつ、
僕のチームで実際にどう設定したかを事例として紹介します💡
pre-commitって?
pre-commit は、Git フックを利用して、コミット前に自動でスクリプトを実行するためのツールです。導入するメリットはシンプルですが強力です。
- コミット前にLintや整形を自動実行できる
- チームメンバー間で同じLintルールを強制できる
- プルリクエスト後の指摘による修正コミットのムダを減らせる
今回やりたかったこと
- とにかくチームメンバーが導入しやすい方法で実現したい
- Dockerコンテナ内で フック処理を実行したい
- 対象ディレクトリを限定して静的解析を実行したい
pre-commitの導入
今回のpre-commit導入では、.pre-commit-config.yaml(pre-commitの設定ファイル)を使って設定を管理しました。
pre-commitにはCLIオプションでhookを個別登録する方法もありますが、設定をYAMLファイルで一元管理すると、チームメンバー全員で同じ環境を共有できるのでおすすめです。
pre-commitのインストール
まずはPCにpre-commitをインストールします。
# macOS / Linux (pip)
pip install pre-commit
# macOS (Homebrew)
brew install pre-commit
# インストールチェック
pre-commit --version
.pre-commit-config.yaml
の配置
設定ファイルは プロジェクトのルートディレクトリ に置きます。
イメージはこんな感じ👇
my-rails-app/
├── .git/
├── .pre-commit-config.yaml # ココ
├── .typos.toml
├── Gemfile
├── app/
│ ├── models/
│ │ └── user.rb
│ ├── views/
│ │ └── users/index.html.slim
│ └── assets/stylesheets/
│ └── users.scss
└── spec/
Gitフックへの登録
pre-commitをGit フックに登録します。
.pre-commit-config.yaml
ファイルがあるプロジェクトルートディレクトリで実施します。
# これで、「git commit」のたびにの内容が自動で実行される
pre-commit install
静的解析の導入
pre-commitでは好きな処理をコミット前に差し込めます。
僕のチームでは、以下の静的解析をフックしています
ツール | 役割 |
---|---|
typos | タイポチェック |
rubocop | Rubyコード規約チェック |
slim-lint | SlimテンプレートLint |
scss-lint | SCSSファイルLint |
例えばtyposの設定はこんな感じです。
repos:
#-------------------------
# 1. typoチェック
#-------------------------
- repo: https://github.com/crate-ci/typos
rev: v1.31.2
hooks:
- id: typos
args: [ "--config", ".typos.toml", "--force-exclude" ]
これで、typoがあればコミット前に検出されます。
他のLintも同じように差し込むことができます。
実際の設定ファイル例(rubocop / slim-lint)
Dockerコンテナ上でアプリを動かしているため、ホスト環境ではなくコンテナ内のGemを使って静的解析を実行しています。
#-------------------------
# 2. rubocop
#-------------------------
- repo: local
hooks:
- id: rubocop
name: rubocop
entry: "\
docker exec -i <container-name> sh -c '\
cd /path/to/app && \
BUNDLE_GEMFILE=./Gemfile bundle exec rubocop $@'\
"
language: system
files: ^src/
pass_filenames: false
#-------------------------
# 3. slim-lint
#-------------------------
- repo: local
hooks:
- id: slim-lint # scss-lint に変える
name: slim-lint # scss-lint に変える
entry: "\
docker exec -i <container-name> sh -c '\
cd /path/to/app && \
BUNDLE_GEMFILE=./Gemfile bundle exec slim-lint $@'\
"
language: system
files: ^src/app/views # scss-lintは「^src/app/assets/stylesheets」を指定
pass_filenames: true
実行例
typos..........................................................Failed
- hook id: typos
- exit code: 2
error: `seperate` should be `separate`
--> test.txt:8:1
|
1 | seperate (正: separate)
| ^^^^^^^^
|
rubocop........................................................Failed
- hook id: rubocop
- exit code: 1
Inspecting 100 files
.....................C...................................(省略)
Offenses:
app/controllers/hoge/hogehoge.rb:6:10:
C: Naming/AsciiIdentifiers: Use only ascii symbols in identifiers.
rubocopで検出される専用の文字列です。
^^^^^^^^^^^^^^^
100 files inspected, 1 offense detected
slim-lint......................................................Passed
scss-lint..................................(no files to check)Skipped
おおー!
コミットしようとした瞬間に、自動で typos と rubocop のチェックが走って止めてくれました。
typosとrubocopで引っかかったのでコミットは中止されます。このおかげでコミット後の指摘が減らせる訳ですね。該当箇所を修正して再コミットすればOKです。
まとめ
pre-commitを導入したことで、導入前に期待していたメリットがそのまま形になりました。
- コミット前にLintや整形を自動実行できる
- typosやrubocopでエラーを検知し、修正しないとコミットできない仕組みになった
- チームメンバー間で同じLintルールを強制できる
- どの開発者でも、同じ手順で仕組みを導入できた
- どの開発者でも、同じタイミング・ルールで静的解析を実行可能になった
- プルリクエスト後の指摘による修正コミットのムダを減らせる
- レビューにて静的解析忘れの指摘が減少し、建設的なレビューに集中できるようになった
つまり、pre-commitを入れることで 「安心してコミットできる環境」 が整い、結果的にレビューも開発スピードも改善しました!
どんなLintを入れるかはチームごとに違っていても、コミット前に自動でチェックを走らせられるというpre-commitのメリットは共通です。まずはtypos
のような軽いチェックから試してみると、すぐに効果を実感できると思います。
今後は「実行時間をもっと短縮する工夫」や「解析結果のチーム共有」なども検討し、さらに開発体験を高めていきたいです。
以上、pre-commitを導入しようとする方の参考になれば幸いです
Discussion