Subversion Hooks
hide
前言
hide
Subversion 被视为 CVS 的替代者
leaf
它改进了 CVS 恼人的文件名、目录名修改等多个 CVS 的痼疾
leaf
它最大限度的保持了和 CVS 的一致,照顾了 CVS 的使用习惯
hide
Subversion 也支持扩展,成为 Hooks(钩子脚本),类似于 CVS 的 CVSROOT 脚本扩展
hide
CVSROOT 脚本扩展可以参见 WHODO 的这篇文章
leaf
WHODO 项目采用 Subversion 作为版本控制系统。本文介绍了 WHODO SVN 的 Hooks 定制
leaf
本文的 Hooks 脚本仅在 svn 1.1.4 和 svn 1.3.1 上进行过测试。
hide
Hooks 定制的注意事项
hide
1. 权限:脚本是否具有可执行权限?
leaf
chmod a+rx
hide
2. 不要依赖系统的环境变量,尤其是 PATH
leaf
为安全计,svn 执行脚本的环境变量为空
leaf
命令使用绝对路径,或者定义 PATH 环境
hide
3. 并不是 STDERR 输出都返回控制台!
leaf
不要认为输出到 stderr 的错误信息都输出
leaf
只有 Hooks 运行失败,才将 STDERR 的输出传给 SVN 客户端
hide
4. DOS 格式还是 Unix 格式?
leaf
还有经常犯的一个错误是上传了 DOS 格式的脚本到 Linux/Unix 服务器
leaf
Linux 上可以用 dos2unix 命令将 DOS 换行符 0D0A 转换为 0A
hide
5. pre-revprop-change 和 post-revprop-change
leaf
pre-commit, post-commit 脚本,只有 pre-commit 的返回值被检查,post-commit 即便运行错误,也不影响提交
leaf
但 pre-revprop-change 和 post-revprop-change 这两个脚本任何一个脚本错误,都导致对 revision 的属性修改失败。(实现似乎和手册中有出入)
hide
SVN Hooks 介绍
hide
SVN Hooks 的位置
leaf
位于 Repository 的 hooks 目录下
leaf
每一个 Repository 对应一个 hooks 目录
hide
SVN Hooks 名称
leaf
SVN 1.3 有 9 个 Hooks 脚本,SVN Repository 刚刚配置完成,所有的 hooks 脚本以 .tmpl 扩展名存在。若要启用某个 hooks,去掉其 .tmpl 扩展名
leaf
$ ls repos/hooks/

post-commit.tmpl post-unlock.tmpl pre-revprop-change.tmpl

post-lock.tmpl pre-commit.tmpl pre-unlock.tmpl

post-revprop-change.tmpl pre-lock.tmpl start-commit.tmpl
leaf
Windows:Windows 上是根据扩展名判断可执行文件的,因此需要有扩展名,可以为 .exe 或 .bat
hide
依赖的第三方软件
hide
CPAN
leaf
Config::IniFiles
hide
svn 的 SWIG python binding
hide
测试是否已经正确安装
leaf
>>> import sys

>>> print sys.path

>>> from svn import *
hide
安装
leaf
SWIG
hide
Subversion
leaf
make swig-py && make install-swig-py
hidepassword
leafidea
由于 Web 服务器限制,有的脚本可能无法从 web 直接下载。可以从 Whodo SVN 版本控制系统中下载。
hide
start-commit
hide
说明
leaf
在客户端还没有向服务器提交数据之前,即还没有建立 Subversion transaction(缩写为 txn) 之前,执行执行该脚本
leaf
因此该脚本可以很快执行,不像其他 pre-commit, post-commit 脚本要等到数据传输完成之后才执行。
leaf
也因此,该脚本获取的信息有限,不堪大用
hide
参数
leaf
[1] REPOS-PATH (the path to this repository)
leaf
[2] USER (the authenticated user attempting to commit)
hide
定制:暂时关闭提交功能
leaf
如果 hooks 目录中存在文件 COMMIT_LOCK ,则暂时终止提交
hide
代码示例
leaf
#!/bin/sh



REPOS="$1"

USER="$2"

TOOLS_DIR=$REPOS/hooks/scripts

LOCK_FILE=$REPOS/hooks/COMMIT_LOCK



if [ -f $LOCK_FILE ]; then

if [ -s $LOCK_FILE ]; then

# Characters in LOCK_FILE should be utf-8 format!

cat $LOCK_FILE >&2

else

# echo "系统维护中,暂时禁止提交。" >&2

echo "Under maintenance, commit not allowed this time." >&2

fi

exit 1

