git自定義項目鈎子和全局鈎子


鈎子介紹

自定義鈎子分為:項目鈎子和全局鈎子

自定義全局鈎子:

全局鈎子目錄結構:

(注意:excludes目錄結構是我們自定義的目錄,規則邏輯在update.d/update.py腳本里實現的,非gitlab官方提供功能)

/opt/gitlab/embedded/service/gitlab-shell/custom_hooks
                                                                             ├── excludes
                                                                             │            └── excludes.txt
                                                                             └── update.d
                                                                                           └── update.py

鈎子目錄:

自定全局鈎子目錄: /opt/gitlab/embedded/service/gitlab-shell/custom_hooks  ,其中 update.d/update.py  是我們自定義的鈎子腳本, 腳本使用python語言。

如何擴展和修改全局鈎子指定目錄?

1.修改 /etc/gitlab/gitlab.rb  配置文件中的配置項:gitlab_shell['custom_hooks_dir'] = "/opt/gitlab/embedded/service/gitlab-shell/custom_hooks"

2. 執行 sudo gitlab-ctl reconfigure 命令來使配置生效。

 

     Tips:不要嘗試直接修改 <gitlab-shell>/config.yml 的文件內容,文件中有說明: 
     This file is managed by gitlab-ctl. Manual changes will be erased! To change the contents below, edit /etc/gitlab/gitlab.rb and runsudo gitlab-ctl reconfigure.

3.在自定的 custom_hooks_dir 目錄(即我們的custom_hooks目錄)下可創建三個文件夾對應三類 server hook name :

    • pre-receive.d
    • update.d
    • post-receive.d
  • 在每個文件夾下可創建任意文件,在對應的 hook 時期,gitlab 就會主動調用
  • 文件名以 ~ 結尾的文件會被忽略
  • 如果想看這部分的實現細節可以看 <gitlab-shell>/lib/gitlab_custom_hook.rb 文件

如何對單個項目排除全局鈎子?

在 /opt/gitlab/embedded/service/gitlab-shell/custom_hooks/excludes/excludes.txt   文件中新加一行你要排除的git的地址即可:例如: https://git.test.abc/example.git 。 (該功能是自定義實現的,非官方提供)

自定義項目鈎子

項目鈎子的目錄為固定目錄:

<project>.git/custom_hooks/   例如我們的項目自定義目錄為: /var/opt/gitlab/git-data/repositories/frontend/testWeb.git/custom_hooks/update   ,update是我們自定義的鈎子腳本。

 

鈎子的執行順序

