さくらのVPS を改めて使いはじめる 11 - Git、Gitolite、GitHub その 2
さくらのVPS(v3) 2GB プランへの環境構築メモ 11。今回は Git 関連の続き。Gitolite と GitHub の push をフックする方法などについて書く。
Gitolite への push をフックする
前回 Gitolite をセットアップしてリポジトリを Redmine へ表示するところまで書いた。しかしそのままでは Gitolite の push がミラーリングされたリポジトリに反映されず内容は古いままとなる。この問題への対処方法は 2 通りある。
- ミラーリングされたリポジトリ上で
git fetch
を定期実行 - Gitolite 上で push を検知して、ミラーリング先リポジトリに対して
git push --mirror
を実行
方法 1 なら crontab を利用することになるだろう。たとえば 10 分間隔ぐらいで git fetch
させるなど。しかし push が全くないときでも無駄なコマンド実行が発生するうえ実行されるまで push が反映されないためイマイチ。
そのため今回は方法 2 で検討する。話の前提として前回の記事により以下のような環境が構築されているものとする。
リポジトリ | ユーザー:グループ | 説明 |
---|---|---|
/home/gituser/repositories/testing.git | gituser:gituser | Gitolite のテスト用リポジトリ。 |
/var/lib/git/testing.git | XXXX:XXXX | Gitolite のテスト用リポジトリに対するミラーリング リポジトリ。 |
XXXX
というのは普段 SSH 接続でログインしているユーザーとグループになる。例えば test というユーザーとグループなら test:test
のようになる。
ミラーリング リポジトリの設定
ミラーリングされたリポジトリを参照したいユーザーが XXXX
なら gpasswd
コマンドで gituser グループに追加する。所属しているグループは id コマンドで確認。リポジトリを Redmine 上のプロジェクトに関連づけるなら Redmine 実行ユーザーを指定すること。このシリーズにならって環境構築していた場合は SSH 接続でログインしているユーザーと一緒のはず。
$ sudo gpasswd -a XXXX gituser
Adding user XXXX to group gituser
$ id XXXX
uid=500(XXXX) gid=500(XXXX) 所属グループ=500(XXXX),10(wheel),501(gituser)
ミラーリングしたリポジトリの所有者とグループを gituser、権限を 755
(グループに読み取りを許可) に変更。
$ sudo chown -R gituser:gituser /var/lib/git/testing.git/
$ sudo chmod -R 755 /var/lib/git/testing.git/
この時点で gituser グループに所属するユーザー全員が /var/lib/git/testing.git/
以下の内容を読み取れるようになる。
このリポジトリを Redmine プロジェクトに関連づけているなら、そちらの表示も確認すること。もしリポジトリ画面で 404 になる場合はグループやパーミッションの設定が Redmine の稼働ユーザーからアクセス不能になった可能性がある。
ミラーリング元からこのリポジトリに対して git push --mirror
が実行された場合、testing.git/objects
などにディレクトリが追加されるのだがパーミッションは 700
になる。つまり所有者しかアクセスできない。というわけで以下の記事を参考に config ファイルの設定を変更する。
ミラーリング リポジトリ直下に移動して git config
コマンドで core
セクションの sharedRepository
に group
を指定。
$ cd /var/lib/git/testing.git
$ sudo git config core.sharedRepository group
設定が反映されたことを確認。
$ git config --list
core.repositoryformatversion=0
core.filemode=true
core.bare=true
core.sharedrepository=group
remote.origin.fetch=+refs/*:refs/*
remote.origin.mirror=true
remote.origin.url=/home/gituser/repositories/testing.git
ばっちり反映されている。これで Gitolite からの push を受け入れる体制が整った。
Gitolite の push をフックしてミラーに push
Git フックを利用して Gitolite 上のリポジトリに push がおこなわれたらミラーリング リポジトリ側にも反映されるようにする。
はじめに hook 時のパーミッション設定を変更。gituser の HOME にある .gitolite.rc の REPO_UMASK を編集する。gituser になってからファイルを開く。
$ sudo su gituser
$ cd
$ vi .gitolite.rc
REPO_UMASK を設定している部分を探す。デフォルトのパーミッションは 0077 になっているので元をコメントアウト、 0027 の設定を追加してからファイルを保存する。この作業は一度だけ実施すればよい。
#$REPO_UMASK = 0077;
$REPO_UMASK = 0027;
gituser の状態で testing.git 内の hooks ディレクトリに post-receive というファイルを新規作成する。
$ sudo su - gituser
$ cd repositories/testing.git/hooks
$ vi post-receive
このファイルにはリポジトリに対する操作に応じた処理をシェル スクリプトとして記述できる。今回はミラーリング リポジトリとなる /var/lib/git/testing.git に変更を git push --mirror
する処理を書いて保存。
#!/bin/sh
/usr/bin/git push --mirror /var/lib/git/testing.git
スクリプト ファイルなので実行属性を付ける。
$ chmod 700 post-receive
正しく設定できているなら testing.git へ push が行われるたびにミラー側へも変更が反映されるはず。
GitHub への push をフックして VPS に送信
GitHub 上のリポジトリに変更がおこなわれた時は Post-Receive Hooks でハンドリングできる。今回はこの機能を利用してリポジトリへの push 時に VPS へ更新を通知してみる。
GitHub のクローン リポジトリ
前回は GitHub から clone したリポジトリに対して Redmine の動作ユーザー (= SSH ログインしているユーザー) を所有者に設定した。今回は Gitolite 用のミラーリング リポジトリの設定でこのユーザーを gituser
グループに所属させる。
今後は Git 系の操作を gituser
または gituser
グループに統一。GitHub に Test というリポジトリがあると仮定して、以下のようにクローンする。
$ cd /var/lib/git
$ sudo git clone --mirror git://github.com/akabekobeko/Test.git
$ sudo chown -R gituser:gituser Test.git
$ sudo chmod -R 755 Test.git/
これで gituser
グループに所属するユーザーでもリポジトリを読めるようになる。ただし git fetch
などの更新系は gituser
のみに許可する。
PHP 実行ユーザーに sudo を許可
今回 GitHub からの更新通知による VPS 上のリポジトリ反映は PHP スクリプトでおこなう予定である。反映は git fetch
コマンドで実行するのだが、これを PHP から呼び出す方法としては以下が考えられる。
- suEXEC を有効にしてスクリプトの実行ユーザーを git 操作可能なユーザーにする
- PHP の実行ユーザーに
sudo
を許可し、その-u
オプションでユーザー指定する
前者は設定が面倒なうえスクリプトや CGI 全体の実行にかかわってくるので後者を採用する。
本シリーズのsudo と SSH ポート変更の回で取り上げた visudo
コマンドを実行。
$ sudo visudo
すると vi
で sudoers ファイルが開かれるので Defaults requiretty
の行をコメントアウトする。これが有効だとシェルにログインできないユーザーは sudo
を利用出来ない。PHP 実行ユーザーとなる apache
はこれに該当するので設定を無効にする。
セキュリティ的には好ましくないのだが VPS 全体として SSH ログインを必須にし、かつそのユーザーを限定しているのでよしとしておく。
#
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear.
# You have to run "ssh -t hostname sudo <cmd>".
#
#Defaults requiretty
次に apache
ユーザーへ sudo
を許可。といっても代替ユーザーは gituser
、実行可能なコマンドは git
に限定しておく。この設定は wheel
グループの後ろあたりに記述。
## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL
## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL
apache ALL=(gituser) NOPASSWD: /usr/bin/git
設定できたらファイルを保存して編集を終了。
更新通知時に実行するスクリプト
GitHub の clone リポジトリと PHP 実行環境が整ったので GitHub からの更新通知を処理するスクリプトを書く。GitHub からの変更通知は POST 形式で送信される。これを VPS 上で受信したときにスクリプトが実行されるように設定しておく。
Git リポジトリ ルート直下に scripts というディレクトリを作成、この中にスクリプトを置く。スクリプトの言語は何でもよいのだが今回は PHP を選んでみた。ファイル名は github-post-receive.php としておく。
$ cd /var/lib/git/
$ sudo mkdir scripts
$ sudo chown gituser:gituser scripts
$ cd scripts
$ vi github-post-receive.php
ファイルを以下のように記述して保存する。
<?php
$githubIPs = array( "207.97.227.253", "50.57.128.197", "108.171.174.178" );
$errorStatus = "HTTP/1.1 500 Internal Server Error";
if( !in_array( $_SERVER[ "REMOTE_ADDR" ], $githubIPs ) )
{
header( $errorStatus );
die;
}
$payload = json_decode( $_REQUEST[ "payload" ] );
if( $payload == NULL )
{
header( $errorStatus );
die;
}
if( !chdir( "/var/lib/git/" . $payload->repository->name . ".git" ) )
{
header( $errorStatus );
die;
}
shell_exec( "sudo -u gituser git fetch" );
?>
GitHub からのアクセスであることをチェックする。GitHub が POST に使用する IP アドレスは固定なのでそれを判定。IP アドレスは GitHub リポジトリの WebHook URLs 設定画面に記載されているものを指定すること。
json_decode
により POST のリクエストボディに含まれる payload パラメータの JSON をデコード。この中には push 時の様々な情報 ( ユーザー、リポジトリ名、...etc ) が格納されている。続けて JSON 内のリポジトリ名を元に clone 先へ移動し、gituser として git fetch を実行している。
スクリプトが完成したら所有者とグループを apache にして Web に公開する。いたずらにアクセスされると厄介なので、シンボリックリンクには UUID を指定し URL を類推されにくくしておく。
$ sudo chown apache:apache github-post-receive.php
$ sudo chmod 644 github-post-receive.php
$ uuidgen
a2267948-9f75-46ba-85df-058199cdfb7d
$ sudo ln -s /var/lib/git/scripts /var/www/html/a2267948-9f75-46ba-85df-058199cdfb7d
UUID は uuidgen
コマンドで生成。コマンドを実行すると UUID が出力されるのでそれをコピーしてリンク名に使用。このだとスクリプトへの URL は以下のようになる。
http://ホスト名/a2267948-9f75-46ba-85df-058199cdfb7d/github-post-receive.php
Web ブラウザーからアクセスすると真っ白なページが表示される。Firebug などで HTTP ステータスを確認すると 500 エラーになるはず。
GitHub リポジトリの設定
スクリプトが用意できたので GitHub 側から呼び出せる用にする。GitHub にログインして push をフックしたいリポジトリの設定画面を開く。そして上段にある Admin ボタンを押す。
左側のメニューから「Service Hooks」→「WebHook URLs」を選択。
画面右側に URL の入力欄があるので push 時に呼び出すものを指定。URL を入力したら、Update Settings ボタンを押して設定を保存する。
保存に成功すると、以下のような画面が表示される。
スクリプトが正しく実行されることを確認するため GitHub リポジトリへ何度か push してみる。以下はスクリプトによって更新された VPS 上のリポジトリを Redmine で表示してみたところ。
...今回はここまで。次回は phpMyAdmin あたりを取り上げるかもしれない。