fi
hide
定制:用户黑名单
leaf
如果 hooks 目录中存在 BLACK_LIST 文件,列在其中的用户,禁止提交
hide
代码示例
leaf
BLACKLIST_FILE=$REPOS/hooks/BLACK_LIST



# check black_list

if [ -s $BLACKLIST_FILE ]; then

( grep -v "^#" "$BLACKLIST_FILE" | grep -iwq "$USER" ) && \

echo "$USER is not allowed to commit." >&2 && exit 1

fi
hide
定制:禁止未登录用户提交
leaf
不过 SVN 的身份验证仅在需要时提供,因此好像此定制未必有效?
hide
代码示例
leaf
#!/bin/sh



REPOS="$1"

USER="$2"



if [ "x$USER" = "x" ]; then

echo "You must login before you can commit." 1>&2

exit 1

fi
hide
定制:简陋的权限控制示例
leaf
一般不在 start-commit 中进行访问控制,因为只能限制到 repository 一级,不能精细到 repository 内部的目录、文件,因此基本没有用处。
hide
代码示例
leaf
case $REPOS in

/opt/svnhome/test)

case $USER in

jiangxin)

exit 0

;;

*)

echo "User: $USER not allowed" >&2

exit 1

;;

esac

;;

*)

echo "$REPOS is unchangeable for $USER" >&2

exit 1

;;

esac
hide
定制:检查 Repository 容量限制
leaf
参考: http://www.toutprogrammer.com/article_29_3.html
hide
代码示例
leaf
#!/bin/sh

# 参考: http://www.toutprogrammer.com/article_29_3.html



PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:$PATH



REPOS="$1"

USER="$2"



MAX_SIZE=20480 # 20MB

repos_size=`du -s $REPOS | cut -f 1`



if [ $repos_size -gt $MAX_SIZE ]; then

echo "Repositroy $REPOS has exceeded maximum size: $MAX_SIZE" 1>&2

exit 1

fi



exit 0
hide
pre-commit
hide
说明
leaf
在 Subversion transaction 完毕之后,在提交之前,执行该脚本
leaf
用 svnlook 可以查看 transaction 中包含的用户信息、提交信息等,具体参见 svnlook 命令帮助
hide
用途
leaf
检查 commit log 格式是否符合规范
leaf
精细的“写权限”检查。如果是限制对 repos 的读取,需要使用 mod_authz_svn 模块!
hide
参数
leaf
[1] REPOS-PATH (the path to this repository)
leaf
[2] TXN-NAME (the name of the txn about to be committed)
hide
定制:检查 Commit Log 长度
hide
bash
leaf
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:$PATH



REPOS="$1"

TXN="$2"

TOOLS_DIR=$REPOS/hooks/scripts



# Make sure that the log message contains some text.

SVNLOOK=/usr/local/bin/svnlook



commitlog=`$SVNLOOK log -t "$TXN" "$REPOS"`

case `echo -n ${commitlog}|wc -c` in

0)

echo "Commit log is blank, please write a comment for your commit." >&2

exit 1

;;

[1-2])

echo "Commit log must greater than 2 characters." >&2

exit 1

;;

*)

;;

esac
hide
python
leaf
因为我经常偷懒,输入一排相同字符(如 .............)当作 commit log
leaf
def check_strlen(log_msg, minLen):

log_length = len(log_msg)



if log_length > 0:

char = log_msg[0]

char2= log_msg[-1]

idx=1

while idx < len(log_msg):

if char == -1 and char2 == -1 and log_length <= 0:

break



if (char == log_msg[idx]) and (char != -1):

log_length = log_length - 1

char = log_msg[idx]

else:

char = -1



if (char2 == log_msg[-idx]) and (char2 != -1):

log_length = log_length - 1

char2 = log_msg[-idx]

else:

char2 = -1



idx = idx + 1



if log_length < minLen:

sys.stderr.write ("Commit log must greater than %d characters, or too simple.\n" % minLen)

sys.exit(1)
hide
定制:检查 Commit Log 内容
hide
python
leaf
def check_pattern(log_msg):

patterns = [

#r'(issue\s*[#]?[0-9]+)|(new.*:)|(bugfix:)',

]

for pat in patterns:

if re.compile(pat, re.I).search(log_msg, 1):

continue

else:

sys.stderr.write ("Cannot find pattern: '%s' in commit log.\n" % pat)

sys.exit(1)

hide
定制:检查是否有同名(大小写不同)文件存在
leaf
如果服务器 repository 中存在文件名只是在大小写上有区分的文件,这在 Unix/Linux 上没有问题。

但是当客户端为 Windows 时,将会造成各种古怪的现象。

此扩展用于 检查 Repository 是否有同名文件(只是大小写不同而已),如果检查到,作为冲突报错。
hide
perl 实现
leaf
脚本 check-case-insensitive.pl
leaf
对于 svn 1.1.x 需要使用 perl 的实现,直接调用 svnlook 命令行
hide
python 实现
leaf
check-case-insensitive.py
leaf
依赖 subversin SWIG BINDINGS for Python
Arrow Link
hide
定制:检查新增文件的 svn:mime-type,以及 svn:eol-style 设置
leaf
check-mime-type.pl
hide
check-mime-type.py
leaf
根据 check-mime-type.pl 改写,主要是为了移植到 Windows 平台
hide
定制:检查用户权限
hide
perl
hide
脚本 commit-access-control.pl
leafmessagebox_warning
依赖 CPAN 的 Config::IniFiles 包
Arrow Link
hide
配置文件 commit-access-control.cfg
hide
说明
hide
INI 格式
leaf
后面的覆盖前面的权限
hide
每一节必须包含 match 关键字
leaf
match = PERL_REGEX
hide
每一节必须包含 access 关键字
leaf
access = (read-only|read-write)
hide
可选的 users 关键字
leaf
如果没有 users 关键字,则针对所有用户
hide
可以在一行写下多个用户名,用空格分隔
leaf
users = username1 [username2 [username3 [username4 ...]]]
hide
也可以多条 users 语句
leaf
users = username1 [username2]

users = username3 username4
hide
示例
leaf
[Make everything read-only for all users]

match = .*

access = read-only



[Make project1 read-write for users Jane and Joe]

match = ^(branches|tags|trunk)/project1

users = jane joe

access = read-write



[However, we don't trust Joe with project1's Makefile]

match = ^(branches|tags|trunk)/project1/Makefile

users = joe

access = read-only
hide
缺点
leaf
ACL 控制功能没有下面的 python 脚本全面
leaf
没有用户分组设置
hide
python
leaf
脚本 svnperms.py
leaf
配置文件 svnperms.conf
hide
配置
leaf
一个配置文件可以被多个 Repository 共享
hide
定制用户组
hide
可以包含多个 groups 节
leaf
[groups]

group1 = user1 user2
hide
还可以包含只对 repository 有效的用户组
leaf
[repos_basename groups]

group2 = user5 user6
leaf
引用用户组时,前面加上一个 @ 符号,用以和 用户名 区分
hide
每个 repository 有一个 Section 与之对应
leaf
section 的名称默认为 repository 的 basename,即去掉前面目录部分
hide
Section 中的权限策略由 KV 对组成
leaf
KV 对的 KEY 由常规表达式组成,和修改的文件路径匹配
leaf
KV 对的 Value 定义用户访问控制。用户访问控制可以是多条访问控制策略用空格分开
hide
访问控制策略格式: 用户列表(权限列表)
leaf
用户列表是逗号分开的用户或用户组
leaf
用户列表为 * 则代表所有用户
leaf
权限列表是用逗号分隔的权限组合组成
hide
三种权限
hide
add
leaf
增加权限
hide
remove
leaf
删除权限
hide
update
leaf
修改文件以及修改属性权限
hide
Section 之间可以继承
hide
leaf
[example5 extends example2]

releases/[^/]+/ = *(add)
hide
post-commit
hide
说明
leaf
在提交之后,执行该脚本。提交已经完成,不可更改,因此本脚本的返回值被忽略
leaf
该脚本一般用于外发邮件
hide
参数
leaf
[1] REPOS-PATH (the path to this repository)
leaf
[2] REV (the number of the revision just committed)
hide
定制:发送邮件
hide
perl
hide
commit-email.pl 脚本
leaf
语法: commit-email.pl REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...
hide
对于单个项目
leaf
单个项目,可以用简化的命令行语法
leaf
commit-email.pl REPOS REVNUM [email_addr ...]
leaf
单个项目有一个默认的配置项组。-m 匹配所有路径
hide
同时支持多个项目
leaf
多个 -m 参数,形成多个配置项组: -m pattern --from EMAIL -s subject EMAIL_ADDR
leaf
commit-email.pl REPOS REVNUM \

-m pattern --from EMAIL -s subject EMAIL_ADDR \

-m pattern --from EMAIL -s subject EMAIL_ADDR \

