コミット前にチェックしたい(pre-commit)

$ pre-commit --version
pre-commit 4.6.2

$ pre-commit install
$ pre-commit run --all-files

pre-commitはGit Hooksを使って、 コミット前などにコードのチェック作業などを自動化できるツールです。 設定ファイルは.pre-commit-config.yamlです。 Pythonで書かれていますが、いろいろなプログラミング言語やプロジェクトで使えるようになっています。

コミット時に自動的にリンターやフォーマッターを実行することで、 コード品質を保ち、後戻り作業を減らすことができます。

注釈

commitizenを有効にすると、自動で追加されます。

インストールしたい(pre-commit)

複数のインストール方法から選べます:

  • pipでインストール

$ pip3 install pre-commit
$ pip3 install -U pre-commit  # アップグレード
  • pipxでインストール

$ pipx install pre-commit
$ pipx upgrade pre-commit
  • poetryで依存関係に追加

$ poetry add pre-commit --group=dev
  • uvでインストール(推奨)

$ uv tool install pre-commit
$ uv tool upgrade pre-commit

設定したい(.pre-commit-config.yaml)

$ pre-commit sample-config > .pre-commit-config.yaml

pre-commit sample-configコマンドで、雛形となる.pre-commit-config.yamlを出力できます。 出力されるrevは固定の古いバージョンなので、次のように書き換えて使うのがオススメです。

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v6.0.0  # 最新バージョンにする
  hooks:
  - id: trailing-whitespace
  - id: end-of-file-fixer
  - id: check-added-large-files
- repo: リポジトリ
  rev: バージョン
  hooks:
  - id: フック名

.pre-commit-config.yamlにフック情報を記述します。 設定できるフックはSupported Hooksで確認できます。

フックをインストールしたい(install)

$ pre-commit install

pre-commit installでGit Hooksを有効化できます。 .pre-commit-config.yamlの設定が完了したら実行してください。

フックを実行したい(run)

$ pre-commit run --all-files
$ pre-commit run -a  # 短縮形

pre-commit run --all-files(短縮形-a)で すべてのファイルに対してチェックを実行します。

trailing-whitespaceやend-of-file-fixerなどのフックは自動でファイルを修正します。

フックを追加したい

$ pre-commit install --install-hooks
$ pre-commit install --install-hooks --hook-type pre-commit
$ pre-commit install --install-hooks --hook-type commit-msg

--install-hooksで、.pre-commit-config.yamlに書かれたフックをインストールできます。 --hook-type フックタイプで、フックのタイミングを追加できます。

フックのタイミング

フックのタイミングは[stages]で変更できます。 デフォルトではpre-commit(コミットの直前)に実行されます。

Git Hook

タイミング

設定例

pre-commit

コミットの直前

stages: [pre-commit]

pre-merge-commit

マージコミットの直前

stages: [pre-merge-commit]

commit-msg

コミットメッセージ作成の直後

stages: [commit-msg]

prepare-commit-msg

コミットメッセージ作成の直前

stages: [prepare-commit-msg]

pre-push

プッシュの直前

stages: [pre-push]

pre-rebase

リベースの直前

stages: [pre-rebase]

post-checkout

ブランチ切り替え後

stages: [post-checkout]

post-commit

コミット後

stages: [post-commit]

post-merge

マージ後

stages: [post-merge]

post-rewrite

コミット書き換え後(commit --amendやrebase)

stages: [post-rewrite]

pre-commit install --helpの--hook-typeで、実際にサポートされているタイミングの一覧を確認できます。

設定ファイルでタイミングを指定する例:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: detect-private-key
        stages: [pre-push]  # プッシュの直前に実行

フックを確認したい

$ find .git/hooks -type f ! -name "*.sample" -perm -u+x

pre-commit自身に、フックを一覧する機能はありません。 .git/hooks/を直接確認して、フックが追加されているかを確認します。

findコマンドを使う場合、-perm -u+xのようにハイフンを付けることで、実行可能なファイルだけに絞り込めます

