Webサイトをgithubで管理してpush時に自動的に同期する方法

Webサーバに Subversion のサーバを立てておき、HTML や CSS を commit することでWebサイトを更新する方法は、良く知られているテクニック、らしいですね*1。更新の履歴を残すことができるし、ましてチマチマとFTPやsftpでアップロードするよりずっと簡単です。


しかし SVN の代わりに git を使おうとすると、pushしてもリポートリポジトリではファイルを更新してくれません。
また、リポジトリはWebサーバ上に作るよりも、便利な管理インタフェースがある github(や噂のgitosis)に置いておきたいところです。


そこで、github の Post-Receive Hook を使うと、リポジトリに変更を push すると同時に、Webサーバにも同期させることができます*2

Webサーバに同期する前に、Sphinxでドキュメントを整形したり、SassをCSSに変換したり、あるいはJavaのソースからJavaDocを生成したりと、色々な前処理を走らせることもできます。


流れは下図のようになります。Post-Recieve Hook を設定しておくと、リポジトリに変更が push されたときに指定しておいたURLに POST リクエストを送信してくれます。この POST リクエストを受け取ったタイミングで github から変更を pull し、色々な前処理を実行した後に、Webサーバにアップロードすれば完了です。


http://help.github.com/post-receive-hooks/


実際に、msgpack.org ではこの方法を使ってWebサイトを管理しています。

サーバスクリプト

まず、github から Post-Recive Hookを受け取って、同期スクリプトを起動する簡単なWebサーバを用意します。
POSTされるデータには、payload というパラメータ名でJSON形式のデータが含まれています。

RubySinatra を使って書くと↓こうなります。

#!/usr/bin/env ruby
require 'sinatra'
require 'json'

here = File.dirname(__FILE__)
SYNC_SCRIPT = "#{here}/update-website.sh"

post '/' do
   begin
      push = JSON.parse(params[:payload])
      system(SYNC_SCRIPT)
      "ok."
   rescue
      "error."
   end
end

同期スクリプト

次に、githubから変更をpullしてWebサーバに同期するスクリプトを用意します。
同期する方法は、同期先のWebサーバで使える方法を選択してください。ここでは ssh + rsync を使ってみます:

  • update-website.sh
#!/bin/sh
tmpdir=/home/viver/gitsync/work/msgpack-website
repo=git://github.com/msgpack/website.git
rsyncto=viver@example.com:htdocs/
rsync='rsync -e "ssh -i ~/.ssh/id_rsa_nopass" -vrtl --delete'

if [ -d "$tmpdir" ];then
    cd "$tmpdir"
    if git pull; then
        $rsync ./ "$rsyncto"
        exit 0
    fi
fi

dirname=`dirname "$tmpdir"`
basename=`basename "$tmpdir"`

mkdir -p "$dirname"
cd "$dirname"
rm -rf "$basename"

if git clone "$repo" "$basename"; then
    cd "$basename"
    $rsync ./ "$rsyncto"
fi

githubの設定

最後に、githubで Post-Receive Hook を発行するURLを設定します。
リポジトリのトップページで [スパナアイコン] Admin ボタンをクリックし、サイドバーから Service Hook を選んで、Post-Receive URLs にサーバスクリプトへのURLを入力してください。


http://help.github.com/post-receive-hooks/

Service Hookの用途

Webサイトにコンテンツを同期する代わりに、push時にテストケースを走らせたり、新しいtagが作成されたタイミングでリリースビルドを作成するなど、発想次第で色々なことができそうです。

面倒な作業はどんどん自動化してみてください^^;

*1:自分ではやったことがない^^;

*2:gitosisや生のgitリポジトリでも、post-receiveスクリプトを使って同じことが(もっと簡単に)できるはずです。