鈎子將按照以下的順序執行

  1. <project>.git/hooks/ - symlink to gitlab-shell/hooks global dir
  2. <project>.git/hooks/<hook_name> - executed by git itself, this is gitlab-shell/hooks/<hook_name>
  3. <project>.git/custom_hooks/<hook_name> - per project hook (this is already existing behavior)
  4. <project>.git/custom_hooks/<hook_name>.d/* - per project hooks
  5. <project>.git/hooks/<hook_name>.d/* OR <custom_hooks_dir>/<hook_name.d>/* - global hooks: all executable files (minus editor backup files)

鈎子校驗規則

1、提交合並代碼到test分支前,需要先合並代碼到dev驗證通過后方能push到test

2、提交合並代碼到master分支前,需要先合並代碼到dev、test驗證通過后方能push到master

3、不同分支不能相互合並(如果需要合並,請合並后刪除一個不需要的分支方能提交)

4、合並代碼到dev、test、master,merge過程出錯了怎么辦?

可以直接在dev、test、master分支上修改代碼,但是需要在備注comment里填寫FIX_MERGE_ERROR 就可以直接提交(但請不要正常的代碼也使用這個命令!!!!!否則后果很嚴重)

 

git鈎子原理

在編寫鈎子的過程中發現鈎子原理如下:

1.gitlab默認安裝完畢后在server端會有兩個比較重要的目錄。

<project>.git 目錄例如:/var/opt/gitlab/git-data/repositories/test/automation.git (我們稱這個目錄為git的project) 目錄樹結構如下:

├── branches
├── config
├── description
├── HEAD
├── hooks -> /opt/gitlab/embedded/service/gitlab-shell/hooks    
├── hooks.old.1500269284
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
├── heads
└── tags

這個目錄結構和git本地項目.git 的目錄結構相似。目錄中保存了git操作相關的版本信息。在git本地修改的元數據信息提交后都會保存在該目錄中。

/opt/gitlab/embedded/service/gitlab-shell/hooks

改目錄是git默認鈎子的目錄。在git的低版本( GitLab 8.15.以下)中擴展全局鈎子可以該目錄的hooks目錄中直接修改或新加,但是可能影響到原有的鈎子,因此不建議使用,推薦方式見本章1.3 如何修改全局鈎子。

2.git 鈎子的腳本代碼中:  0 正常退出,用戶可以 push;非 0 異常退出,中斷提交(pre-receive 和 update)

3.通過本人研究發下,鈎子(腳本中調用的shell的命令)其實是在server端的 <project>.git  git目錄 執行的。舉例:當執行automation項目的全局鈎子時鈎子內執行的shell命令會在/var/opt/gitlab/git-data/repositories/test/automation.git 目錄中執行,可以通過在鈎子中pwd來打印當前鈎子的執行目錄發現的。

 

鈎子的python代碼:

  1 #!/usr/bin/env python
  2 # coding=utf-8
  3 '''
  4 該腳本在pre-receive或post-receive鈎子中被調用,也可以直接將該文件作為git的鈎子使用
  5 若鈎子為shell腳本,則需要加入以下代碼調用該腳本:
  6 while read line;do
  7         echo $line | python $PATH/pre-receive.py
  8 done
  9 當用戶執行git push的時候會在遠程版本庫上觸發此腳本
 10 該腳本的主要作用:獲取用戶提交至版本庫的文件列表,提交者及時間信息
 11 '''
 12 
 13 import sys, subprocess
 14 import re
 15 import os
 16 
 17 __author__ = "zhanghuiwen"
 18 excludPath ="/opt/gitlab/embedded/service/gitlab-shell/custom_hooks/excludes/excludes.txt";
 19 baseGitUrl="http://172.26.0.80:8081"
 20 
 21 
 22 class Trigger(object):
 23 
 24 
 25     def __init__(self):
 26         '''
 27         初始化文件列表信息,提交者信息,提交時間,當前操作的分支
 28         '''
 29         self.pushAuthor = ""
 30         self.pushTime = ""
 31         self.fileList = []
 32         self.ref = ""
 33 
 34 
 35 
 36     def __getGitInfo(self):
 37         '''
 38         '''
 39         self.oldObject = sys.argv[2]
 40         self.newObject = sys.argv[3]
 41         self.ref = sys.argv[1]
 42 
 43     # 跳過排除的項目
 44     def _skipExcludeProjects_(self):
 45         '''
 46          跳過掃描的項目
 47         '''
 48         rev = subprocess.Popen("pwd", shell=True, stdout=subprocess.PIPE);
 49         gitServerRepoPath = rev.stdout.readline();  # 路徑'/var/opt/gitlab/git-data/repositories/alpha/testhook.git'
 50         paths = gitServerRepoPath.split("repositories");
 51         projectPath = paths[1];  # /alpha/testhook.git
 52         rev.stdout.close();
 53 
 54         # 讀取配置中的文件
 55         lines = open(excludPath, "r");
 56         for line in lines:
 57             realLine = line.strip("\n");
 58             result = realLine.replace(baseGitUrl,"")
 59             if projectPath.strip(" ").strip("\n") == result.strip(" ").strip("\n"):
 60                 lines.close()
 61                 print ("例外項目允許不經過dev和test直接提交")
 62                 exit(0)
 63             else:
 64                 pass
 65         lines.close()
 66         # 繼續執行
 67 
 68     def __getPushInfo(self):
 69         '''
 70         git show命令獲取push作者,時間,以及文件列表
 71         文件的路徑為相對於版本庫根目錄的一個相對路徑
 72         '''
 73         rev = subprocess.Popen('git rev-list ' + self.oldObject + '..' + self.newObject, shell=True,
 74                                stdout=subprocess.PIPE)
 75         pushList = rev.stdout.readlines()
 76         pushList = [x.strip() for x in pushList]
 77         # 循環獲取每次提交的文件列表
 78         for pObject in pushList:
 79             p = subprocess.Popen('git show ' + pObject, shell=True, stdout=subprocess.PIPE)
 80             pipe = p.stdout.readlines()
 81             pipe = [x.strip() for x in pipe]
 82             self.pushAuthor = pipe[1].strip("Author:").strip()
 83             self.pushTime = pipe[2].strip("Date:").strip()
 84 
 85             self.fileList.extend(['/'.join(fileName.split("/")[1:]) for fileName in pipe if
 86                                   fileName.startswith("+++") and not fileName.endswith("null")])
 87 
 88         uBranch = self.ref.split('/')[len(self.ref.split('/')) - 1]
 89         print '提交分支:  %s' % uBranch
 90         print '提交變動from:%s to:%s' % (self.oldObject, self.newObject)
 91         print '提交的commit:%s' % pushList
 92         # if uBranch == 'dev':
 93         #    return
 94         # 循環獲取每次提交的文件列表
 95         for pObject in pushList:
 96             # 判斷是否是merge commit,如果是merge commit則忽略
 97             gitCatFileCmd = ('git cat-file -p %s') % (pObject)
 98             p = subprocess.Popen(gitCatFileCmd, shell=True, stdout=subprocess.PIPE)
 99             pipe = p.stdout.readlines()
100             pipe = [x.strip() for x in pipe]
101             i = 0
102             for branch in pipe:
103                 if branch.startswith('parent '):
104                     i += 1
105             if i >= 2:
106                 continue
107 
108             # 如果提交的帶上的msg是FIX_MERGE_ERROR則可以通行(避免合錯分支引起的問題)
109             msgLine = pipe[-1]
110             print msgLine
111             if msgLine == 'FIX_MERGE_ERROR':
112                 continue
113                 # if not re.match(r'^(\w+)-(\d+)', msgLine):
114                 #       print '\033[1;35m %s 提交的信息沒有帶上jira編號,請確認添加 \033[0m' % pObject
115                 #       exit(-1)
116             listCmd = ('git branch --contains %s') % (pObject)
117             p = subprocess.Popen(listCmd, shell=True, stdout=subprocess.PIPE)
118             pipe = p.stdout.readlines()
119             pipe = [x.strip() for x in pipe]
120             print 'commit:%s->所屬分支:%s' % (pObject, pipe)
121             # 如果是master分支push提交,必須先提交dev、test
122             if 'master' == uBranch:
123                 if 'dev' not in pipe or 'test' not in pipe:
124                     print '\033[1;35m 合並到master的分支必須先在dev、test上經過驗證合並才能提交,具體錯誤提交的hash:%s \033[0m' % pObject
125                     exit(-1)
126             elif 'test' == uBranch:
127                 if 'dev' not in pipe:
128                     print '\033[1;35m 合並到test的分支必須先在dev上經過驗證合並才能提交,具體錯誤提交的hash:%s \033[0m' % pObject
129                     exit(-1)
130             branchs = set()
131             isMaster = True
132             for branch in pipe:
133                 branch = branch.replace('* ', '')
134                 if 'master' == branch:
135                     isMaster = False
136                     break
137                 if 'test' == branch or 'dev' == branch or 'dev-permission' == branch or 'test-permission' == branch:
138                     continue
139                     # elif uBranch != 'master' and uBranch != 'test' and uBranch != 'dev' and branch != uBranch:
140                     # print '\033[1;35m 提交失敗!你合並提交的分支來自於多個分支,請確認,你的分支%s,其他分支%s \033[0m' % (uBranch, branch)
141                     # exit(-1)
142                 branchs.add(branch)
143             if len(branchs) >= 2 and isMaster:
144                 print '\033[1;35m 提交失敗!你合並提交的分支來自於多個分支,請確認%s \033[0m' % pipe
145                 exit(-1)
146 
147     def getGitPushInfo(self):
148         '''
149         返回文件列表信息,提交者信息,提交時間
150         '''
151         self.__getGitInfo()
152         self._skipExcludeProjects_()
153         self.__getPushInfo()
154         print '========================================='
155         print "Time:", self.pushTime
156         print "Author:", self.pushAuthor
157         print "Ref:", self.ref
158         print "Files:", self.fileList
159         print '========================================='
160 
161 
162 if __name__ == "__main__":
163     t = Trigger()
164     t.getGitPushInfo()
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM