2026年7月: pre-commit互換のGitフック管理フレームワーク「prek」の紹介(ryu22e)¶
筒井@ryu22eです。 今月の『Python Monthly Topics』は、Gitフックを管理するフレームワーク「prek」を紹介します。prekは、Pythonプログラマーの間で人気があるpre-commitのRust実装です。
prekの使い方はpre-commitとほぼ同じですが、本記事ではprek/pre-commitをどちらも使ったことがない人向けに使い方を丁寧に解説します。 また、pre-commitから移行したい人のために、最後に移行方法についても解説します。
なお、本記事の内容はprek 0.4.8(執筆時点での最新版)を前提にしています。
なぜprek/pre-commitを使うか¶
ここでは、prek/pre-commitを使うと便利な場面について説明します。
Gitには「Gitフック」という機能があります。Gitフックは特定の操作の前後にカスタムスクリプト(シェルスクリプト)を実行して操作内容に問題がないかを検証してくれます。 たとえば、コミットの直前にRuffのようなPythonリンターを実行して、Ruffが問題を検出したらコミットを中断させることができます。
Gitフックに関する詳細は、以下の公式ドキュメントを参照してください。
https://git-scm.com/book/ja/v2/Git-のカスタマイズ-Git-フック
Gitフックの作り方について説明します。 Gitリポジトリには、ディレクトリ.git/hooks/以下に拡張子sampleのファイルがいくつかあります。
$ mkdir git-example # 専用のディレクトリを作成
$ cd git-example
$ git init
Initialized empty Git repository in /****/git-example/.git/
$ ls .git/hooks/ # 拡張子sampleのファイルが複数ある
applypatch-msg.sample post-update.sample pre-merge-commit.sample pre-receive.sample sendemail-validate.sample
commit-msg.sample pre-applypatch.sample pre-push.sample prepare-commit-msg.sample update.sample
fsmonitor-watchman.sample pre-commit.sample pre-rebase.sample push-to-checkout.sample
これらのファイルの拡張子のsampleを取ると、Gitはそのファイルをカスタムスクリプトとして認識します。 各ファイルの用途については以下の公式ドキュメントを参照してください。
https://git-scm.com/docs/githooks
なお、.git/hooks/以下に置いたカスタムスクリプトは、Gitのコミット対象になりません。
Gitフックは、誤操作を未然に防ぐなどの利点がある一方で、以下のような問題もあります。
シェルスクリプトなので書きにくい
複数のプロジェクトで共通のカスタムスクリプトを作る場合の管理が難しい
カスタムスクリプトの中でPython、Ruby、Node.jsなどの言語に依存するツールを呼び出す際は、スクリプトファイルの設置とは別に初期化処理が必要な場合がある
prek/pre-commitは、上記の問題を解決するために作られたフレームワークです。prek/pre-commitは、フック名が書かれた設定ファイルを元にGitのカスタムスクリプトを作成してくれます。カスタムスクリプトの修正が必要な場合も該当ファイルを一つ一つ編集する必要はありません。生成元のフックの定義を修正し、カスタムスクリプトを再作成するだけで済みます。また、実行に必要な初期化処理も自動的に行なってくれます。たとえば、カスタムスクリプトでPythonのツールを呼び出すのであれば、仮想環境の作成、ツールのインストールも自動化されます。
prekとpre-commitの違い¶
prekはpre-commitより後発のフレームワークです。 pre-commitはPython製のため、実行にPythonが必要ですが、prekはRustで実装しています。 Rustはシングルバイナリとしてコンパイルされるため、ランタイムを別途インストールする必要がありません。 また、prekはpre-commitと互換性があります。 詳細は後述しますが、pre-commitの設定ファイルをそのまま使えるので、移行は容易です。 以下の有名リポジトリでもpre-commitからprekに移行しています。
prekの主な特徴は以下のとおりです。 prekはpre-commitより後発だけあって、性能面、機能面でさまざまな改善点があります。
ビルトインフックをRustで実装して高速化
ディスク容量の使用効率を改善
フックの並列実行を可能に
prekのインストール方法¶
prekのインストール方法は複数提供されています。 以下で代表的な例を紹介します。
$ # uvを使う場合(公式の推奨)
$ uv tool install prek
$ # pipを使う場合
$ pip install prek
$ # Homebrewを使う場合
$ brew install prek
その他のインストール方法は、以下の公式ドキュメントを参照してください。
prekの使い方¶
ここでは、prekの主なコマンドと使い方について説明します。 本記事で紹介している以外の内容については、以下の公式ドキュメントを参照してください。
https://prek.j178.dev/reference/cli/
prek sample-configで初期設定を行う¶
prekの設定ファイルはYAML形式の.pre-commit-config.yaml(pre-commitでサポートする形式)、またはTOML形式のprek.tomlです。
本記事では、prek.tomlを採用する前提で解説します。
prek sample-configコマンドで設定ファイルのサンプルの生成ができます。
--formatオプションでファイル形式(yamlまたはtoml)、-fオプションで出力先ファイルを指定できます。
以下の手順で、prek.tomlを含むGitリポジトリを作成してみましょう。
$ mkdir prek-example # 専用のディレクトリを作成
$ cd prek-example
$ git init # まずGitリポジトリの初期化
Initialized empty Git repository in /***/prek-example/.git/
$ prek sample-config --format toml -f prek.toml # サンプルをprek.tomlに出力
Written to `prek.toml`
$ cat prek.toml
# Configuration file for `prek`, a git hook framework written in Rust.
# See https://prek.j178.dev for more information.
#:schema https://www.schemastore.org/prek.json
[[repos]]
repo = "builtin"
hooks = [
{ id = "trailing-whitespace" },
{ id = "end-of-file-fixer" },
{ id = "check-added-large-files" },
]
$ git add prek.toml # prek.tomlをGit管理対象に入れる
$ git commit -am "add prek.toml"
[main (root-commit) 45fbeae] add prek.toml 1 file changed, 11 insertions(+) create mode 100644 prek.toml
prek.tomlのrepo = "builtin"以下に書かれているのが、prekが標準で提供しているビルトインフックです。それぞれ、以下の機能があります。
フックID |
説明 |
|---|---|
行末にスペースがあるとエラーになる(自動的に除去も行う) |
|
ファイル末尾に改行がないとエラーになる(自動的に改行付与も行う) |
|
ファイルサイズが大きい(デフォルトは500KB)ファイルをコミットしようとするとエラーになる |
argsにオプション引数を渡すことでフックの挙動を変更できます。
たとえば、check-added-large-filesでチェックする最大ファイルサイズを1024KBに変更する場合は、以下のように書きます。
check-added-large-fileでチェックする最大ファイルサイズを1024KBに変更¶[[repos]]
repo = "builtin"
hooks = [
{ id = "trailing-whitespace" },
{ id = "end-of-file-fixer" },
{
id = "check-added-large-files",
args = ["--maxkb=1024"] # これを追加
},
]
複数のフックを並列実行したい場合は、フックの実行優先順位を表すpriorityオプションを指定します。priorityは値が小さいほど優先的に実行され、同じ値のフックは並列実行になります。
以下の例では、trailing-whitespace、end-of-file-fixerを並列実行するようpriorityを設定しています。
priorityの設定例¶[[repos]]
repo = "builtin"
hooks = [
# trailing-whitespace、end-of-file-fixerは並列実行
{ id = "trailing-whitespace", priority = 0 },
{ id = "end-of-file-fixer", priority = 0 },
# 上記の後にcheck-added-large-filesを実行
{ id = "check-added-large-files", priority = 1 },
]
なお、priorityを指定しない場合は上のフックから順に実行されます。
その他のビルトインフックについては以下の公式ドキュメントを参照してください。
https://prek.j178.dev/builtin/#supported-hooks_1
また、ビルトインフック以外にサードパーティのフックも利用できます。 サードパーティフックはpre-commit用のフックをそのまま使えます。 詳細は、以下のpre-commit公式ドキュメントで紹介しているサードパーティフックを参照してください。
prek installでGitフックを作成¶
prek installコマンドを実行すると、prek.tomlを元にGitフックを作成します。
prek installコマンドの実行例¶$ prek install
prek installed at `.git/hooks/pre-commit`
$ cat .git/hooks/pre-commit # このGitフックが作成される
#!/bin/sh
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
HERE="$(cd "$(dirname "$0")" && pwd)"
PREK="/Users/ryu22e/.local/bin/prek"
# Check if the full path to prek is executable, otherwise fallback to PATH
if [ ! -x "$PREK" ]; then
PREK="prek"
fi
exec "$PREK" hook-impl --hook-dir "$HERE" --script-version 4 --hook-type=pre-commit -- "$@"
デフォルトでは、コミット前に実行されるプレコミットフックのみが作成されます。
他のフックも作成したい場合は、prek.tomlにdefault_install_hook_typesを追加します。
たとえば、プレコミットフック(pre-commit)に加えてプレプッシュフック(pre-push)も加えたい場合は、以下のように書きます。
default_install_hook_typesの設定例¶default_install_hook_types = [
"pre-commit",
"pre-push"
]
[[repos]]
# (省略)
default_install_hook_typesでサポートする値は、以下の公式ドキュメントを参照してください。
prek uninstallでGitフックを削除¶
prek installコマンドで作成したGitフックを削除するには prek uninstallコマンドを実行します。
一時的にGitフックを無効にしたい場合にこれを使います。
prek uninstallコマンドの実行例¶$ prek uninstall
Uninstalled `pre-commit`
$ prek install # 後述する手順を実行するために再度Gitフックを作成
prek installed at `.git/hooks/pre-commit`
変更をコミットしてGitフックを実行してみる¶
行末にスペースを入れたファイルを追加して、git commitを実行してみましょう。
プレコミットフックの中でprekが初期化処理を行ってから、prek.tomlに定義されたフックを実行します。
この例では、trailing-whitespaceでエラーを検出して、コミットに失敗します。
$ echo "行末にスペースを入れる→ " > test.txt
$ git add test.txt
$ git commit -am "add test.txt"
trim trailing whitespace
.................................................
Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook
Fixing test.txt fix end of files
.........................................................
Passed
check for added large files
..............................................
Passed
実行時にファイルを自動修正してくれるので、2度目のコミットは成功します。
$ git commit -am "add test.txt"
trim trailing whitespace
.................................................
Passed
fix end of files
.........................................................
Passed
check for added large files
..............................................
Passed
[main 6098a84] add test.txt 1 file changed, 1 insertion(+) create mode 100644 test.txt
プレコミットフックは、git commitでコミット対象になるファイルに対してのみ実行されます。
すでにコミットされたファイルに対しては何もしません。
後からprekを導入した場合にこの仕様が問題になる場合があります。
今までコミットしたファイルも含めてチェックを実行したい場合は、次に紹介するprek runコマンドに-aまたは--all-filesオプションをつけて実行します。
prek runでフックを手動実行する¶
prek runコマンドはgit commitコマンドでコミット対象になるファイルに対してGitフックと同じチェック処理を実行します。
prek runコマンドの実行例¶$ echo "行末にスペースを入れる→ " > test2.txt
$ git add test2.txt
$ prek run
trim trailing whitespace
# (省略)
Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook
Fixing test2.txt fix end of files
# (省略)
$ git status # 行末のスペースが除去されているので、さらに差分が生まれている
On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage)
new file: test2.txt
Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)
modified: test2.txt
$ git add test2.txt
$ prek run # 行末のスペースが除去されているので、今度はエラーにならない
# (省略)
Passed
$ git commit -am "add test2.txt"
# (省略)
[main db1c5a9] add test2.txt
1 file changed, 1 insertion(+)
create mode 100644 test2.txt
また、--filesオプションでファイル名を指定すればgit addでコミット対象に入れていないファイルもチェックできます。
prek run --filesコマンドの実行例¶$ echo "行末にスペースを入れる→ " > test3.txt
$ prek run --files test3.txt # --filesオプションでファイル名を指定すればgit addでコミット対象に入れていないファイルもチェックできる
trim trailing whitespace
# (省略)
Failed
- hook id: trailing-whitespace
- exit code: 1
Fixing test3.txt fix end of files
# (省略)
$ git add test3.txt
$ git commit -am "add test3.txt"
# (省略)
[main 81f8e14] add test3.txt
1 file changed, 1 insertion(+)
create mode 100644 test3.txt
-aまたは--all-filesオプションを付けると、すでにコミットされているファイルも対象にしてチェック処理を実行します。
以下の例では、prek uninstallコマンドで一旦Gitフックを無効にしてから行末にスペースが入ったファイル(trailing-whitespaceフックでエラーになる)をコミットし、その後prek run -aコマンドを実行しています。
prek run -aコマンドの実行例¶$ prek uninstall # 一旦Gitフックを無効化
Uninstalled `pre-commit`
$ echo "行末にスペースを入れる→ " > test4.txt # trailing-whitespaceフックでエラーになるファイル
$ git add test4.txt
$ git commit -am "add test4.txt" # Gitフックを無効化しているのでコミットできる
$ prek install # Gitフックを再び有効化
$ prek run -a # すでにコミットしたファイルも対象にチェックを実行し、test4.txtがエラーになる
trim trailing whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook
Fixing test4.txt
fix end of files.........................................................Passed
check for added large files..............................................Passed
-aまたは--all-filesオプションは以下のような場合に役立ちます。
後からprekを導入する場合(既存のファイルにルール違反のファイルがないかチェックできる)
GitHub ActionsなどのCI上で実行する(Gitフックのチェックを通さずコミットしたファイルをここでチェックできる)
GitHub Actionsで実行する場合は、j178/prek-actionというアクションが提供されています。
.github/workflows/の下に以下のようなYAMLファイルを置くと、GitHub Actions上でprek run -aコマンドを実行してくれます。
j178/prek-actionの使用例¶name: Prek checks
on: [push, pull_request]
jobs:
prek:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: j178/prek-action@v2 # ここでprek run -aを実行
prek updateでフックのリビジョンを更新する¶
prekでは、サードパーティのフックはリビジョン番号の指定(rev)が必須のため、必ずリビジョン番号を固定化して使います。
ただ、カスタムスクリプトから呼ぶツールを最新版にしたい、既存の不具合を修正したリビジョンを使いたい、などの理由からリビジョン番号を更新したい場合もあります。
そんな時役立つのがprek updateコマンドです。
prek updateコマンドは、prek.tomlに書かれたフックのリビジョン番号を更新してくれます。
以下は、prek.tomlにサードパーティのフックastral-sh/ruff-pre-commitのv0.15.18(最新リビジョンではない)を指定している例です。
revに書かれているのがフックのリビジョンです。
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.15.18" # これがフックのリビジョン
hooks = [
{ id = "ruff-check" },
{ id = "ruff-format" }
]
カレントディレクトリに上記のprek.tomlファイルがある状態でprek updateコマンドを実行すると、revに書かれたリビジョンを最新版に更新してくれます。
prek updateコマンドの実行例¶$ prek update
https://github.com/astral-sh/ruff-pre-commit
updating rev `v0.15.18` -> `v0.15.20`
$ cat prek.toml # prek.tomlの現在の内容を確認
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.15.20" # これがフックのリビジョン
hooks = [
{ id = "ruff-check" },
{ id = "ruff-format" }
]
次回のGitフック実行時には、更新後のリビジョンのフックをダウンロードしてチェックを実行してくれます。
なお、v0.15.20は本記事執筆時点でのastral-sh/ruff-pre-commitの最新リビジョンです。 実行タイミングによっては結果が異なる場合があります。
【コラム】--cooldown-daysオプションを指定してprek updateコマンドを安全に使う¶
prek updateコマンドの--cooldown-daysオプションには、更新対象のリビジョンになるために必要なリリース後の経過日数を指定できます。
以下は--cooldown-daysに5を指定した場合の実行例です。
最新リビジョンのv0.15.20ではなく、一つ前のリビジョンv0.15.19に更新されています。
--cooldown-daysオプションをつけてprek updateを実行¶$ prek update --cooldown-days 5
https://github.com/astral-sh/ruff-pre-commit
updating rev `v0.15.18` -> `v0.15.19`
なお、このオプションはコマンド実行日を起点として各リビジョンのリリース経過日数を計算しています。 実行する日によって上記とは違うリビジョン番号になる可能性があります。
--cooldown-daysオプションを毎回指定するのが面倒な場合は、prek.tomlにupdate.cooldown_daysを書いておく方法もあります。
update.cooldown_daysに指定した値は、--cooldown-daysオプションのデフォルト値になります。
update.cooldown_daysの記述例¶# (省略)
[update]
cooldown_days = 7
--cooldown-daysオプションが役立つ場面について説明します。
--cooldown-daysオプションは、昨今話題の「サプライチェーン攻撃」への対策として有効です。
prek/pre-commitのフックは、公開の仕組み上サプライチェーン攻撃を受けやすいため、利用者側での対策が必要です。
prek/pre-commitのフックは新リビジョンをリリースした際、第三者が安全性(マルウェアなどが仕込まれていないか)をチェックしてから公開するプロセスにはなっていません。 つまり、以下のような事態が発生するリスクがあります。
攻撃者がフック開発者のPCやGitHubアカウントを乗っ取る
攻撃者が不正なコードを仕込んだフックを新リビジョンとしてリリースする
(第三者のチェックを経由せず)不正なコードを含んだ新リビジョンのフックが公開される
利用者が不正なコードを含んだ最新リビジョンをダウンロードし、Gitフック実行時に攻撃を受ける
上記リスクの緩和策として、--cooldown-daysオプションを指定して、リリース後ある程度の日数が経過したフックのみを利用する運用方針が効果的です。
prek/pre-commitのフックに限らず、リリース後のOSSは多くの人たちによって検証されています。
検証に参加する人の中には、セキュリティベンダーなどのセキュリティ専門家もいます。
そのため、リリース後しばらく時間をおくことでマルウェアなどが仕込まれている可能性が低くなり、安全性を高めることができます。
AIエージェントのスキルとして使う¶
prekはAIエージェントからprekを呼ぶためのSKILL.mdを作成する機能もあります。 SKILL.mdはAIエージェントに「prekとは何か」「prekの使い方」を伝え、prekを適切に使うように指示できます。
この機能はGitHub CLIと組み合わせて使います。
gh skill install j178/prek prekコマンドを実行すると、SKILL.mdが作成されます。
gh skill install j178/prek prekコマンドの実行例¶$ mkdir prek-skill # 専用のディレクトリを作成
$ cd prek-skill
$ git init
Initialized empty Git repository in /****/prek-skill/.git/
$ gh skill install j178/prek prek
Using ref v0.4.5 (bb310828)
! Skills are not verified by GitHub and may contain prompt injections, hidden instructions, or malicious scripts. Always review skill contents before use.
? Select target agent(s): [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]
> [x] GitHub Copilot
# agentは「GitHub Copilot」、Installation scopeは「install in current repository (recommended)」を選択
# (省略)
✓ Installed prek (from j178/prek@v0.4.5) in .agents/skills
prek/
└── SKILL.md
! Skills may contain prompt injections or malicious scripts.
Review installed content before use:
gh skill preview j178/prek prek@bb3108283fd4aab0d9eb59f23098e0c05a96cdbe
$ ls .agents/skills/prek # ここにSKILL.mdが作成される
SKILL.md
$ git add .agents
$ git commit -am "add prek skill"
[main (root-commit) b2b91af] add prek skill
1 file changed, 219 insertions(+)
create mode 100644 .agents/skills/prek/SKILL.md
以下は、GitHub Copilot Chat拡張をインストールしたVisual Studio Codeから前述のスキルを呼び出している画面です。
プロンプトはastral-sh/ruff-pre-commitフックのv0.15.20を入れたprek.tomlを作成してからprek installを実行し、prek.tomlをコミットしてください。です。
pre-commitからの移行方法¶
すでにpre-commitを導入しているプロジェクトでprekを導入する方法について解説します。
prekはpre-commitの設定ファイル.pre-commit-config.yamlをサポートします。 そのため、pre-commitコマンドの呼び出しをprekコマンドの呼び出しに置き換えるだけで移行できます。
pre-commitとprekのサブコマンドはほぼ同じです(一部、名前やオプションが異なるものがあります)。 prekとpre-commitのサブコマンドの対比表は以下のとおりです。
prekサブコマンド |
pre-commitサブコマンド |
|---|---|
また、prek.tomlを使いたい場合は、.pre-commit-config.yamlをprek.tomlに変換するprek util yaml-to-tomlコマンドが提供されています。
以下がprek util yaml-to-tomlコマンドの使用例です。
まずprek sample-configコマンドで.pre-commit-config.yamlを作成してから、prek util yaml-to-tomlコマンドで.pre-commit-config.yamlをprek.tomlに変換しています。
prek util yaml-to-tomlコマンドの使用例¶$ mkdir yaml-to-toml-example # 専用のディレクトリを作成
$ cd yaml-to-toml-example
$ prek sample-config -f .pre-commit-config.yaml # まず.pre-commit-config.yamlを作成
$ cat .pre-commit-config.yaml # .pre-commit-config.yamlの内容を確認
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
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-yaml
- id: check-added-large-files
$ prek util yaml-to-toml # .pre-commit-config.yamlをprek.tomlに変換
Converted `.pre-commit-config.yaml` → `prek.toml`
$ rm .pre-commit-config.yaml # .pre-commit-config.yamlは不要になるので消す
$ cat prek.toml # prek.tomlの内容を確認
# Configuration file for `prek`, a git hook framework written in Rust.
# See https://prek.j178.dev for more information.
#:schema https://www.schemastore.org/prek.json
[[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-yaml" },
{ id = "check-added-large-files" }
]
最後に¶
Python製のpre-commitはPythonがない環境では動かないため、Python以外で開発するプロジェクトに導入しにくいと感じることが個人的にありました。 シングルバイナリで動くpre-commitのような類似プロダクトはないものか、と数年前からぼんやり願っていたのですが、そのものズバリのものが登場していたのですね。
pre-commitを愛用している人はもちろん、はじめて知った人もぜひ使ってみてください!