Subversionのフック・スクリプトでコミット前にRedmineのチケットを確認

RedmineのチケットとSubversionのリビジョンの連携を徹底するために、コミット時にチケット番号の指定を強制するようにした。

Subversionのフックとは?*1

  • 特定のタイミングで特定の外部プログラムを実行することであり、そのプログラムはフック・スクリプトと言われる。
  • 外部プログラムを実行する条件は次の通り。
    • リポジトリの hooks ディレクトリに決められたファイル名で存在する。
    • そのファイルが実行可能。フック・スクリプトと言っているがバイナリの実行ファイルでも OK 。 linux の場合は実行パーミッションがセットされている必要がある。
    • ユーザにそのファイルを実行する権限がある。
  • 実行可能なファイル(コミット関連)
    • start-commit ファイル
    • pre-commit ファイル
    • post-commit ファイル
      • コミットされ、新しいリビジョンが作られた後に実行。
      • 第一引数 : リポジトリへのパス
      • 第二引数 : 今回作られたリビジョン番号
      • 終了コードは無視される。

コミットに制約を設ける

制約内容:

  1. 参照用(refs)のチケット番号が指定されていて、そのチケットがRedmimeに存在すること。
  2. 完了用(fixes)のチケット番号が指定されていた場合は、そのチケットがRedmineに存在して、かつ、完了していないこと。
  • pre-commitの内容
#!/bin/sh

REPOS="$1"
TXN="$2"

# Make sure that the log message contains some text.
SVNLOOK=/usr/bin/svnlook
#$SVNLOOK log -t "$TXN" "$REPOS" | \
#   grep "[a-zA-Z0-9]" > /dev/null || exit 1

#参照用のチケット番号の確認
$SVNLOOK log -t "$TXN" "$REPOS" | \
grep -i -E "refs[ ]*[#]*[0-9]+" > /dev/null || \
{ echo "No refs ticket number.(refs #)" 1>&2 && exit 1; }

#チケット進捗状態確認の関数
getTicketRatio()
{
mysql -s -u root redmine <<__REDMINE__
select
    issue_statuses.default_done_ratio
from
    issues 
,   issue_statuses
where
    issues.id = ${1}
and issues.status_id = issue_statuses.id
; 
__REDMINE__
} 

#参照用のチケット番号の取り出し
TICKET_NO=`$SVNLOOK log -t "$TXN" "$REPOS" | sed -e "s/.*refs[ ]\+[#]*\([0-9]\+\).*/\1/i" | grep -i -E "^[0-9]+$"`
if [ "NULL" == ${TICKET_NO:-NULL} ]; then
  echo "No refs ticket number.(refs #)" 1>&2
  exit 1
else
  #チケット進捗状態確認
  TICKET_RATIO=`getTicketRatio ${TICKET_NO}`

  #進捗状態が確認できない場合はコミット不可
  if [ "NULL" == ${TICKET_RATIO:-NULL} ]; then
    echo "Ticket #${TICKET_NO} is nothing." 1>&2
    exit 1
  fi 
fi

#完了用のチケット番号の取り出し
TICKET_NO=`$SVNLOOK log -t "$TXN" "$REPOS" | sed -e "s/.*fixes[ ]\+[#]*\([0-9]\+\).*/\1/i" | grep -i -E "^[0-9]+$"`
if [ "NULL" != ${TICKET_NO:-NULL} ]; then
  #チケット進捗状態確認
  TICKET_RATIO=`getTicketRatio ${TICKET_NO}`

  #100%の時はコミット不可
  if [ "NULL" == ${TICKET_RATIO:-NULL} ]; then
    echo "Ticket #${TICKET_NO} is nothing." 1>&2
    exit 1
  elif [ 100 -eq ${TICKET_RATIO} ]; then
    echo "Ticket #${TICKET_NO} is complite." 1>&2
    exit 1
  fi
fi

# Check that the author of this commit has the rights to perform
# the commit on the files and directories being modified.
#commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1

# All checks passed, so allow the commit.
exit 0

テスト

先ずはサンプルのリポジトリを作成
cd /home/svnrepos
svnadmin create --fs-type fsfs test
chown -R apache:apache test
  • モジュール登録
cd /home/test
svn import -m 'Import project' test http://localhost/svn/test
ローカルPCでテスト
  • ローカルPCに作業コピー
cd ~
svn co http://svnServer/svn/test/trunk test
  • ローカルPCでファイルを追加
cd ~/test
>test.html
svn add test.html
  • ローカルPCでコミット
    • コミットログなしのパターン
cd ~/test
svn commit

--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: 'pre-commit' hook failed with error output:
No refs ticket number.(refs #)
    • 参照用チケット番号指定なしのパターン
cd ~/test
svn commit -m "ファイル追加 refs"
--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: 'pre-commit' hook failed with error output:
No refs ticket number.(refs #)
    • 参照用チケット番号が存在しないのパターン
cd ~/test
svn commit -m "ファイル追加 refs 9999"

--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: 'pre-commit' hook failed with error output:
Ticket #9999 is nothing.
    • 完了用チケット番号が存在しないのパターン
cd ~/test
svn commit -m "ファイル追加 refs 1 fixes 9999"

--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: 'pre-commit' hook failed with error output:
Ticket #9999 is nothing.
    • 完了用チケット番号が完了しているパターン
cd ~/test
svn commit -m "ファイル追加 refs 1 fixes 12"

--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .svn: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: 'pre-commit' hook failed with error output:
Ticket #12 is complite.
    • OKのパターン
cd ~/test
svn commit -m "ファイル追加 refs 1 fixes 1"

--- 結果 ---
追加しています              test.html
ファイルのデータを送信しています .
リビジョン ? をコミットしました。