DUICUO

Gitシリーズ(パート6):独自のGitサーバーを設定する方法

[[172092]]

ここでは、Git サーバーをセットアップする方法、特定のイベントで対応するアクション (通知など) をトリガーするカスタム Git フックを作成する方法、またはコードをサイトに公開する方法について学習します。

これまでは、ユーザーとしてGitを操作する方法について主に議論してきました。この記事では、Gitの管理と柔軟なGitフレームワークの設計について解説します。これは「高度なGitテクニック」や「熱心な愛好家向け」の婉曲表現のように聞こえるかもしれませんが、実際には、ここで説明する各タスクは、Gitの仕組みを理解するために深い知識や特別なトレーニングを必要としません。Linuxに関するちょっとした知識があれば十分でしょう。

共有Gitサーバー

独自の共有Gitサーバーを作成するのは驚くほど簡単で、多くの場合、そのちょっとした手間は十分に価値があります。独自のコードへのアクセスが保証されるだけでなく、個人用Gitフック、独自のデータストレージ、継続的インテグレーションとディストリビューション(CI & CD)など、Gitのより幅広い用途への扉が開かれます。

GitとSSHの使い方がわかれば、Gitサーバーの作成方法もすでにご存知でしょう。Gitは、リポジトリを作成またはクローンするだけでサーバー設定の半分が完了するように設計されています。リポジトリへのSSHアクセスが許可されると、必要な権限を持つユーザーは誰でも、そのリポジトリを新しいリポジトリのクローンのベースとして利用できるようになります。

ただし、これは小規模なアドホック環境です。特定のスキームに従えば、同じ機能を持ちながらもスケーラビリティに優れた、適切に設計されたGitサーバーを構築できます。

まず第一に、現在のユーザーと将来のユーザーの両方を特定してください。ユーザーが1人だけであれば、変更の必要はありません。ただし、他のコード貢献者にも利用してもらいたい場合は、開発者専用の共有システムを用意する必要があります。

稼働中のサーバーをお持ちの場合(そうでなくても大丈夫です。Gitで対応できますし、CentOS Raspberry Pi 3 は良い出発点です)、最初のステップはSSHログインをSSHキー認証のみで許可することです。これはブルートフォース攻撃を回避できるだけでなく、ユーザーキーを削除するだけでユーザーを無効化できるため、パスワードログインよりもはるかに安全です。

SSHキー認証を有効にしたら、gituserユーザーを作成します。これは、すべての承認済みユーザーのためのパブリックユーザーです。

  1. $ su -c 'adduser gituser'  

次に、先ほど作成したgituserユーザーに切り替え、~/.sshファイルを作成し、適切な権限を設定します。これは重要です。権限を広く設定しすぎると、保護されたSSHアクセスが意味をなさなくなります。

  1. $ su - gituser
  2. $ mkdir .ssh && chmod 700 .ssh
  3. $ .ssh/authorized_keys をタッチします
  4. $ chmod 600 .ssh/authorized_keys

`authorized_keys` ファイルには、すべての開発者の SSH 公開鍵が含まれており、これにより開発者は Git プロジェクトで作業する権限を付与されます。開発者はそれぞれ独自の SSH 鍵ペアを作成し、公開鍵をあなたに渡す必要があります。公開鍵を `gituser` ユーザーの `authorized_keys` ファイルにコピーします。例えば、Bob という名前の開発者の場合は、以下のコマンドを実行します。

  1. $ cat ~/path/から/id_rsa.bob.pub >> /home/gituser/.ssh/authorized_keys

開発者 Bob が秘密鍵を所有し、対応する公開鍵を提供している限り、Bob は gituser ユーザーを使用してサーバーにアクセスできます。

しかし、開発者にはgituserユーザーであってもサーバーへのアクセスは許可したくないでしょう。Gitリポジトリへのアクセスのみを許可したいのです。このため、Gitはgit-shellという制限付きシェルを提供しています。以下のコマンドをroot権限で実行し、git-shellをシステムに追加してgituserユーザーのデフォルトシェルとして設定してください。

  1. # grep git-shell /etc/shells || su -c "echo `which git-shell` >> /etc/shells"  
  2. # su -c 'usermod -s git-shell gituser'  

現在、gituser ユーザーは Git リポジトリのプッシュとプルに SSH のみ使用でき、他のアクセス可能なシェルは使用できません。gituser と同じグループ(サンプルサーバーでは gituser という名前)に自身を追加する必要があります。

例えば:

  1. # usermod -a -G gituser seth

残るステップはGitリポジトリの作成だけです。サーバー上のGitは誰も直接操作できないため(つまり、SSHでサーバーに接続してこのリポジトリを直接操作することはできません)、空のリポジトリを作成してください。サーバー上のこのリポジトリを使って作業を完了したい場合は、元の場所からクローンを作成し、ホームディレクトリで作業してください。

