チームでGit Hooksのスクリプトを共有する

DevOps

はじめに

本記事では、バージョン管理システムGitの強力な機能であるGit Hooksの使用方法を解説します。特に、開発チーム全体で一貫したGit Hooksを共有する方法と、自動的にコードの品質チェックやテストを行うためのスクリプトの作成方法について詳しく説明します。

Git Hooksとは

Git Hooksは、Gitのデフォルトの機能の一つで、git commitやgit pushなどのコマンドが実行される際に特定のスクリプトを実行できるようにするものです。このスクリプトの実行結果によって、そのコマンドを通すかどうかを自動的に判断させることができます。実行されるスクリプトファイルは.git/hooksディレクトリにあり、特定のイベントごとに異なるスクリプトが呼び出されます。デフォルトの状態では拡張子が.sampleになっています。.sample拡張子を削除すれば、該当のGit操作が行われたときにスクリプトが自動的に実行されます。

Git Hooksの共有

GitHooksのデフォルトの参照先は.git/hooksです。このディレクトリは通常、Gitのバージョン管理対象外であるためチームで共有することはできません。
共有する方法の一つとして、本記事では.githooksディレクトリをリポジトリのルートに作成し、そこにGit Hooksスクリプトを配置します。そしてGitの設定を変更して、GitがHooksスクリプトを探すデフォルトのパスを.git/hooksから.githooksに変更します。

$ mkdir .githooks
$ touch .githooks/pre-commit
$ git config --local core.hooksPath .githooks

現在の参照先を確認するには以下のコマンドを使用します。

$ git config --local --list
...(省略)
core.hookspath=.githooks
...

この設定を行うことで、プロジェクトメンバー全員が同じGit Hooksスクリプトを使用できるようになります。
元の設定に戻すには以下のコマンドを使用します。

$ git config --local core.hooksPath .git/hooks

続いて、作成した各Hooksスクリプトファイルに実行権を付与します。

$ chmod a+x .githooks/*

Git Hooksスクリプトの例

Pre-Commit Hook

Pre-commitフックは、コミットが作成される前にトリガーされます。一般的には、このフックを利用してコードスタイルのチェック、テストの実行、リントチェックなど、コードの品質を保証するためのチェックを行います。

以下に、PythonのコードをPEP8に準拠しているかチェックするスクリプトの例を示します。

#!/bin/sh

FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '.py$')

if [ -n "$FILES" ]; then
    flake8 $FILES
    if [ $? -ne 0 ]; then
        echo "PEP8 check failed, commit denied"
        exit 1
    fi
fi

exit 0

このスクリプトは、ステージングされたPythonファイル(.py)を取得し、それらのファイルに対してflake8(Pythonのリントツール)を実行します。もしflake8がエラーを返した場合(つまり、PEP8に準拠していないコードが存在した場合)、スクリプトはエラーメッセージを出力し、非ゼロの終了コードを返してコミットを中断します。

以下、コマンドの詳細です。

  • git diff --cached --name-only --diff-filter=ACM: このコマンドはステージングエリアの変更を表示します。オプションの詳細は以下の通り
    • --cached:ステージングエリアにある変更を表示します。
    • --name-only:変更されたファイルの名前だけを表示します。
    • --diff-filter=ACM:’A’(追加されたファイル), ‘C’(コピーされたファイル), ‘M’(修正されたファイル)のみを表示します。
  • grep '.py$':このコマンドは入力行の中で、末尾が .py であるものを抽出します。つまり、Pythonのソースコードファイルを抽出します。

以下に、未解決のマージコンフリクトが存在するかどうかをチェックするスクリプトの例を示します。

#!/bin/sh

# Check for unresolved merge conflicts
conflicts=$(git diff --cached --name-only -G"<<<<<|=====|>>>>>")

# If conflicts exist, print the conflicted files and exit with a non-zero status code
if [[ -n "$conflicts" ]]; then
    echo "Please resolve the following unresolved merge conflicts:"
    for conflict in $conflicts; do
        echo "Conflict in file: $conflict"
    done;
    exit 1;
fi

# If no conflicts, exit successfully
exit 0

このスクリプトはまず、マージコンフリクトのマーカー(<<<<<<<、=======、および>>>>>>>)が含まれるステージングされたファイルのリストを取得します。次に、そのリストが空でない場合(つまり、未解決のマージコンフリクトが存在する場合)、それらのファイル名を出力し、スクリプトは終了ステータス1で終了します。

Pre-Push Hook

Pre-pushフックは、git pushコマンドが実行される前にトリガーされます。一般的には、このフックを利用してリモートリポジトリにpushする前の最終チェックを行います。以下に、pre-pushフックで全ての単体テストを実行するスクリプトの例を示します。

#!/bin/sh

echo "Running unit tests..."

pytest

if [ $? -ne 0 ]; then
    echo "Tests must pass before push!"
    exit 1
fi

exit 0

このスクリプトは、全ての単体テストを実行します(この例では pytest コマンドを使用)。もしテストが失敗した場合、スクリプトはエラーメッセージを出力し、非ゼロの終了コードを返してpushを中断します。

おわりに

本記事では、Git Hooksをチーム全体で共有し、一定のルールとして使用するための手順を説明しました。この方法を使用すれば、チーム全体で一貫した開発プロセスを実現し、より効率的な開発を可能にすることができます。
この記事がどなたかの参考になれば幸いです。

参考

コメント