開発者ドキュメント

Gitフックを使用して開発およびデプロイタスクを自動化する方法

序章

バージョン管理は、最新のソフトウェア開発の中心的な要件になっています。 これにより、プロジェクトは変更を安全に追跡し、復帰、整合性チェック、および他の利点の中でのコラボレーションを可能にします。

「フック」システムを使用することで、gitを使用すると、開発者と管理者は、さまざまなイベントやアクションに基づいてgitが呼び出すスクリプトを指定することで機能を拡張できます。

このガイドでは、gitフックのアイデアを探求し、独自の環境でタスクを自動化するのに役立つコードを実装する方法を示します。 このガイドではUbuntu20.04サーバーを使用しますが、gitを実行できるシステムはすべて同様に機能するはずです。

前提条件

注: gitおよびgitフックの概念に既に慣れていて、実際の例に飛び込みたい場合は、「リポジトリの設定」に進んでください。

上記の要件が完了したら、続行します。

Gitフックの基本的なアイデア

Gitフックは、ニーズに対応するために実装されたかなり単純な概念です。 共有プロジェクトでソフトウェアを開発するとき、スタイルガイド標準を維持するとき、またはソフトウェアを展開するとき、アクションが実行されるたびに実行したい反復的なタスクがしばしばあります。

Gitフックはイベントベースです。 特定のgitコマンドを実行すると、ソフトウェアは hooks gitリポジトリ内のディレクトリで、実行する関連スクリプトがあるかどうかを確認します。

一部のスクリプトは、アクションが実行される前に実行されます。これを使用して、コードが標準に準拠していることを確認したり、健全性チェックを行ったり、環境をセットアップしたりできます。 他のスクリプトは、コードをデプロイしたり、正しい権限を再確立したりするためにイベントの後に実行されます(gitがうまく追跡できないもの)など。

これらの機能を使用すると、ポリシーを適用し、一貫性を確保し、環境を制御し、展開タスクを処理することもできます。

フックのカテゴリの定義

ScottChaconによる本ProGit は、さまざまなタイプのフックをカテゴリに分類しようとしています。 彼はそれらをそのように分類します:

これらの分類は、オプションでフックを設定できるイベントの概要を把握するのに役立ちます。 ただし、これらの項目がどのように機能するかを実際に理解するには、実験して、実装しようとしているソリューションを見つけるのが最善です。

パラメータでフックを見る

特定のフックもパラメーターを取ります。 これは、gitがフックのスクリプトを呼び出すときに、スクリプトがタスクを完了するために使用できるいくつかの関連データを渡すことを意味します。 完全に、利用可能なフックは次のとおりです。

