Subversion Hooks
- Subversion Hooks
- 前言
- Subversion 被视为 CVS 的替代者
- 它改进了 CVS 恼人的文件名、目录名修改等多个 CVS 的痼疾
- 它最大限度的保持了和 CVS 的一致,照顾了 CVS 的使用习惯
- Subversion 也支持扩展,成为 Hooks(钩子脚本),类似于 CVS 的 CVSROOT 脚本扩展
- CVSROOT 脚本扩展可以参见 WHODO 的这篇文章
- WHODO 项目采用 Subversion 作为版本控制系统。本文介绍了 WHODO SVN 的 Hooks 定制
- 本文的 Hooks 脚本仅在 svn 1.1.4 和 svn 1.3.1 上进行过测试。
- Hooks 定制的注意事项
- 3. 并不是 STDERR 输出都返回控制台!
- 只有 Hooks 运行失败,才将 STDERR 的输出传给 SVN 客户端
- 4. DOS 格式还是 Unix 格式?
- 还有经常犯的一个错误是上传了 DOS 格式的脚本到 Linux/Unix 服务器
- Linux 上可以用 dos2unix 命令将 DOS 换行符 0D0A 转换为 0A
- 5. pre-revprop-change 和 post-revprop-change
- pre-commit, post-commit 脚本,只有 pre-commit 的返回值被检查,post-commit 即便运行错误,也不影响提交
- 但 pre-revprop-change 和 post-revprop-change 这两个脚本任何一个脚本错误,都导致对 revision 的属性修改失败。(实现似乎和手册中有出入)
- SVN Hooks 介绍
- SVN Hooks 的位置
- 位于 Repository 的 hooks 目录下
- 每一个 Repository 对应一个 hooks 目录
- SVN Hooks 名称
- SVN 1.3 有 9 个 Hooks 脚本,SVN Repository 刚刚配置完成,所有的 hooks 脚本以 .tmpl 扩展名存在。若要启用某个 hooks,去掉其 .tmpl 扩展名
- $ 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
- Windows:Windows 上是根据扩展名判断可执行文件的,因此需要有扩展名,可以为 .exe 或 .bat
- 依赖的第三方软件
- svn 的 SWIG python binding
- 测试是否已经正确安装
- >>> import sys
>>> print sys.path
>>> from svn import *
- 安装
- Subversion
- make swig-py && make install-swig-py
- 下面分别介绍各个脚本以及可行的定制方案。脚本参见 hooks 目录。
- 由于 Web 服务器限制,有的脚本可能无法从 web 直接下载。可以从 Whodo SVN 版本控制系统中下载。
- start-commit
- 说明
- 在客户端还没有向服务器提交数据之前,即还没有建立 Subversion transaction(缩写为 txn) 之前,执行执行该脚本
- 因此该脚本可以很快执行,不像其他 pre-commit, post-commit 脚本要等到数据传输完成之后才执行。
- 参数
- [1] REPOS-PATH (the path to this repository)
- [2] USER (the authenticated user attempting to commit)
- 定制:暂时关闭提交功能
- 如果 hooks 目录中存在文件 COMMIT_LOCK ,则暂时终止提交
- 代码示例
- #!/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
- 定制:用户黑名单
- 如果 hooks 目录中存在 BLACK_LIST 文件,列在其中的用户,禁止提交
- 代码示例
- 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
- 定制:禁止未登录用户提交
- 不过 SVN 的身份验证仅在需要时提供,因此好像此定制未必有效?
- 代码示例
- #!/bin/sh
REPOS="$1"
USER="$2"
if [ "x$USER" = "x" ]; then
echo "You must login before you can commit." 1>&2
exit 1
fi
- 定制:简陋的权限控制示例
- 一般不在 start-commit 中进行访问控制,因为只能限制到 repository 一级,不能精细到 repository 内部的目录、文件,因此基本没有用处。
- 代码示例
- 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
- 定制:检查 Repository 容量限制
- 参考: http://www.toutprogrammer.com/article_29_3.html
- 代码示例
- #!/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
- pre-commit
- 说明
- 在 Subversion transaction 完毕之后,在提交之前,执行该脚本
- 用 svnlook 可以查看 transaction 中包含的用户信息、提交信息等,具体参见 svnlook 命令帮助
- 用途
- 精细的“写权限”检查。如果是限制对 repos 的读取,需要使用 mod_authz_svn 模块!
- 参数
- [1] REPOS-PATH (the path to this repository)
- [2] TXN-NAME (the name of the txn about to be committed)
- 定制:检查 Commit Log 长度
- bash
- 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
- python
- 因为我经常偷懒,输入一排相同字符(如 .............)当作 commit log
- 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)
- 定制:检查 Commit Log 内容
- python
- 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)
- 定制:检查是否有同名(大小写不同)文件存在
- 如果服务器 repository 中存在文件名只是在大小写上有区分的文件,这在 Unix/Linux 上没有问题。
但是当客户端为 Windows 时,将会造成各种古怪的现象。
此扩展用于 检查 Repository 是否有同名文件(只是大小写不同而已),如果检查到,作为冲突报错。
- perl 实现
- 脚本 check-case-insensitive.pl
- 对于 svn 1.1.x 需要使用 perl 的实现,直接调用 svnlook 命令行
- python 实现
- check-case-insensitive.py
- 定制:检查新增文件的 svn:mime-type,以及 svn:eol-style 设置
- check-mime-type.py
- 根据 check-mime-type.pl 改写,主要是为了移植到 Windows 平台
- 定制:检查用户权限
- perl
- 脚本 commit-access-control.pl
- 配置文件 commit-access-control.cfg
- 说明
- 每一节必须包含 access 关键字
- access = (read-only|read-write)
- 可选的 users 关键字
- 可以在一行写下多个用户名,用空格分隔
- users = username1 [username2 [username3 [username4 ...]]]
- 也可以多条 users 语句
- users = username1 [username2]
users = username3 username4
- 示例
- [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
- 缺点
- ACL 控制功能没有下面的 python 脚本全面
- python
- 配置
- 一个配置文件可以被多个 Repository 共享
- 定制用户组
- 可以包含多个 groups 节
- [groups]
group1 = user1 user2
- 还可以包含只对 repository 有效的用户组
- [repos_basename groups]
group2 = user5 user6
- 引用用户组时,前面加上一个 @ 符号,用以和 用户名 区分
- 每个 repository 有一个 Section 与之对应
- section 的名称默认为 repository 的 basename,即去掉前面目录部分
- Section 中的权限策略由 KV 对组成
- KV 对的 KEY 由常规表达式组成,和修改的文件路径匹配
- KV 对的 Value 定义用户访问控制。用户访问控制可以是多条访问控制策略用空格分开
- Section 之间可以继承
- 如
- [example5 extends example2]
releases/[^/]+/ = *(add)
- post-commit
- 说明
- 在提交之后,执行该脚本。提交已经完成,不可更改,因此本脚本的返回值被忽略
- 参数
- [1] REPOS-PATH (the path to this repository)
- [2] REV (the number of the revision just committed)
- 定制:发送邮件
- perl
- commit-email.pl 脚本
- 语法: commit-email.pl REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...
- 对于单个项目
- commit-email.pl REPOS REVNUM [email_addr ...]
- 同时支持多个项目
- 多个 -m 参数,形成多个配置项组: -m pattern --from EMAIL -s subject EMAIL_ADDR
- 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
- 示例
- commit-email.pl -m "^(trunk|branches|tags)/project1" -
- commit-email.pl "$REPOS" "$REV" commit-watchers@example.org
- pre-revprop-change
- 说明
- 如果该脚本不存在,或者该脚本返回 false,则不进行修改 revision 属性操作
- 因为 revision 的属性是没有版本控制的,因此脚本最好提供一个备份的机制
- 参数
- [1] REPOS-PATH (the path to this repository)
- [2] REVISION (the revision being tweaked)
- [3] USER (the username of the person tweaking the property)
- [4] PROPNAME (the property being set on the revision)
- 定制:只允许修改 svn:log,不允许修改其他属性(如 svn:author)
- if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi
echo "Changing revision properties other than svn:log is prohibited" >&2
exit 1
- post-revprop-change
- 说明
- 在修改 revision 属性之后,执行该脚本。因为修改稿已经完成,不可更改,因此本脚本的返回值被忽略(不过实际上的实现似乎是该脚本的正确执行与否影响属性修改)
- 参数
- [1] REPOS-PATH (the path to this repository)
- [2] REV (the revision that was tweaked)
- [3] USER (the username of the person tweaking the property)
- [4] PROPNAME (the property being set on the revision)
- 定制:发送邮件
- perl
- propchange-email.pl
- 语法: propchange-email.pl REPOS REVNUM USER PROPNAME [[-m regex] [options] [email_addr ...]] ...
- 对于单个项目
- propchange-email.pl REPOS REVNUM USER PROPNAME [email_addr ...]
- 同时支持多个项目
- 多个 -m 参数,形成多个配置项组: -m pattern --from EMAIL -s subject EMAIL_ADDR
- 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
- pre-lock
- 定制:检查已经存在的 lock 的属主,如果是本人,允许 lock
- pre-unlock
- 定制:检查已经存在的 lock 的属主,如果是本人,允许 lock
- Windows 平台上的 Subversion Hooks
- 说明
- Windows 上的可执行程序是依据扩展名识别的,因此 Hooks 脚本需要带扩展名。如 pre-commit.bat, 或者 pre-commit.exe
- Windows 平台的 SVN Hooks,仍然沿用了 Unix 的脚本,因而需要安装 Perl, Python 等软件
- 更多的采用 Python 脚本,因为一些 Perl 脚本用到了命令管道等没有移植到 Windows 平台的 Perl Feature。
- pre-commit.bat
- 定制:检查新增文件的 svn:mime-type,以及 svn:eol-style 设置
- pre-revprop-change.bat
- 定制:只允许修改 svn:log,不允许修改其他属性(如 svn:author)
- 尚未实现功能
- 由于 subversion 的 Python Binding 在 Windows 平台的移植的困难,很多用到 svn python 模块的脚本没有移植。可以考虑将依赖 subversion python binding 的脚本用 svnlook 重写。
- 邮件:可以考虑使用 Cygwin 的 exim, ssmtp 等替代 脚本中的 sendmail
- 关于本文
- 版本
- 0.2, 2006/5/8
- 增加 Windows 上的 SVN Hooks 脚本;
- 参考资料
- 《Version Control with Subversion》
Subversion Hooks//mm2html.xsl FreemindVersion:0.8.0