事前チェックしたい(pre-commit-hooks)

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v6.0.0
  hooks:
  - id: detect-private-key
  - id: trailing-whitespace
    args: ["--markdown-linebreak-ext=md"]
  - id: end-of-file-fixer
  - id: check-case-conflict
  - id: check-merge-conflict
  - id: check-added-large-files
    args: ["--maxkb=500"]
  - id: check-json
  - id: check-toml
  - id: check-yaml
  - id: name-tests-test
    args: [--pytest-test-first]

pre-commit-hooksのフックから、 使うとよさそうなものを選んでみました。

ruffしたい(ruff-pre-commit)

ruffは、Pythonプロジェクトの リンター&フォーマッターです。 コミットごとに自動チェックすることで、コードの表記ゆれを抑えることができます。

フックの設定方法はフォーマッター/リンターしたい(ruff)に整理しました。

commitizenしたい(commitizen)

commitizen (cz)はコミットメッセージの形式を守るためのツールです。 stages: [commit-msg]でコミットメッセージを保存したあとにフックがかかるようにしておきます。

フックの設定方法はセマンティック・バージョニングしたい(commitizen)に整理しました。

poetryしたい

repos:
- repo: ...
- repo: https://github.com/python-poetry/poetry
  rev: 2.4.1
  hooks:
  - id: poetry-check
    args: [--lock]
    stages: [pre-push]
  #- id: poetry-lock
  #- id: poetry-install
- repo: https://github.com/python-poetry/poetry-plugin-export
  rev: 1.10.0
  hooks:
  - id: poetry-export
    args: [--format, requirements.txt, --output, requirements.txt]
    stages: [pre-push]

Pythonプロジェクトをpoetryで管理している場合は、 poetry-check(=poetry check)、 poetry-lock(=poetry lock)、 poetry-export(=poetry export)、 poetry-install(=poetry install) のフックを導入してみるとよいかもしれません。

それぞれに適切なargsを設定して使うとよいと思います。 また、コミット時ではなくプッシュ時(pre-push)に設定するとよいと思います。

注意

poetry-exportフックは、poetry本体(python-poetry/poetry)ではなく、 poetry-plugin-exportという別リポジトリに移動しました。 poetry本体のrepoを指定すると、 is not present in repositoryエラーになります。 poetry exportコマンド自体も、poetry本体からは削除されており、 別途プラグインとしてインストールしないと使えません。

注釈

Read the Docsに公開するためにrequirements.txtが必要です。 このリポジトリも、poetry-exportフックを使って、 requirements.txtを生成できるようにしてあります。

脆弱性を検出したい(Bandit)

repos:
- repo: ...
- repo: ...
- repo: https://github.com/PyCQA/bandit
  rev: 1.9.4
  hooks:
  - id: bandit
    args: ["-r", "ディレクトリ名"]

BanditはPythonコードの潜在的なセキュリティ上の問題を検出するリンターです。 shell=Trueを使ったsubprocess呼び出しなど、脆弱性につながりやすいパターンを警告してくれます。 -rオプションで、指定したディレクトリを再帰的にスキャンします。

pytestしたい

repos:
- repo: local
  hooks:
  - id: pytest
    name: pytest
    entry: uv run pytest --verbose
    language: system
    pass_filenames: false
    always_run: true
    stages: [pre-push]

pytest用の公式フックはありません。 pre-commitはローカル(local)にインストールされているコマンドを使うことができます。 プッシュ時(pre-push)にテストを走らせるとよいと思います。

entryに実行するコマンドを指定します。 uvでプロジェクトの依存関係を管理している場合、 pytestは仮想環境(.venv)の中にインストールされているはずなので、 entry: pytestではなくentry: uv run pytestのように指定してください。

pass_filenames: falseがないと、変更されたファイル名がそのままpytestの引数として渡されてしまい、うまく動かないので注意してください。 always_run: trueで、対象ファイルの変更有無にかかわらず毎回実行します。

注釈

テストはCI/CDでも実行しているかもしれません。 ローカルからのプッシュ前に確認を追加することで、パイプライン時間の無駄遣いを減らすことができます。

リファレンス