Subversion post-commit

FreeBSD サーバ beatrice 復旧がやっと終わったと思ったら,バージョン管理システム Subversion の commit がエラーになってしまった。サーバに Subversion のリポジトリがあり,クライアント PC から自作プログラムや Web ページのバージョン管理を行っている。Web ページについては commit (新バージョンの登録) 操作を行うと,Subversion の post-commit スクリプト(commit の延長で実行される Subversion の付加スクリプト。これを改造して独自処理を追加できる)により,新しい HTML を Web 公開ディレクトリにコピーし,namazu 検索インデックスを更新する,という一連の更新作業を自動的に行うようになっている。

エラーはこの実行環境を復旧し忘れていたためだった。このリカバリには少し悩んだ。自分で組込んだのにその仕組みを完全に忘却してしまっていた。メモを残しておかないとこういうハメになる。そういうことで,改めてここで記しておくことにする。

自動更新の手続きは次のようなものである。

  1. post-commit スクリプトは Web ページ更新シェルスクリプト webupdate スクリプト(自作)を呼び出す。
  2. webupdate は作業ユーザ user のホームディレクトリにある Subversion 管理下の Web サイトソースにおいて,svn update を実行し,新しい版を取り出す。
  3. webupdate は,前回 Web 更新実行時刻のタイムスタンプをもつタイムスタンプファイルよりも新しいファイル群だけを纏めて一時アーカイブファイルを作成する。
  4. webupdate は Apache22 ドキュメントルートにおいて一時アーカイブファイルを解凍し,公開 Web ドキュメントツリーを更新する。
  5. webupdate はタイムスタンプファイルを現在の時刻で更新する(touch コマンドによる)。
  6. webupdate は namazu 検索インデックス更新スクリプト mkwebidx(自作)を呼び出す。
  7. mkwebidx は,前回インデックス更新実行時刻のタイムスタンプをもつタイムスタンプファイルよりも新しく,かつインデックス作成対象のファイル群だけを,namazu インデックス更新用作業領域にコピーする。
  8. mkwebidx は,コピーしたファイルのうち UTF-8 エンコード文書を EUC-JP にコード変換する(namazu が EUC-JP 前提のため)。
  9. mkwebidx は namazu 検索インデックス更新プログラム mknmz コマンドで検索インデックスを更新する。
  10. mkwebidx はタイムスタンプファイルを現在の時刻で更新する(touch コマンドによる)。

この手続きは Web 環境で実行されるため,ユーザ権限は www のパーミションである。しかし,一連の操作において user, root 権限が必要なオペレーションがあり,www がこれらに成り代わる仕掛けが必要になる。もちろん関連するリソースすべてを www の所有とすればよいのだけれども,その他運用事情で www 専用とするわけにも行かなかった。このため,スクリプト内で対話操作を実現するために expect を利用する。www ユーザが expect コマンドで他のユーザでシステムにログインしてその権限の必要なオペレーションを実行するわけである。expect は FreeBSD ports /usr/ports/lang/expectmake install clean を行い組込んでおく。

ただし,このためには www のアカウント設定を通常の nologin からログイン可能ユーザに変更しておかなくてはならない。www アカウント設定(vipw で編集)を以下のようにした。

www:パスワード:80:80::0:0:World Wide Web Owner:/nonexistent:/bin/tcsh

/nonexistent はもともとダミーなんだけど,www オーナで同名ディレクトリを作成しておく。これで一度,www ユーザとして userslogin しておき,ssh が通るようにしておく。

beatrice:/home/user % su -
xxxxxxx
# mkdir -p /nonexistent
# chown -R www:www /nonexistent
# exit
beatrice:/home/user % su - www
xxxxxxx
> slogin user@beatrice 
The authenticity of host 'beatrice (192.168.1.4)' can't be established.
RSA key fingerprint is 62:35:a2:d5:e6:20:8f:91:6e:ce:e6:d2:a4:2b:5c:a7.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'beatrice,192.168.1.4' (RSA) to the list of known hosts.
Password: xxxxxx
beatrice:/home/user % 

post-commit スクリプトは,Web ページソースの Subversion リポジトリが /usr/local/var/svn/website/ だとすると,/usr/local/var/svn/website/hooks/ 下に格納する。

以下に post-commit を示す。Subversion によって,引数としてリポジトリディレクトリ名とリビジョンが渡されて起動される。このスクリプトは /var/log/svnupdate.log に実行ログを出力するようになっているので,あらかじめ www オーナ属性でこのファイルを touch しておく。

#!/bin/sh
REPOS="$1"
REV="$2"
LOG=/var/log/svnupdate.log
/bin/echo "*** post-commit STARTED at `/bin/date` $REPOS $REV" >> $LOG
/home/user/bin/webupdate >> $LOG
/bin/echo "*** post-commit ENDED at `/bin/date` $REPOS $REV" >> $LOG

以下に,順次実行される webupdate, mkwebidx の両スクリプトも掲載しておく。

#!/bin/sh
#
#   webupdate
#
#   * HTML install for Beatrice
#   * will be called by post-commit of Subversion
#   Copyright (c) 1998-2011, isao yasuda, All Rights Reserved.
#
DATADIR="/home/user/src/noxinsomniae"
STAMP="$DATADIR/stamp"
EXPECT="/usr/local/bin/expect"
echo "*** Web install start at `date` ***"
# 新しい HTML の取り出し
$EXPECT -c '
    set timeout -1
    spawn slogin user@beatrice
    expect "Password:" 
    send "xxxxxx\r"
    expect "% " 
    send "cd /home/user/src/noxinsomniae\r"
    expect "% " 
    send "/usr/local/bin/svn update\r"
    expect "% " 
    puts "End"
    exit
    '
# 更新ページのアーカイブ
cd $DATADIR
echo "*** $DATADIR archiving & Beatrice installing ..."
find -L . \( -newer $STAMP \! -name .svn \! -name prop-base \! \
    -name text-base \! -name props \! -name tmp \! -name all-wcprops \
    \! -name entries \! -name "*svn-base*" -type f \) | \
    xargs tar zcf /tmp/webarc.tar.gz
echo "*** archive gen done. *** "
ls -las /tmp/webarc.tar.gz
# 公開 Web ツリーへの展開
$EXPECT -c '
    set timeout -1
    spawn slogin user@beatrice
    expect "Password:" 
    send "xxxxxx\r"
    expect "% " 
    send "su\r"
    expect "Password:" 
    send "xxxxxx\r"
    expect "# "
    send "tar zxvf /tmp/webarc.tar.gz -C /usr/local/www/apache22/data\r"
    expect "# " 
    puts "End"
    exit
    '
echo "*** extraction done. ***"
rm -f /tmp/webarc.tar.gz
echo "*** removed archive. ***"
echo "*** Web install ended at `date` ***"
# タイムスタンプの更新
touch $STAMP
#
# namazu 検索インデックスの更新
#
echo "*** Search index generation ***"
$EXPECT -c '
    set timeout -1
    spawn slogin user@beatrice
    expect "Password:" 
    send "xxxxxx\r"
    expect "% " 
    send "su\r"
    expect "Password:" 
    send "xxxxxx\r"
    expect "# "
    send "/home/user/bin/mkwebidx\r"
    expect "# " 
    puts "End"
    exit
    '
echo "*** done. ***"
 
#!/bin/sh
#
#   mkwebidx
#
#   * namazu Web 検索インデックス更新
#   Copyright (c) 1998-2011, isao yasuda, All Rights Reserved.
#
LANG=ja_JP.eucJP
export LANG
SRCDIR=/usr/local/www/apache22/data
TMPIDX=/tmp/webidx
MKNMZRC=/usr/local/etc/namazu/web/mkwebidxrc
IDXDIR=/usr/local/var/namazu/index/web
MKNMZ=/usr/local/bin/mknmz
STAMP=/tmp/webidx/stamp
LIST=/tmp/UTF8LIST
if [ `whoami` != "root" ]; then
    echo "Invoke as a superuser, `whoami`."
    exit
fi
# 更新ページの抽出
cd $SRCDIR
find -L ./*.html ./bbs ./dl ./izhltndoc ./japlit ./lan \
    ./oldslav ./profile ./pushkin ./rus2 ./russify ./slavonic \
    ./tex ./misima ./cyr ./usconcord ./concordance  \
    \( -newer $STAMP \) -and \
    \( -name "*.html" -or -name "*.shtml" -or -name \
    "*.pdf" -or -name "*.txt" \) | \
    xargs tar cf - | \
    ( cd $TMPIDX; tar xvf - )
# UTF-8 to EUC-JP コード変換
find -L ./*.html ./bbs ./dl ./izhltndoc ./japlit ./lan \
    ./oldslav ./profile ./pushkin ./rus2 ./russify ./slavonic \
    ./tex ./misima ./cyr ./usconcord ./concordance \
    \( -newer $STAMP \) -and \
    \( -name "*.html" -or -name "*.shtml" \) | \
    xargs grep -H -e "charset=[Uu][Tt][Ff]-8" | \
    cut -d : -f 1 > $LIST
for i in `cat $LIST`
do
    iconv -c -f UTF-8 -t EUC-JP $i | \
    sed 's/charset=[Uu][Tt][Ff]-8/charset=EUC-JP/g' > $TMPIDX/$i
done
# インデックス更新
$MKNMZ -f $MKNMZRC -a -O $IDXDIR -k $TMPIDX
# タイムスタンプの更新
touch $STAMP
rm -f $LIST
echo "*** done. ***"