-m pattern --from EMAIL -s subject EMAIL_ADDR
hide
示例
leaf
commit-email.pl -m "^(trunk|branches|tags)/project1" -
leaf
commit-email.pl "$REPOS" "$REV" commit-watchers@example.org
hide
python
leaf
mailer.py
leaf
配置文件 mailer.conf
leaf
定制:与 Bugtracking 系统整合
hide
pre-revprop-change
hide
说明
leaf
在修改 revision 属性之前,执行该脚本
leaf
如果该脚本不存在,或者该脚本返回 false,则不进行修改 revision 属性操作
leaf
因为 revision 的属性是没有版本控制的,因此脚本最好提供一个备份的机制
hide
参数
leaf
[1] REPOS-PATH (the path to this repository)
leaf
[2] REVISION (the revision being tweaked)
leaf
[3] USER (the username of the person tweaking the property)
leaf
[4] PROPNAME (the property being set on the revision)
hide
定制:只允许修改 svn:log,不允许修改其他属性(如 svn:author)
leaf
if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi



echo "Changing revision properties other than svn:log is prohibited" >&2

exit 1
hide
post-revprop-change
hide
说明
leaf
在修改 revision 属性之后,执行该脚本。因为修改稿已经完成,不可更改,因此本脚本的返回值被忽略(不过实际上的实现似乎是该脚本的正确执行与否影响属性修改)
leaf
一般用于触发邮件通知
hide
参数
leaf
[1] REPOS-PATH (the path to this repository)
leaf
[2] REV (the revision that was tweaked)
leaf
[3] USER (the username of the person tweaking the property)
leaf
[4] PROPNAME (the property being set on the revision)
hide
定制:发送邮件
hide
perl
hide
propchange-email.pl
leaf
语法: propchange-email.pl REPOS REVNUM USER PROPNAME [[-m regex] [options] [email_addr ...]] ...
hide
对于单个项目
leaf
单个项目,可以用简化的命令行语法
leaf
propchange-email.pl REPOS REVNUM USER PROPNAME [email_addr ...]
leaf
单个项目有一个默认的配置项组。-m 匹配所有路径
hide
同时支持多个项目
leaf
多个 -m 参数,形成多个配置项组: -m pattern --from EMAIL -s subject EMAIL_ADDR
leaf
propchange-email.pl REPOS REVNUM USER PROPNAME \

-m pattern --from EMAIL -s subject EMAIL_ADDR \

-m pattern --from EMAIL -s subject EMAIL_ADDR \

-m pattern --from EMAIL -s subject EMAIL_ADDR
leaf
python
Arrow Link
hide
pre-lock
leaf
说明:对文件进行加锁操作之前
leaf
定制:检查已经存在的 lock 的属主,如果是本人,允许 lock
hide
post-lock
leaf
说明:对文件进行加锁操作之后
leaf
定制:发送邮件
Arrow Link
hide
pre-unlock
leaf
说明:对文件进行解锁操作之前
leaf
定制:检查已经存在的 lock 的属主,如果是本人,允许 lock
hide
post-unlock
leaf
说明:对文件进行解锁操作之后
leaf
定制:发送邮件
Arrow Link
hide
Windows 平台上的 Subversion Hooks
hide
说明
leaf
Windows 上的可执行程序是依据扩展名识别的,因此 Hooks 脚本需要带扩展名。如 pre-commit.bat, 或者 pre-commit.exe
leaf
Windows 平台的 SVN Hooks,仍然沿用了 Unix 的脚本,因而需要安装 Perl, Python 等软件
leaf
更多的采用 Python 脚本,因为一些 Perl 脚本用到了命令管道等没有移植到 Windows 平台的 Perl Feature。
leaf
代码下载,参见
Arrow Link
hide
pre-commit.bat
hide
定制:检查 Commit Log 长度
leaf
commit_log_check.py
hide
定制:检查 Commit Log 内容
leaf
commit_log_check.py
hide
定制:检查新增文件的 svn:mime-type,以及 svn:eol-style 设置
leaf
check-mime-type.py
hide
pre-revprop-change.bat
leaf
定制:只允许修改 svn:log,不允许修改其他属性(如 svn:author)
hide
尚未实现功能
leaf
由于 subversion 的 Python Binding 在 Windows 平台的移植的困难,很多用到 svn python 模块的脚本没有移植。可以考虑将依赖 subversion python binding 的脚本用 svnlook 重写。
leaf
邮件:可以考虑使用 Cygwin 的 exim, ssmtp 等替代 脚本中的 sendmail
hide
关于本文
hide
作者
leaf
等待你的加入...
hide
版本
hide
0.2, 2006/5/8
leaf
增加 Windows 上的 SVN Hooks 脚本;
leaf
0.1