厳密に言えば、この空のリポジトリを必ずしも作成する必要はありません。通常のリポジトリと同じように機能します。ただし、空のリポジトリには作業ブランチが存在しません(つまり、`checkout` を実行してもブランチが表示されません)。これは、リモートユーザーが有効なブランチにプッシュするのを防ぐため重要です(`dev` ブランチで作業中に、誰かが突然あなたの作業ブランチに変更をプッシュしたらどう感じるでしょうか?)。空のリポジトリには有効なブランチが存在しない可能性があるため、これは問題になりません。

このリポジトリは、ユーザーとユーザーグループに作業権限を付与する限り、どこにでも配置できます。ただし、ユーザーのホームディレクトリなどには厳格な権限制限があるため、絶対に保存しないでください。/opt や /usr/local/share などの通常の共有ディレクトリに保存してください。

ルートとして空のリポジトリを作成します。

  1. # git init --bare /opt/jupiter.git  
  2. # chown -R gituser:gituser /opt/jupiter.git
  3. # chmod -R 770 /opt/jupiter.git

これで、gituserとして認証されているユーザー、またはgituserグループに所属するユーザーは、jupiter.gitリポジトリの読み書きができるようになりました。ローカルマシンで以下のコマンドを試してみてください。

  1. $ git clone [email protected]:/opt/jupiter.git jupiter.clone
  2. クローン  「jupiter.clone」 ...
  3. 警告:空のリポジトリを複製したようです。

重要: 開発者は、gituser ユーザーの authorized_keys ファイルに SSH 公開鍵を追加する必要があります。また、サーバー上にユーザーがいる場合 (ユーザーに権限を与えた場合)、そのユーザーは gituser ユーザー グループに属している必要があります。

Gitフック

独自のGitサーバーを運用する最大のメリットの一つは、Gitフックを利用できることです。Gitホスティングサービスの中にはフッククラスへのインターフェースを提供しているものもありますが、ファイルシステムにアクセスするためのGitフックを実際に提供しているわけではありません。Gitフックとは、Gitプロセスの特定の時点で実行されるスクリプトです。リポジトリがコミットを受信する直前、コミットを受信した直後、プッシュを受信する直前、プッシュを受信した直後など、様々なタイミングでフックを実行できます。

これはシンプルなシステムです。`.git/hooks` ディレクトリに標準的な命名規則に従って配置されたスクリプトは、指定された時間に実行されます。スクリプトが実行されるかどうかは、スクリプトの名前によって決まります。pre-push スクリプトはプッシュ前に実行され、post-receive スクリプトはコミットが承認された後に実行されます。これは、名前からある程度明らかです。

スクリプトは任意の言語で記述できます。システムに実行可能なスクリプト言語(例えば「hello world」を出力するもの)がある場合は、その言語でGitフックスクリプトを記述できます。Gitにはデフォルトでいくつかのサンプルスクリプトが付属していますが、有効化されていません。

試してみませんか?簡単です。Gitリポジトリをお持ちでない場合は、まず作成してください。

  1. $ mkdir 木星
  2. $ cd ジュピター
  3. `$ git init .`

次に、「hello world」Gitフックを記述します。私はレガシーシステムでtschを使用しているため、スクリプト言語としてtschを今でも使用しています。お好みの言語(Bash、Python、Ruby、Perl、Rust、Swift、Go)を自由にお使いください。

  1. $ echo "#\!/bin/tcsh" > .git/hooks/post-コミット 
  2. $ echo "echo 'POST-COMMIT SCRIPT TRIGGERED'" >> ~/jupiter/.git/hooks/post- commit  
  3. $ chmod +x ~/jupiter/.git/hooks/post-コミット 

次に出力をテストします。

  1. $ echo "hello world" > foo.txt
  2. git でfoo.txtを追加する
  3. $ git commit -m '最初のコミット'  
  4. ! コミット後スクリプトがトリガーされました
  5. [マスター (ルートコミット) c8678e0]最初 専念 
  6. 1 つのファイルが変更され、1 つの挿入(+)
  7. 作成モード 100644 foo.txt

これで、最初の機能的な Git フックが完成しました。

有名なpush-to-webフック

Gitフックの目的は、変更されたコードを本番環境対応のウェブサーバー上のディレクトリに自動的にプッシュすることです。これは、FTPを使わずに製品全体のバージョン管理を維持し、コンテンツ公開を統合・自動化するのに最適な方法です。

正しく実行すれば、ウェブサイトのデプロイメントは以前と同じようにスムーズに、そしてある意味非常に正確に進むでしょう。Gitは本当に素晴らしいです。誰が最初にこのアイデアを思いついたのかは分かりませんが、私が初めてGitについて知ったのは、EmacsとGitの専門家であるIBMのBill von Hagen氏でした。彼の記事には、このプロセスに関する権威ある入門書が掲載されています。「Gitは分散型ウェブ開発のゲームチェンジャーです。」

Git変数

