📘

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の設定はこんな感じです。

.pre-commit-config.yaml
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を使って静的解析を実行しています。

.pre-commit-config.yaml
#-------------------------
# 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

おおー!
コミットしようとした瞬間に、自動で typosrubocop のチェックが走って止めてくれました。
typosとrubocopで引っかかったのでコミットは中止されます。このおかげでコミット後の指摘が減らせる訳ですね。該当箇所を修正して再コミットすればOKです。

まとめ

pre-commitを導入したことで、導入前に期待していたメリットがそのまま形になりました。

  • コミット前にLintや整形を自動実行できる
    • typosやrubocopでエラーを検知し、修正しないとコミットできない仕組みになった
  • チームメンバー間で同じLintルールを強制できる
    • どの開発者でも、同じ手順で仕組みを導入できた
    • どの開発者でも、同じタイミング・ルールで静的解析を実行可能になった
  • プルリクエスト後の指摘による修正コミットのムダを減らせる
    • レビューにて静的解析忘れの指摘が減少し、建設的なレビューに集中できるようになった

つまり、pre-commitを入れることで 「安心してコミットできる環境」 が整い、結果的にレビューも開発スピードも改善しました!

どんなLintを入れるかはチームごとに違っていても、コミット前に自動でチェックを走らせられるというpre-commitのメリットは共通です。まずはtyposのような軽いチェックから試してみると、すぐに効果を実感できると思います。

今後は「実行時間をもっと短縮する工夫」や「解析結果のチーム共有」なども検討し、さらに開発体験を高めていきたいです。

以上、pre-commitを導入しようとする方の参考になれば幸いです

Inventit Tech

Discussion