フック名 呼び出される 説明 パラメータ(数と説明)
applypatch-msg git am コミットメッセージファイルを編集でき、パッチのメッセージをプロジェクトの標準に合わせて検証またはアクティブにフォーマットするためによく使用されます。 ゼロ以外の終了ステータスは、コミットを中止します。 (1)提案されたコミットメッセージを含むファイルの名前
パッチの事前適用 git am これは実際にはパッチが適用された後と呼ばれますが、変更がコミットされる前です。 ゼロ以外のステータスで終了すると、変更はコミットされていない状態のままになります。 実際に変更をコミットする前に、ツリーの状態を確認するために使用できます。 (無し)
パッチ適用後 git am このフックは、パッチが適用されてコミットされた後に実行されます。 このため、プロセスを中止することはできず、主に通知の作成に使用されます。 (無し)
事前コミット git commit このフックは、提案されたコミットメッセージを取得する前に呼び出されます。 ゼロ以外で終了すると、コミットが中止されます。 これは、(メッセージではなく)コミット自体をチェックするために使用されます。 (無し)
prepare-commit-msg git commit デフォルトのコミットメッセージを受信した後、コミットメッセージエディタを起動する直前に呼び出されます。 ゼロ以外の出口はコミットを中止します。 これは、抑制できない方法でメッセージを編集するために使用されます。 (1〜3)コミットメッセージを含むファイルの名前、コミットメッセージのソース(message, template, merge, squash、 また commit)、およびコミットSHA-1(既存のコミットで操作する場合)。
commit-msg git commit 標準への準拠を確認したり、任意の基準に基づいて拒否したりするために、編集後にメッセージを調整するために使用できます。 ゼロ以外の値で終了すると、コミットを中止できます。 (1)提案されたメッセージを保持するファイル。
コミット後 git commit 実際のコミットが行われた後に呼び出されます。 このため、コミットを中断することはできません。 これは主に通知を許可するために使用されます。 (無し)
プリリベース git rebase ブランチをリベースするときに呼び出されます。 主に、望ましくない場合にリベースを停止するために使用されます。 (1または2)分岐された場所からの上流で、ブランチがリベースされます(現在のリベース時に設定されません)
チェックアウト後 git checkoutgit clone ワークツリーの更新後または後にチェックアウトが呼び出されたときに実行します git clone. これは主に、条件の確認、相違点の表示、および必要に応じた環境の構成に使用されます。 (3)前のHEADの参照、新しいHEADの参照、ブランチチェックアウト(1)またはファイルチェックアウト(0)のどちらであったかを示すフラグ
マージ後 git merge また git pull マージ後に呼び出されます。 このため、マージを中止することはできません。 権限やgitが処理しない他の種類のデータを保存または適用するために使用できます。 (1)マージがスカッシュであったかどうかを示すフラグ。
プレプッシュ git push リモートにプッシュする前に呼び出されます。 パラメータに加えて、スペースで区切られた追加情報は、「」の形式でstdinを介して渡されます。 」。 入力を解析すると、確認に使用できる追加情報を取得できます。 たとえば、ローカルのsha1の長さが40ゼロの場合、プッシュは削除であり、リモートのsha1の長さが40ゼロの場合、それは新しいブランチです。 これを使用して、プッシュされた参照と現在存在する参照との多くの比較を行うことができます。 ゼロ以外の終了ステータスはプッシュを中止します。 (2)宛先リモートの名前、宛先リモートの場所
事前受信 git-receive-pack リモートリポジトリ上 これは、プッシュされた参照を更新する直前にリモートリポジトリで呼び出されます。 ゼロ以外のステータスはプロセスを中止します。 パラメータを受け取りませんが、「」の形式でstdinを介して文字列が渡されます。 」各参照。 (無し)
アップデート git-receive-pack リモートリポジトリ上 これは、プッシュごとに1回ではなく、プッシュされる参照ごとに1回リモートリポジトリで実行されます。 ゼロ以外のステータスはプロセスを中止します。 これを使用して、たとえば、すべてのコミットが早送りのみであることを確認できます。 (3)更新される参照の名前、古いオブジェクト名、新しいオブジェクト名
ポストレシーブ git-receive-pack リモートリポジトリ上 これは、すべての参照が更新された後にプッシュするときにリモートで実行されます。 パラメータを取りませんが、stdinを介して「 」。 更新後に呼び出されるため、プロセスを中止することはできません。 (無し)
更新後 git-receive-pack リモートリポジトリ上 これは、すべての参照がプッシュされた後に1回だけ実行されます。 その点では受信後フックに似ていますが、古い値または新しい値を受け取りません。 これは主に、プッシュされた参照の通知を実装するために使用されます。 (?)名前を含むプッシュされた各参照のパラメーター
pre-auto-gc git gc --auto リポジトリを自動的にクリーニングする前に、いくつかのチェックを行うために使用されます。 (無し)
書き換え後 git commit --amend, git-rebase これは、gitコマンドがすでにコミットされたデータを書き換えているときに呼び出されます。 パラメータに加えて、「stdin」の形式で文字列を受け取ります。 」。 (1)それを呼び出したコマンドの名前(amend また rebase)

Gitフックを使用した環境変数についてはさておき

スクリプトを開始する前に、フックを呼び出すときにgitが設定する環境変数について少し学ぶ必要があります。 スクリプトを機能させるには、最終的にgitが呼び出す環境変数の設定を解除する必要があります。 post-commit 針。

これは、信頼できる方法で機能するgitフックを作成したい場合に内部化するための非常に重要なポイントです。 Gitは、呼び出されているフックに応じて異なる環境変数を設定します。 これは、gitが情報を取得する環境がフックによって異なることを意味します。

これに関する最初の問題は、どの変数が自動的に設定されているかを知らないと、スクリプト環境が非常に予測不能になる可能性があることです。 2番目の問題は、設定された変数がgit自身のドキュメントにほとんど完全に存在しないことです。

幸い、Mark Longairは、これらのフックを実行するときにgitが設定する各変数をテストするためのメソッドを開発しました。 これには、さまざまなgitフックスクリプトに次の内容を含めることが含まれます。

#!/bin/bash
echo Running $BASH_SOURCE
set | egrep GIT
echo PWD is $PWD

彼のサイトの情報は2011年からgitバージョン1.7.1で動作しているため、いくつかの変更があります。 このガイドでは、Ubuntu20.04とgit2.25.1を使用しています。

このバージョンのgitでのテストの結果は、以下のとおりです(各フックを実行したときにgitによって表示される作業ディレクトリを含む)。 テストのローカル作業ディレクトリは /home/sammy/test_hooks そして裸のリモート(必要な場合)は /home/sammy/origin/test_hooks.git:

これらの変数は、gitがその環境をどのように見るかに影響を及ぼします。 変数に関する上記の情報を使用して、スクリプトがその環境を正しく考慮していることを確認します。

この一般的な情報がすべて揃ったので、いくつかのシナリオでこれらを実装する方法を示すことができます。

リポジトリの設定

開始するには、ホームディレクトリに新しい空のリポジトリを作成します。 あなたはこれを呼ぶことができます proj.

  1. mkdir ~/proj
  2. cd ~/proj
  3. git init
Output
Initialized empty Git repository in /home/sammy/proj/.git/

このガイドの残りの部分では、必要に応じてsammyを自分のユーザー名に置き換えます。

これで、git制御ディレクトリの空の作業ディレクトリに移動します。 他のことをする前に、と呼ばれる隠しファイルに保存されているリポジトリにジャンプします .git このディレクトリ内:

  1. cd .git
  2. ls -F
Output
branches/ config description HEAD hooks/ info/ objects/ refs/

いくつかのファイルとディレクトリが表示されます。 あなたが興味を持っているのは hooks ディレクトリ:

  1. cd hooks
  2. ls -l
Output
total 40 -rwxrwxr-x 1 sammy sammy 452 Aug 8 16:50 applypatch-msg.sample -rwxrwxr-x 1 sammy sammy 896 Aug 8 16:50 commit-msg.sample -rwxrwxr-x 1 sammy sammy 189 Aug 8 16:50 post-update.sample -rwxrwxr-x 1 sammy sammy 398 Aug 8 16:50 pre-applypatch.sample -rwxrwxr-x 1 sammy sammy 1642 Aug 8 16:50 pre-commit.sample -rwxrwxr-x 1 sammy sammy 1239 Aug 8 16:50 prepare-commit-msg.sample -rwxrwxr-x 1 sammy sammy 1352 Aug 8 16:50 pre-push.sample -rwxrwxr-x 1 sammy sammy 4898 Aug 8 16:50 pre-rebase.sample -rwxrwxr-x 1 sammy sammy 3611 Aug 8 16:50 update.sample

ここでいくつかのことがわかります。 まず、これらの各ファイルが実行可能としてマークされていることがわかります。 これらのスクリプトは名前で呼び出されるだけなので、実行可能である必要があり、正しいスクリプトインタープリターを呼び出すには、最初の行がシバンマジックナンバー参照である必要があります。 最も一般的には、これらはbash、perl、pythonなどのスクリプト言語です。

次に気付くかもしれないのは、すべてのファイルがで終わることです .sample. これは、gitが実行するフックファイルを見つけようとするときにファイル名を調べるだけだからです。 gitが探しているスクリプトの名前から外れると、基本的にスクリプトが無効になります。 このディレクトリ内のスクリプトを有効にするには、を削除する必要があります .sample サフィックス。

最初の例:コミット後のフックを使用したローカルWebサーバーへのデプロイ

最初の例では、 post-commit コミットが行われるたびにローカルWebサーバーにデプロイする方法を示すフック。 これは、実稼働環境で使用するフックではありませんが、フックを使用するときに知っておく必要のある、ほとんど文書化されていない重要な項目を示すことができます。

まず、Apache Webサーバーをインストールして、次のことを示します。

  1. sudo apt-get update
  2. sudo apt-get install apache2

スクリプトがWebルートを変更するために /var/www/html (これはUbuntu20.04のドキュメントルートです。 必要に応じて変更してください)、書き込み権限が必要です。 まず、このディレクトリの通常のユーザー所有権を付与します。 これを行うには、次のように入力します。

  1. sudo chown -R `whoami`:`id -gn` /var/www/html

次に、プロジェクトディレクトリに、 index.html ファイル:

  1. cd ~/proj
  2. nano index.html

内部には、アイデアを示すためだけにHTMLを少し追加できます。 複雑にする必要はありません。

index.html
<h1>Here is a title!</h1>

<p>Please deploy me!</p>

新しいファイルを追加して、ファイルを追跡するようにgitに指示します。

  1. git add .

さて、コミットする前にpost-commit リポジトリのフック。 このファイルを .git/hooks プロジェクトのディレクトリ:

  1. nano .git/hooks/post-commit

gitフックは標準のスクリプトであるため、シバンから始めてbashを使用するようにgitに指示する必要があります。

#!/bin/bash
unset GIT_INDEX_FILE
git --work-tree=/var/www/html --git-dir=$HOME/proj/.git checkout -f

次の行では、毎回設定される環境変数を詳しく調べる必要があります。 post-commit フックが呼び出されます。 特に、 GIT_INDEX_FILE に設定されています .git/index.

このパスは、この場合は次の作業ディレクトリに関連しています。 /var/www/html. この場所にはgitインデックスが存在しないため、そのままにしておくとスクリプトが失敗します。 この状況を回避するには、変数を手動でunsetすることができます。

その後、git自体を使用して、コミット後に最新バージョンのリポジトリをWebディレクトリに解凍します。 毎回これが成功することを確認するために、このトランザクションを強制する必要があります。

これらの変更が終了したら、ファイルを保存して閉じます。

これは通常のスクリプトファイルであるため、実行可能にする必要があります。

  1. chmod +x .git/hooks/post-commit

これで、最終的にgitリポジトリで行った変更をコミットする準備が整いました。 正しいディレクトリに戻ったことを確認してから、変更をコミットします。

  1. cd ~/proj
  2. git commit -m "here we go..."

これで、ブラウザでサーバーのドメイン名またはIPアドレスにアクセスすると、 index.html 作成したファイル:

http://server_domain_or_IP

ご覧のとおり、最新の変更は、コミット時にWebサーバーのドキュメントルートに自動的にプッシュされています。 いくつかの追加の変更を加えて、各コミットで機能することを示すことができます。

  1. echo "<p>Here is a change.</p>" >> index.html
  2. git add .
  3. git commit -m "First change"

ブラウザを更新すると、適用した新しい変更がすぐに表示されます。

ご覧のとおり、このタイプの設定により、変更をローカルでテストするのが簡単になります。 ただし、本番環境でコミット時に公開することはほとんどありません。 コードをテストし、準備ができていることを確認した後でプッシュする方がはるかに安全です。

Gitフックを使用して別の本番サーバーにデプロイする

この次の例では、運用サーバーを更新するためのより良い方法を示します。 これを行うには、push-to-deployモデルを使用して、ベアgitリポジトリにプッシュするたびにWebサーバーを更新します。 開発マシンとしてセットアップしたのと同じサーバーを使用できます。

本番マシンでは、別のWebサーバー、変更をプッシュするベアgitリポジトリ、およびプッシュが受信されるたびに実行されるgitフックをセットアップします。

sudo権限を持つ通常のユーザーとして、以下の手順を実行します。

本番サーバーの受信後フックをセットアップします

本番サーバーで、Webサーバーをインストールすることから始めます。

  1. sudo apt-get update
  2. sudo apt-get install apache2

繰り返しになりますが、操作しているユーザーにドキュメントルートの所有権を与える必要があります。

  1. sudo chown -R `whoami`:`id -gn` /var/www/html

このマシンにもgitをインストールする必要があります。

  1. sudo apt-get install git

これで、ユーザーのホームディレクトリ内にリポジトリを保持するディレクトリを作成できます。 次に、そのディレクトリに移動して、ベアリポジトリを初期化できます。 ベアリポジトリには作業ディレクトリがなく、直接作業することがあまりないサーバーに適しています。

  1. mkdir ~/proj
  2. cd ~/proj
  3. git init --bare

これはベアリポジトリであるため、作業ディレクトリはなく、通常は次の場所にあるすべてのファイルがあります。 .git 現在、メインディレクトリ自体にあります。

次に、別のgitフックを作成する必要があります。 今回は、 post-receive フックは、受信するサーバーで実行されます git push. このファイルをエディターで開きます。

  1. nano hooks/post-receive

繰り返しますが、あなたはあなたが書いているスクリプトのタイプを特定することから始める必要があります。 その後、で使用したのと同じチェックアウトコマンドを入力できます post-commit このマシンのパスを使用するように変更されたファイル:

#!/bin/bash
while read oldrev newrev ref
do
if [[ $ref =~ .*/master$ ]];
then
echo "Master ref received.  Deploying master branch to production..."
git --work-tree=/var/www/html --git-dir=$HOME/proj checkout -f
else
echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."
fi
done

これはベアリポジトリであるため、 --git-dir そのリポジトリの最上位ディレクトリを指す必要があります。

ただし、このスクリプトにロジックを追加する必要があります。 誤って押した場合 test-feature このサーバーへのブランチでは、それをデプロイする必要はありません。 を展開するだけであることを確認する必要があります master ブランチ。

まず、標準入力を読み取る必要があります。 プッシュされる参照ごとに、3つの情報(古いrev、新しいrev、ref)が、標準入力として空白で区切られてスクリプトに送られます。 あなたはこれを読むことができます while を囲むループ git 指図。

これで、プッシュされているものに基づいて3つの変数が設定されます。 マスターブランチプッシュの場合、 ref オブジェクトには、次のようなものが含まれます refs/heads/master. サーバーが受信している参照がこの形式であるかどうかを確認するには、 if 構築します。

最後に、検出された状況と実行されたアクションを説明するテキストを追加します。 追加する必要があります else アクションがデプロイメントをトリガーしない場合でも、マスター以外のブランチが正常に受信されたときにユーザーに通知するためのブロック。

終了したら、ファイルを保存して閉じます。 ただし、フックを機能させるには、スクリプトを実行可能にする必要があることを忘れないでください。

  1. chmod +x hooks/post-receive

これで、クライアントでこのリモートサーバーへのアクセスを設定できます。

クライアントマシンでリモートサーバーを構成する

クライアント(開発)マシンに戻り、プロジェクトの作業ディレクトリに戻ります。

  1. cd ~/proj

内部で、リモートサーバーをと呼ばれるリモートとして追加します production. 入力するコマンドは次のようになります。

  1. git remote add production sammy@remote_server_domain_or_IP:proj

次に、現在のマスターブランチを本番サーバーにプッシュします。

  1. git push production master

SSHキーが設定されていない場合は、運用サーバーユーザーのパスワードを入力する必要がある場合があります。 次のようなものが表示されます。

Output]
Counting objects: 8, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 473 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) remote: Master ref received. Deploying master branch... To sammy@107.170.14.32:proj 009183f..f1b9027 master -> master

あなたが見ることができるように、あなたからのテキスト post-receive フックはコマンドの出力にあります。 Webブラウザで本番サーバーのドメイン名またはIPアドレスにアクセスすると、プロジェクトの現在のバージョンが表示されます。

情報を受け取ったら、フックがコードを正常に本番環境にプッシュしたようです。

それでは、新しいコードをテストしてみましょう。 開発マシンに戻り、変更を保持するための新しいブランチを作成します。 と呼ばれる新しいブランチを作成します test_feature 次のように入力して、新しいブランチを確認します。

  1. git checkout -b test_feature

あなたは今で働いています test_feature ブランチ。 可能性のある本番環境に移行する可能性のある変更を加えてみてください。 このブランチにコミットします。

  1. echo "<h2>New Feature Here</h2>" >> index.html
  2. git add .
  3. git commit -m "Trying out new feature"

この時点で、開発マシンのIPアドレスまたはドメイン名に移動すると、変更が表示されます。

これは、各コミットで開発マシンがまだ再デプロイされているためです。 このワークフローは、変更を本番環境に移行する前にテストするのに最適です。

あなたはあなたを押すことができます test_feature リモート本番サーバーへのブランチ:

  1. git push production test_feature

あなたはあなたからの他のメッセージを見るはずです post-receive 出力をフックします:

Output
Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 301 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Ref refs/heads/test_feature successfully received. Doing nothing: only the master branch may be deployed on this server To sammy@107.170.14.32:proj 83e9dc4..5617b50 test_feature -> test_feature

ブラウザで本番サーバーを再度チェックアウトすると、何も変更されていないことがわかります。 プッシュした変更はマスターブランチになかったので、これはあなたが期待することです。

開発マシンで変更をテストしたので、この機能をマスターブランチに組み込むことができます。 あなたはあなたのチェックアウトすることができます master ブランチしてマージします test_feature 開発マシンのブランチ:

  1. git checkout master
  2. git merge test_feature

これで、新しい機能がマスターブランチにマージされました。 本番サーバーにプッシュすると、変更がデプロイされます。

  1. git push production master

本番サーバーのドメイン名またはIPアドレスを確認すると、変更内容が表示されます。

このワークフローを使用すると、コミットされた変更をすぐに表示する開発マシンを作成できます。 マスターブランチをプッシュするたびに、実稼働マシンが更新されます。

結論

ここまで進んだら、gitフックがいくつかのタスクを自動化するのに役立つさまざまな方法を見ることができるはずです。 これらは、コードをデプロイしたり、不適合な変更を拒否したりメッセージをコミットしたりすることで品質基準を維持するのに役立ちます。

gitフックの有用性について議論するのは難しいですが、実際の実装を把握するのはかなり難しく、トラブルシューティングするのにイライラする可能性があります。 さまざまな構成の実装の練習、引数と標準入力の解析の実験、およびgitがフックの環境を構築する方法を追跡することは、効果的なフックの作成方法を教えるのに大いに役立ちます。 長期的には、時間の投資は通常それだけの価値があります。プロジェクトの全期間を通じて、あなたとあなたのチームの手作業を簡単に節約できるからです。

gitを使用してプロジェクトに貢献するには、オープンソースに貢献する方法:Git入門を確認してください。 または、gitのその他の使用方法に興味がある場合は、 Gitの使用方法:リファレンスガイドをお試しください。

モバイルバージョンを終了