Gitlab + checkstyle 检查客户端推送代码是否规范


本文讲述如何在gitlab 服务器上,拒绝用户推送不合规范的 java 代码

1、一般在仓库的/var/opt/gitlab/git-data/repositories/<group>/<project>.git目录 下创建目录:custom_hooks

2、新增 可执行文件:pre-receive (脚本可以是任何ruby python shell可执行脚本,没有后缀名)输入内容如下:

 

#!/usr/bin/python
#coding=utf-8
import os
import sys
import subprocess
import tempfile
import shutil
from shutil import copyfile
__author__ = "lance"
class Trigger(object):
   def __init__(self):
       '''
       初始化文件列表信息,提交者信息,提交时间,当前操作的分支
       '''
       self.pushAuthor = ""
       self.pushTime = ""
       self.fileList = []
       self.ref = ""
   def __getGitInfo(self):
       value = sys.stdin.readline().strip()
       self.oldObject, self.newObject,self.ref = value.split(' ')
   def __getPushInfo(self):
       '''
       git show命令获取push作者,时间,以及文件列表
       文件的路径为相对于版本库根目录的一个相对路径
       '''
       rev = subprocess.Popen('git rev-list '+self.newObject,shell=True,stdout=subprocess.PIPE)
       revList = rev.stdout.readlines()
       revList = [x.strip() for x in revList]
       #查找从上次提交self.oldObject之后还有多少次提交,即本次push提交的object列表
       indexOld = revList.index(self.oldObject)
       pushList = revList[:indexOld]
       pushList.reverse()
       # temp file
       tempdir = tempfile.mkdtemp('git_hook')
       #循环获取每次提交的文件列表i
       diff_file_list = []
       for pObject in pushList:
           p = subprocess.Popen('git show '+pObject,shell=True,stdout=subprocess.PIPE)
           pipe = p.stdout.readlines()
           pipe = [x.strip() for x in pipe]
           #print("===>",pipe)
           #验证是否java文件
           file = pipe[6].strip("diff").strip()
           if not file.lower().endswith('.java'):
               continue
           filename = file.split('/')[-1]
           #git get Tree
           print(pipe)
           content_hash = pipe[7].strip("index").strip()[9:16]
           content_p = subprocess.Popen('git cat-file -p '+content_hash,shell=True,stdout=subprocess.PIPE)
           cpipe = content_p.stdout.readlines()
           #print(cpipe)
           with open(os.path.join(tempdir, filename), 'w+') as fp:
                fp.writelines(cpipe)
                self.handler_checkstyle(tempdir+"/"+content_hash+'.java')
   # checkstyle
       self.handler_checkstyle(tempdir)

   def handler_checkstyle(self, file):
       try:
           #print(file)
           cmd = r'java -jar /data/gitlab.checkstyle/lib/checkstyle-8.20-all.jar -c /data/gitlab.checkstyle/style/checkstyle.xml ' + file+'/'
           #print(cmd)
           result = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
           rpipe = result.stdout.readlines()
       stuerr_message = result.stderr.readlines()
           #print(stuerr_message)
       for index in range(0,len(rpipe)):
              # print('\n')
               print(rpipe[index])
           if len(rpipe) > 2:
               exit(1)
       except Exception as e:
          print('error message:'+e.message)
      exit(1)
       finally:
        #shutil.rmtree(file)
         pass

   def getGitPushInfo(self):
       self.__getGitInfo()
       self.__getPushInfo()
if __name__ == "__main__":
   #print("argv: ", sys.argv)
   t = Trigger()
   t.getGitPushInfo()
   print('success')
   exit(0)

注:如果这些脚本在window中编辑后上传到服务器;vim编辑时:set ff=unix 来重新编辑文件格式

 

3、使用的jar包的位置:/data/gitlab.checkstyle/lib/checkstyle-8.20-all.jar 

 

4、使用的xml的位置:/data/gitlab.checkstyle/style/checkstyle.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<!-- This is a checkstyle configuration file. For descriptions of
what the following rules do, please see the checkstyle configuration
page at http://checkstyle.sourceforge.net/config.html -->

<module name="Checker">
    <!-- 重复代码的检查,超过20行就认为重复,UTF-8格式-->
    <module name="TreeWalker">
        <!-- 命名方面的检查,它们都使用了Sun官方定的规则。 -->
        <!-- 局部的final变量,包括catch中的参数的检查 -->
        <module name="LocalFinalVariableName" />
        <!-- 局部的非final型的变量,包括catch中的参数的检查 -->
        <!--<module name="LocalVariableName" />-->
        <!-- 包名的检查(只允许小写字母) -->
        <module name="PackageName">
           <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
        </module>
        <!-- 仅仅是static型的变量(不包括static final型)的检查 -->
        <module name="StaticVariableName" />
        <!-- 类型(Class或Interface)名的检查 -->
        <module name="TypeName" />
        <!-- 非static型变量的检查 -->
        <module name="MemberName" />
        <!-- 方法名的检查 -->
        <module name="MethodName" />
        <!-- 方法的参数名 -->
        <!--<module name="ParameterName " />-->
        <!-- 常量名的检查 -->
        <module name="ConstantName" />
        <!-- 对区域的检查 -->
        <!-- 不能出现空白区域 -->
        <module name="EmptyBlock" />
        <!-- 所有区域都要使用大括号。 -->
        <module name="NeedBraces" />
        <!-- 多余的括号 -->
        <module name="AvoidNestedBlocks">
            <property name="allowInSwitchCase" value="true" />
        </module>
        <!-- String的比较不能用!= 和 == -->
        <module name="StringLiteralEquality" />
        <!-- 同一行不能有多个声明 -->
        <module name="MultipleVariableDeclarations" />
        <!-- 不必要的圆括号 -->
        <module name="UnnecessaryParentheses" />
        <module name="UncommentedMain" />
        <!-- 检查并确保所有的常量中的L都是大写的。因为小写的字母l跟数字1太象了 -->
        <module name="UpperEll" />
        <!-- 检查数组类型的定义是String[] args,而不是String args[] -->
        <module name="ArrayTypeStyle" />
        <module name="MagicNumber">
            <property name="tokens" value="NUM_DOUBLE, NUM_FLOAT"/>
            <property name="ignoreNumbers" value="0,1"/>
            <property name="ignoreAnnotation" value="true"/>
        </module>
    </module>
</module>

 开始测试:

1.包名必须匹配正则:(反例中包含大写)

^[a-z]+(\.[a-z][a-z0-9]*)*$

2.变量名必须匹配正则:(反例中以$开头)

^[a-z]+(\.[a-z][a-z0-9]*)*$

3.字符串数字匹配 Sring[] args 而不是 String args[]

4.字符串的比较用 equals 不能用 ==

5.空的main 方法检查

执行结果如图

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM