欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

REDMINE/SVN安装、配置、集成和应用(三)

程序员文章站 2022-03-21 12:40:24
...

本文将介绍如何使用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命令中,我们将完成下列几个任务:

  1. 检查注释信息内容,保证注释信息不为空。
  2. 检查注释信息中是否包含参照问题ID。
  3. 检查该参照问题是否属于提交者。
  4. 检查该参照问题是否处于正确的状态(正在处理)。
    任何一条不满足,该提交将被拒绝,以保证工作流程被落实。

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

下图显示的是二中可能的错误
REDMINE/SVN安装、配置、集成和应用(三)
REDMINE/SVN安装、配置、集成和应用(三)

其他可用的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