1)默认情况下,对于任何新项目或git仓库的每个默认限制分支都可以使用限制。这些限制适用于Gitlab的非管理员和非root用户。
2)我们通过编写一个名为Gitlab API的Java spring引导CommandLineRunner应用程序来实现规则,以检查我们的开发人员是否符合我们的开发策略和流程。这个应用程序打包成一个jar文件。
我们确保开发者必须拥有有效的门票号码作为他的git commit消息的一部分,然后他/她才能成功推送到他的工作分支的远程对手。必须将此有效票单分配给他,拥有有效的里程碑,并选择正确的标签(新功能,BUG,任务等)才能成功推送。
我们通过使用bash shell脚本与Gitlab服务器上的git挂钩进行了集成,该脚本执行jar文件并根据java应用程序的输出允许或失败推送请求。这个外壳脚本,其为http://blog.hgomez.net/2015/03/02/Gitlab-custom-hooks-Bash-Way.html适配,可以在下面找到:
#!/bin/bash
#
# pre-receive hook for Commit Check
#
COMPANY_EMAIL="mycorp.org"
readonly PROGNAME=$(basename $0)
readonly PROGDIR=$(readlink -m $(dirname $0))
IS_MERGE=0
check_single_commit()
{
COMMIT_CHECK_STATUS=1
echo "Repo >> $REPOSITORY_BASENAME"
if [[ "$COMMIT_MESSAGE" == "Merge branch"* ]]; then
COMMIT_CHECK_STATUS=0
IS_MERGE=1
else
workFlowResult=`java -jar -Dspring.config.location=/home/gitlab/gitlab_custom_hooks/application.properties /home/gitlab/gitlab_custom_hooks/gitlab-tool.jar -prercv "$COMMIT_AUTHOR" "$COMMIT_MESSAGE" "$REPOSITORY_BASENAME"`
echo "COMMIT_AUTHOR=$COMMIT_AUTHOR, COMMIT_MESSAGE=$COMMIT_MESSAGE, REPOSITORY_BASE=$REPOSITORY_BASENAME"
echo " >>>>>>>>>>>>>>>>> $workFlowResult >>>>>>>>>>>>>>>>>" >&2
if [[ "$workFlowResult" == *"PRE_RECEIVE_OK"* ]]; then
echo " >>>>>>>>>>>>>>>>> $workFlowResult >>>>>>>>>>>>>>>>>" >&2
COMMIT_CHECK_STATUS=0
fi
fi
}
check_all_commits()
{
REVISIONS=$(git rev-list $OLD_REVISION..$NEW_REVISION)
IFS='\n' read -ra LIST_OF_REVISIONS <<< "$REVISIONS"
if [ $(git rev-parse --is-bare-repository) = true ]
then
REPOSITORY_BASENAME=$(basename "$PWD")
else
REPOSITORY_BASENAME=$(basename $(readlink -nf "$PWD"/..))
fi
echo REPOSITORY_BASENAME is $REPOSITORY_BASENAME
REPOSITORY_BASENAME=$(basename "$PWD")
REPOSITORY_BASENAME=${REPOSITORY_BASENAME%.git}
for rid in "${!LIST_OF_REVISIONS[@]}"; do
REVISION=${LIST_OF_REVISIONS[rid]}
COMMIT_MESSAGE=$(git cat-file commit $REVISION | sed '1,/^$/d')
COMMIT_AUTHOR=$(git cat-file commit $REVISION | grep committer | sed 's/^.* \([^@ ]\[email protected][^ ]\+\) \?.*$/\1/' | sed 's/<//' | sed 's/>//' | sed 's/@$COMPANY_EMAIL//')
check_single_commit
if [ "$COMMIT_CHECK_STATUS" != "0" ]; then
echo "Commit validation failed for commit $REVISION" >&2
exit 1
fi
done
}
# Get custom commit message format
while read OLD_REVISION NEW_REVISION REFNAME ; do
check_all_commits
done
exit 0
3)虽然不是问题的一部分,在所述的服务器端集成PMD检查而不使用PMD詹金斯插件,需要下载PMD可执行启动依赖项,从python脚本内执行PMD,以静态分析开发人员推送到git服务器(Gitlab服务器)的源文件。引导PMD的python脚本可以很容易地集成到上面的bash shell脚本中。 python脚本,其改编为http://bluec0re.blogspot.com.ng/2012/05/git-pre-receive-hook-with-checkstyle.html,可以在下面找到:
#!/usr/bin/env python
import subprocess
import sys
import tempfile
import shutil
import os
import errno
# variables for checkstyle
#checkstyle = '/var/opt/gitlab/git-data/repositories/product-common/ProductCommonParent.git/custom_hooks/checkstyle-7.5.1-all.jar'
#checkstyle_config = '/var/opt/gitlab/git-data/repositories/product-common/ProductCommonParent.git/custom_hooks/sun_checks.xml'
pmd = '/home/gitlab/gitlab_custom_hooks/pmd-bin-5.5.4/bin/run.sh'
# implementing check_output for python < 2.7
if not hasattr(subprocess, 'check_output'):
def check_output(*popenargs, **kwargs):
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
er = subprocess.CalledProcessError(retcode, cmd)
er.output = output
raise er
return output
subprocess.check_output = check_output
# helper for calling executables
def call(*args, **kwargs):
return subprocess.check_output(*args, **kwargs).strip()
# helper for calling git
def call_git(cmd, *args, **kwargs):
return call(['git'] + cmd, *args, **kwargs)
# get all new commits from stdin
def get_commits():
commits = {}
for line in sys.stdin:
old, new, ref = line.strip().split(' ')
if old == '0000000000000000000000000000000000000000':
old = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
if ref not in commits:
commits[ref] = []
commits[ref].append({
'old': old,
'new': new,
'files': get_changed_files(old, new)
})
return commits
# get a list of changed files between to commits
def get_changed_files(old, new):
return call_git(['diff', '--name-only', old, new]).split('\n')
# get filemode, object type (blob,tree,commit), hash for the given file at the
# given commit
def get_change_type(commit, filename):
return call_git(['ls-tree', commit, filename]).split('\t')[0].split(' ')
commits = get_commits()
# use the latest file commit only
print "Cleaning up file list..."
files = {}
count = 0
for ref, data in commits.iteritems():
files[ref] = {}
for commit in data:
for filename in commit['files']:
if not filename.lower().endswith('.java'): continue
files[ref][filename] = get_change_type(commit['new'], filename)
count += len(files[ref])
print "%d Files to check in %d branches" % (count, len(files))
# create temporary dir and save a copy of the new files
tempdir = tempfile.mkdtemp('git_hook')
for ref, files in files.iteritems():
for filename, data in files.iteritems():
dname = os.path.dirname(filename)
bname = os.path.basename(filename)
try:
os.makedirs(os.path.join(tempdir, dname))
except OSError, exc:
if exc.errno == errno.EEXIST: # directory exists already
pass
else:
raise
with open(os.path.join(tempdir, dname, bname), 'w') as fp:
fp.write(call_git(['cat-file', data[1], data[2]]))
try:
# call checkstyle and/or pmd and print output
# print call(['java', '-jar', checkstyle, '-c', checkstyle_config, tempdir])
# print call(['java', '-jar', '/var/opt/gitlab/git-data/repositories/product-common/ProductCommonParent.git/custom_hooks/hooks-0.0.1-SNAPSHOT.jar', '-prercv', '79', 'developer-email-id', "I am now done with issue #500 #501 #502"])
print call([pmd, 'pmd', '-d', tempdir, '-f', 'text', '-R', 'rulesets/java/basic.xml,rulesets/java/unusedcode.xml,rulesets/java/imports.xml,rulesets/java/strings.xml,rulesets/java/braces.xml,rulesets/java/clone.xml,rulesets/java/design.xml,rulesets/java/clone.xml,rulesets/java/finalizers.xml,rulesets/java/junit.xml,rulesets/java/migrating.xml,rulesets/java/optimizations.xml,rulesets/java/strictexception.xml,rulesets/java/sunsecure.xml,rulesets/java/typeresolution.xml'])
print "SUCCESS"
except subprocess.CalledProcessError, ex:
print ex.output # print checkstyle and/or pmd messages
exit(1)
finally:
# remove temporary directory
shutil.rmtree(tempdir)