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