各Gitフックには、フックをトリガーする様々なGitの動作に対応する一連の異なる変数があります。これらの変数が必要かどうかは、主にプログラムによって異なります。誰かがコードをプッシュした際に一般的なメール通知を受け取るだけであれば、特別な設定は必要ありません。また、追加のスクリプトを作成する必要もありません。なぜなら、ニーズに合ったサンプルスクリプトが既に用意されているからです。コミット情報とコミット作成者をメールに表示したい場合は、スクリプトは比較的複雑になります。

Gitフックはユーザーが直接実行するものではないため、混乱を招く可能性のある情報をどのように収集するかを理解することが重要になります。実際、Gitフックスクリプトは、BASH、Python、C++などの他のスクリプトと同様に、標準入力から引数を読み取ります。違いは、標準入力を提供していないことです。そのため、Gitフックを使用する際には、考えられる入力引数を知っておく必要があります。

Gitフックを書く前に、プロジェクトの`.git/hooks`ディレクトリにあるGitが提供するサンプルをいくつか見てみましょう。例えば、この`pre-push.sample`ファイルのコメントセクションでは、次のような説明がされています。

  1. # $1 -- プッシュするリモートリポジトリの名前 
  2. # $2 -- プッシュ先のリモートリポジトリのURL  
  3. # プッシュ時に名前付きリモート リポジトリが存在しない場合は、これら 2 つのパラメータは同じになります。
  4. #
  5. 送信された情報は、次の形式で行ごとに標準入力に送信されます。
  6. # <ローカル参照> <ローカルsha1> < リモート参照> < リモート sha1>

すべての例がこのように明確というわけではなく、変数を取得するためのフックに関するドキュメントも不足しています(Gitのソースコードを読む限り)。しかし、疑問がある場合は、オンラインで他のユーザーの試みを参考にしたり、`echo $1, $2, $3` などの基本的なスクリプトを書いてみるのも良いでしょう。

分岐検出の例

本番環境ではよくあるニーズがあります。特定のブランチが変更された後にのみイベントをトリガーするフックです。以下はブランチを追跡する方法の例です。

まず、Gitフック自体はバージョン管理されていません。Gitは自身のフックを追跡しません。これは、フックはGitの一部であり、リポジトリの一部ではないためです。そのため、Gitフックはローカルリポジトリではなく、Gitサーバー上の空のリポジトリのコミットとプッシュの記録を監視できます。

post-receiveフック(コミットが承認された後に実行されるフック)を記述してみましょう。まず、ブランチ名を決定します。

  1. #!/bin/tcsh
  2. foreach 引数 ( $< )
  3. argv = ( $arg )を設定します
  4. refname = $1を設定します
  5. 終わり 

このforループは、最初のパラメータ$1を読み取り、それを2番目のパラメータ$2で上書きし、3番目のパラメータ$3で上書きします。Bashでは、より適切な方法があります。`read`コマンドを使用して値を配列に格納します。ただし、これはtcshであり、変数の順序は予測可能なので、この方法も有効です。

コミット レコードの refname を取得したら、Git を使用してこのブランチの人間が読める名前を見つけることができます。

  1. ブランチを `git rev-parse --symbolic --abbrev-ref $refname`に設定します 
  2. $branch #DEBUG をエコーし​​ます

次に、このブランチ名を、トリガーするイベントのブランチ名キーワードと比較します。

  1. if ( "$branch" == "master" ) then  
  2. echo "ブランチが検出されました: master"  
  3. git \
  4. --work-tree=コピー先のパス\  
  5. checkout -f $branch || echo "master fail"  
  6. そうでない場合 ( "$branch" == "dev" ) then  
  7. echo "ブランチが検出されました: dev"  
  8. ギット\
  9. --work-tree=コピー先のパス\  
  10. checkout -f $branch || echo "dev fail"  
  11. それ以外 
  12. echo "プッシュは成功しました。"  
  13. echo "プライベートブランチが検出されました。アクションはトリガーされませんでした。"  
  14. 終了

このスクリプトに実行権限を付与します。

  1. $ chmod +x ~/jupiter/.git/hooks/post-receive

これで、ユーザーがサーバーのmasterブランチにコミットすると、そのコードは本番環境のディレクトリにコピーされ、devブランチにコミットすると、別の場所にコピーされます。他のブランチではこれらの操作は実行されません。

同時に、コミット前のスクリプトの作成も非常に簡単です。例えば、ユーザーがプッシュすべきでないブランチにコードをプッシュしていないか確認したり、コミットメッセージを解析したりするのに使用できます。

Gitフックは複雑で、Gitワークフローの抽象化レベルが異なるため、理解しにくい場合があります。しかし、GitフックはGitインフラストラクチャ内のあらゆる動作に対して対応するアクションを実行できる強力なシステムです。Gitを頻繁に使用するユーザーやフルタイムのGit管理者であれば、Gitフックを習得する価値があります。Gitフックを真に使いこなすには、まずそのプロセスに慣れる必要があります。

このシリーズの次の最初の記事では、Git を使用してオーディオや画像などのテキスト以外のバイナリ データを管理する方法を学びます。