REDMINE/SVN安装、配置、集成和应用(三)
本文将介绍如何使用SVN的hook来严格控制向SVN提交代码,让SVN控制没有有效的问题不能提交代码,不是问题的owner不能提交代码等等,让SVN实现一些收费的代码管理系统才有的功能。
安装工具软件
首先我们需要安装必须的工具软件JQ和RedmineCLI。
JQ是一个轻量级JSON数据流命令行处理器,具体请参见JQ网站。
RedmineCLI是基于NodeJS的Redmine命令行客户端,具体请参见RedmineCLI网站。
安装JSON命令行处理器(jq)
aaa@qq.com:/var/svn/repos# apt install jq
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libjq1 libonig4
The following NEW packages will be installed:
jq libjq1 libonig4
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 276 kB of archives.
After this operation, 930 kB of additional disk space will be used.
Do you want to continue? [Y/n]
Get:1 http://mirrors.aliyun.com/ubuntu bionic/universe amd64 libonig4 amd64 6.7.0-1 [119 kB]
Get:2 http://mirrors.aliyun.com/ubuntu bionic/universe amd64 libjq1 amd64 1.5+dfsg-2 [111 kB]
Get:3 http://mirrors.aliyun.com/ubuntu bionic/universe amd64 jq amd64 1.5+dfsg-2 [45.6 kB]
Fetched 276 kB in 1s (325 kB/s)
Selecting previously unselected package libonig4:amd64.
......
......
......
Setting up libjq1:amd64 (1.5+dfsg-2) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
Processing triggers for man-db (2.8.3-2) ...
Setting up jq (1.5+dfsg-2) ...
aaa@qq.com:/var/svn/repos#
安装Redmine命令行客户端(redmine)
首先安装NPM
aaa@qq.com:/var/svn/repos# apt install npm
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
gyp libjs-async libjs-inherits libjs-node-uuid libjs-underscore libuv1-dev node-abbrev node-ansi node-ansi-color-table
node-archy node-async node-balanced-match node-block-stream node-brace-expansion node-builtin-modules node-combined-stream
node-concat-map node-cookie-jar node-delayed-stream node-forever-agent node-form-data node-fs.realpath node-fstream
node-fstream-ignore node-github-url-from-git node-glob node-graceful-fs node-gyp node-hosted-git-info node-inflight
node-inherits node-ini node-is-builtin-module node-isexe node-json-stringify-safe node-lockfile node-lru-cache node-mime
node-minimatch node-mkdirp node-mute-stream node-node-uuid node-nopt node-normalize-package-data node-npmlog node-once
node-osenv node-path-is-absolute node-pseudomap node-qs node-read node-read-package-json node-request node-retry node-rimraf
node-semver node-sha node-slide node-spdx-correct node-spdx-expression-parse node-spdx-license-ids node-tar node-tunnel-agent
node-underscore node-validate-npm-package-license node-which node-wrappy node-yallist nodejs-dev python-pkg-resources
Suggested packages:
......
......
......
Setting up node-fstream-ignore (0.0.6-2) ...
Setting up node-tar (2.2.1-1) ...
Setting up node-gyp (3.6.2-1ubuntu1) ...
Setting up npm (3.5.2-0ubuntu4) ...
aaa@qq.com:/var/svn/repos#
使用NPM安装客户端redmine
root@scm:/var/svn/repos# npm install -g redmine-cli
npm WARN deprecated swig@1.4.2: This package is no longer maintained
/usr/local/bin/redmine -> /usr/local/lib/node_modules/redmine-cli/bin/redmine
> spawn-sync@1.0.15 postinstall /usr/local/lib/node_modules/redmine-cli/node_modules/spawn-sync
> node postinstall
/usr/local/lib
└─┬ redmine-cli@0.8.1
├─┬ cli-table@0.3.1
......
......
......
├── http-basic@2.5.1
├─┬ promise@7.3.1
│ └── asap@2.0.6
└── qs@6.5.2
root@scm:/var/svn/repos#
设置SVN通过HTTP提交时的注释内容编码
在 /etc/apache/conf-available 下,添加 svn.conf 文件
# SVN默认使用UTF-8
SVNUseUTF8 on
* 启用 svn.conf 文件 *
root@scm:# a2enconf svn
* 重启 apache2 *
root@scm:# service apache2 restart
配置Subversion的Hooks
配置 start-commit
在start-commit命令中,我们将完成下列几个任务:
- 检查注释信息内容,保证注释信息不为空。
- 检查注释信息中是否包含参照问题ID。
- 检查该参照问题是否属于提交者。
- 检查该参照问题是否处于正确的状态(正在处理)。
任何一条不满足,该提交将被拒绝,以保证工作流程被落实。
start-commit 命令在SVN命令执行前就会被执行。
#!/bin/bash
REPOS="$1"
USER="$2"
CAPS="$3"
TXN="$4"
# 管理员的API访问键
ADMIN="4f7bda298ed08254cc6cfb7a813fbef8dae7482f"
# Redmine的URL
REDMINEURL="https://scm.lswin.cn/redmine"
# 获取Redmine用户数据URL
USERSURL="https://scm.lswin.cn/redmine/users.json"
# 获取Redmine问题数据URL
ISSUESURL="https://scm.lswin.cn/redmine/issues.json"
# JSON命令行处理器
JSONPARSER="/usr/bin/jq"
# Redmine命令行客户端
REDMINE="/usr/local/bin/redmine"
# Subversion命令行状态获取
SVNLOOK="/usr/bin/svnlook"
# 命令行Web客户端
CURL="/usr/bin/curl --silent"
#CURL="/usr/bin/curl"
GREP="/bin/grep"
ECHO="/bin/echo"
SED="/bin/sed"
TR="/usr/bin/tr"
WC="/usr/bin/wc"
TRIMWS="/usr/bin/tr -d '\040\011\012\015'"
# 检查SVN的注释信息,注释信息不能为空
RES=$($SVNLOOK log -t "$TXN" "$REPOS")
if [[ ! "$RES" ]]; then
$ECHO "注释不能为空!" 1>&2
exit 1
fi
# 必须有相关联问题,相关联问题的格式为 refs #1234
cnt=$($ECHO $RES | $GREP -oP '(?<=\(refs).*?(?=\))' | wc -l)
if [[ "$cnt" == "0" ]]; then
$ECHO "(refs ...)是必需项!请添加后再提交!" 1>&2
exit 2
fi
# 预处理注释内容
STR=$($ECHO $RES | $GREP -oP '(?<=\(refs.*?(?=\))' | sed 's/[[:space:]]*,[[:space:]]*/,/g;s/^[ \t\n\r]*//;s/[ \t\n\r]*$//')
# 检查相关联问题格式,相关联问题的格式为 refs #1234
if [[ ! $STR =~ ^[0-9,#]+$ ]]; then
$ECHO "refs格式错误!" 1>&2
$ECHO " (refs $STR)" 1>&2
$ECHO "refs格式:" 1>&2
$ECHO " 以'(refs '开始,以')'结束。" 1>&2
$ECHO "issue id的格式:以'#'为头,数字紧跟;多个id,用','分开。" 1>&2
$ECHO " 示例:(refs #123) 或 (refs #123,#456,......)" 1>&2
exit 1
fi
is_valid=true
# 移除all leading/trailing white space, remove all trailing ','
RC=$($ECHO $RES | $GREP -oP '(?<=\(refs ).*?(?=\))' | sed 's/^[ \t]*//;s/[ \t,]*$//'),
INLIST=()
OIFS="$IFS"
IFS="," INLIST=($RC)
IFS="$OIFS"
for idx in "${!INLIST[@]}"
do
tmp=$($ECHO ${INLIST[idx]} | $SED 's/^[ \t]*//;;s/[ \t]*$//')
cnt=$($ECHO $tmp | $TR -cd '#' | $WC -c)
if [[ $tmp = '#'* ]]; then
if [[ "$cnt" != "1" ]]; then
$ECHO "$tmp 不是正确的refs 描述方式!只能有一个 '#'" 1>&2
exit 1
fi
else
$ECHO "$tmp 不是正确的refs 描述方式!必须以 '#' 开头。" 1>&2
exit 1
fi
tmp=$($ECHO ${INLIST[idx]} | $SED 's/.*#//')
INLIST[idx]=$tmp
if [[ $tmp =~ [^[:digit:]] ]]; then
$ECHO "#$tmp 不是正确的refs 描述方式!" 1>&2
$ECHO " refs 的格式:以'#'为头,紧跟着数字。" 1>&2
exit 1
fi
done
# 获取项目 ID
repo="${REPOS##*/}"
PROJID=$($CURL "$REDMINEURL/projects.json?key=$ADMIN&limit=10000" | $JSONPARSER --arg repo "$repo" '.projects[] | select(.identifier==$repo) | .id')
# 获取登陆用户名和ID
AUTHOR=$($SVNLOOK author $REPOS -t $TXN)
USERID=$($CURL "$USERSURL?key=$ADMIN&limit=10000" | $JSONPARSER --arg AUTHOR "$AUTHOR" '.users[] | select(.login==$AUTHOR) | .id')
# 检查该问题是属于提交者,并且状态是"正在解决"
ilist=$($CURL "$ISSUESURL?key=$ADMIN&limit=10000&assigned_to_id=$USERID&status_id=2&project_id=$PROJID" | $JSONPARSER '.issues[].id')
ILIST=()
OIFS="$IFS"
IFS="," ILIST=($ilist)
IFS="$OIFS"
for ele in "${INLIST[@]}"
do
in_list=false
for ele2 in "${ILIST[@]}"
do
if [[ "$ele" == "$ele2" ]]; then
in_list=true
fi
done
if [[ "$in_list" == "false" ]]; then
$ECHO "你不拥有Issue #$ele 或 问题 #$ele 不在 “正在解决” 状态!" 1>&2
exit 1
fi
done
exit 0
# Exit on all errors.
set -e
配置 post-commit
在post-commit命令中,我们将完成下列几个任务:
1. 检查注释信息内容,保证注释信息不为空。
2. 检查注释信息中是否包含参照问题ID。
#!/bin/bash
# POST-COMMIT HOOK
#
# The post-commit hook is invoked after a commit. Subversion runs
# this hook by invoking a program (script, executable, binary, etc.)
# named 'post-commit' (for which this file is a template) with the
# following ordered arguments:
#
# [1] REPOS-PATH (the path to this repository)
# [2] REV (the number of the revision just committed)
# [3] TXN-NAME (the name of the transaction that has become REV)
#
JSONPARSER="/usr/bin/jq"
REDMINE="/usr/local/bin/redmine"
SVNLOOK="/usr/bin/svnlook"
CURL="/usr/bin/curl --silent"
GREP="/bin/grep"
ECHO="/bin/echo"
SED="/bin/sed"
TR="/usr/bin/tr"
WC="/usr/bin/wc"
TRIMWS="/usr/bin/tr -d '\040\011\012\015'"
REDMINE_UPDATE='/usr/bin/curl --silent -H "Content-Type: application/json" -X PUT --data-binary "@/tmp/put_data.json" -H "X-Redmine-API-Key:4f7bda298ed08254cc6cfb7a813fbef8dae7482f" https://scm.lswin.cn/redmine'
REPOSPATH="$1"
REV="$2"
TXN_NAME="$3"
# 获取注释
LOGMSG=$($SVNLOOK log -r $REV $REPOSPATH)
# 可能的issue状态码
# ID NAME DEFAULT CLOSED
# 1 新创建
# 2 正在解决
# 3 已提交待测试
# 4 等待回复
# 5 关闭 X
# 6 已拒绝 X
# 7 评审已通过 X
# 9 评审进行中
# 11 评审未通过 X
# 12 评审请求
# 13 评审已分配
# 14 无需评审 X
# 15 无效
# 16 正在测试
# 17 已分配
# 18 已确认
# 19 暂缓解决 X
# 20 测试通过
# 21 测试失败
#
# Create json file for status of 已提交待测试 (id = 3)
$ECHO '{"issue":{"status_id":3}}' > /tmp/put_data.json
# Retrive IssueIDs end rmove all leading/trailing white space, remove all trailing ','
tmp=$($ECHO $LOGMSG | $GREP -oP '(?<=\(refs ).*?(?=\))' | $SED 's/[ \t]*//;s/,*$//')
#echo "tmp: "$tmp $OUTPUT
# Get ID list
OIFS="$IFS"
IFS="," INLIST=($tmp)
IFS="$OIFS"
# 将Issue状态从“正在解决”修改为“已提交待测试”
for id in "${INLIST[@]}"
do
# get issue ID
tmp=$($ECHO $id | $SED 's/^[ \t]*//;;s/[ \t]*$//')
tmp=$($ECHO $id | $SED 's/^#//')
# Update issue ststus to Resolved (3)
CMD=$REDMINE_UPDATE"/issues/"$tmp".json"
eval $CMD
done
# 发送通知邮件
"$REPOSPATH"/hooks/mailer/mailer.py commit "$REPOSPATH" $REV "$REPOSPATH"/hooks/mailer/mailer.conf
下面是代码提交成功后发送的邮件
张三<aaa@qq.com>;项目管理员<aaa@qq.com>;
svn-project-asia Rev14 - trunk
提交人: er.zhao
日期: Mon Sep 10 16:23:27 2018
新版本号: 14
注释信息:
测试post-commit的功能 (refs #27)
修改:
trunk/count.py
Modified: trunk/count.py
==============================================================================
--- trunk/count.py Mon Sep 10 16:16:59 2018 (r13)
+++ trunk/count.py Mon Sep 10 16:23:27 2018 (r14)
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 3 17:40:29 2017
-Modified on Thur Sept 9 13:23:05
+Modified on Thur Sept 10 13:23:05
@author: Administrator
"""
python工具mailer包含在subversion-tools包中,您可能需要另外安装。这里展示的邮件样本是客户定制过的样本,您可以通过修改mailer.conf文件,按您的需要定制邮件格式。
配置 pre-revprop-change
再提交时,偶尔会出现注释有误的问题,修改提交注释成为一个必需的功能。如果完全开放修改提交注释功能,又会造成其他的问题。在此我们使用pre-revprop-change来控制谁能修改提交注释。
#!/bin/sh
# PRE-REVPROP-CHANGE HOOK
#
# The pre-revprop-change hook is invoked before a revision property
# is added, modified or deleted. Subversion runs this hook by invoking
# a program (script, executable, binary, etc.) named 'pre-revprop-change'
# (for which this file is a template), with the following ordered
# arguments:
#
# [1] REPOS-PATH (the path to this repository)
# [2] REV (the revision being tweaked)
# [3] USER (the username of the person tweaking the property)
# [4] PROPNAME (the property being set on the revision)
# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted)
#
# [STDIN] PROPVAL ** the new property value is passed via STDIN.
#
REPOS="$1"
REV="$2"
USER="$3"
PROPNAME="$4"
ACTION="$5"
# 项目经理在Redmine的登录名
PROJMGR=dt.zhang
# 检查是否是修改提交信息,如不是提示错误退出
if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then
# 检查是否是特定人员在修改提交信息,如不是提示错误然后退出
if [ "$USER" = "$PROJMGR" ]; then
exit 0
fi
echo "只能项目经理能修改提交信息,请联系项目经理!" >&2
exit 200
fi
echo "只能修改提交注释,其他修改都已被禁止!" >&2
exit 201
下图显示的是二中可能的错误
其他可用的Subversion Hooks pre-commit
post-revprop-change
pre-lock
post-lock
pre-unlock
post-unlock
有关上述hooks的具体说明,可参见下列URL:http://svnbook.red-bean.com/nightly/zh/svn.ref.reposhooks.html。