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)