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是我们自定义的钩子脚本。
钩子的执行顺序
钩子将按照以下的顺序执行
<project>.git/hooks/- symlink togitlab-shell/hooksglobal dir<project>.git/hooks/<hook_name>- executed bygititself, this isgitlab-shell/hooks/<hook_name><project>.git/custom_hooks/<hook_name>- per project hook (this is already existing behavior)<project>.git/custom_hooks/<hook_name>.d/*- per project hooks<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代码:
#!/usr/bin/env python
# coding=utf-8
'''
该脚本在pre-receive或post-receive钩子中被调用,也可以直接将该文件作为git的钩子使用
若钩子为shell脚本,则需要加入以下代码调用该脚本:
while read line;do
echo $line | python $PATH/pre-receive.py
done
当用户执行git push的时候会在远程版本库上触发此脚本
该脚本的主要作用:获取用户提交至版本库的文件列表,提交者及时间信息
''' import sys, subprocess
import re
import os __author__ = "zhanghuiwen"
excludPath ="/opt/gitlab/embedded/service/gitlab-shell/custom_hooks/excludes/excludes.txt";
baseGitUrl="http://172.26.0.80:8081" class Trigger(object): def __init__(self):
'''
初始化文件列表信息,提交者信息,提交时间,当前操作的分支
'''
self.pushAuthor = ""
self.pushTime = ""
self.fileList = []
self.ref = "" def __getGitInfo(self):
'''
'''
self.oldObject = sys.argv[2]
self.newObject = sys.argv[3]
self.ref = sys.argv[1] # 跳过排除的项目
def _skipExcludeProjects_(self):
'''
跳过扫描的项目
'''
rev = subprocess.Popen("pwd", shell=True, stdout=subprocess.PIPE);
gitServerRepoPath = rev.stdout.readline(); # 路径'/var/opt/gitlab/git-data/repositories/alpha/testhook.git'
paths = gitServerRepoPath.split("repositories");
projectPath = paths[1]; # /alpha/testhook.git
rev.stdout.close(); # 读取配置中的文件
lines = open(excludPath, "r");
for line in lines:
realLine = line.strip("\n");
result = realLine.replace(baseGitUrl,"")
if projectPath.strip(" ").strip("\n") == result.strip(" ").strip("\n"):
lines.close()
print ("例外项目允许不经过dev和test直接提交")
exit(0)
else:
pass
lines.close()
# 继续执行 def __getPushInfo(self):
'''
git show命令获取push作者,时间,以及文件列表
文件的路径为相对于版本库根目录的一个相对路径
'''
rev = subprocess.Popen('git rev-list ' + self.oldObject + '..' + self.newObject, shell=True,
stdout=subprocess.PIPE)
pushList = rev.stdout.readlines()
pushList = [x.strip() for x in pushList]
# 循环获取每次提交的文件列表
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]
self.pushAuthor = pipe[1].strip("Author:").strip()
self.pushTime = pipe[2].strip("Date:").strip() self.fileList.extend(['/'.join(fileName.split("/")[1:]) for fileName in pipe if
fileName.startswith("+++") and not fileName.endswith("null")]) uBranch = self.ref.split('/')[len(self.ref.split('/')) - 1]
print '提交分支: %s' % uBranch
print '提交变动from:%s to:%s' % (self.oldObject, self.newObject)
print '提交的commit:%s' % pushList
# if uBranch == 'dev':
# return
# 循环获取每次提交的文件列表
for pObject in pushList:
# 判断是否是merge commit,如果是merge commit则忽略
gitCatFileCmd = ('git cat-file -p %s') % (pObject)
p = subprocess.Popen(gitCatFileCmd, shell=True, stdout=subprocess.PIPE)
pipe = p.stdout.readlines()
pipe = [x.strip() for x in pipe]
i = 0
for branch in pipe:
if branch.startswith('parent '):
i += 1
if i >= 2:
continue # 如果提交的带上的msg是FIX_MERGE_ERROR则可以通行(避免合错分支引起的问题)
msgLine = pipe[-1]
print msgLine
if msgLine == 'FIX_MERGE_ERROR':
continue
# if not re.match(r'^(\w+)-(\d+)', msgLine):
# print '\033[1;35m %s 提交的信息没有带上jira编号,请确认添加 \033[0m' % pObject
# exit(-1)
listCmd = ('git branch --contains %s') % (pObject)
p = subprocess.Popen(listCmd, shell=True, stdout=subprocess.PIPE)
pipe = p.stdout.readlines()
pipe = [x.strip() for x in pipe]
print 'commit:%s->所属分支:%s' % (pObject, pipe)
# 如果是master分支push提交,必须先提交dev、test
if 'master' == uBranch:
if 'dev' not in pipe or 'test' not in pipe:
print '\033[1;35m 合并到master的分支必须先在dev、test上经过验证合并才能提交,具体错误提交的hash:%s \033[0m' % pObject
exit(-1)
elif 'test' == uBranch:
if 'dev' not in pipe:
print '\033[1;35m 合并到test的分支必须先在dev上经过验证合并才能提交,具体错误提交的hash:%s \033[0m' % pObject
exit(-1)
branchs = set()
isMaster = True
for branch in pipe:
branch = branch.replace('* ', '')
if 'master' == branch:
isMaster = False
break
if 'test' == branch or 'dev' == branch or 'dev-permission' == branch or 'test-permission' == branch:
continue
# elif uBranch != 'master' and uBranch != 'test' and uBranch != 'dev' and branch != uBranch:
# print '\033[1;35m 提交失败!你合并提交的分支来自于多个分支,请确认,你的分支%s,其他分支%s \033[0m' % (uBranch, branch)
# exit(-1)
branchs.add(branch)
if len(branchs) >= 2 and isMaster:
print '\033[1;35m 提交失败!你合并提交的分支来自于多个分支,请确认%s \033[0m' % pipe
exit(-1) def getGitPushInfo(self):
'''
返回文件列表信息,提交者信息,提交时间
'''
self.__getGitInfo()
self._skipExcludeProjects_()
self.__getPushInfo()
print '========================================='
print "Time:", self.pushTime
print "Author:", self.pushAuthor
print "Ref:", self.ref
print "Files:", self.fileList
print '=========================================' if __name__ == "__main__":
t = Trigger()
t.getGitPushInfo()
git自定义项目钩子和全局钩子的更多相关文章
- Django12-ModelForm中创建局部钩子和全局钩子
一.局部钩子 命名规则为clean_对象名称,例如上面定义了username.pwd对象,那么可以定义clean_username.clean_pwd的局部钩子进行规则校验 1.例子:定义一个手机号校 ...
- form表单钩子,局部钩子和全局钩子
form表单源码解析: 局部钩子: 全局钩子:
- Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解)
我在之前做了一个关于AJAX和form组件的笔记,可以参考:Django学习笔记(8)——前后台数据交互实战(AJAX):Django学习笔记(6)——Form表单 我觉得自己在写Django笔记(8 ...
- Django框架(十四)-- forms组件、局部钩子、全局钩子
一.什么是forms组件 forms组件就是一个类,可以检测前端传来的数据,是否合法. 例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 二.forms组件的使用 1.使用语 ...
- Django框架(十五)—— forms组件、局部钩子、全局钩子
目录 forms组件.局部钩子.全局钩子 一.什么是forms组件 二.forms组件的使用 1.使用语法 2.组件的参数 3.注意点 三.渲染模板 四.渲染错误信息 五.局部钩子 1.什么是局部钩子 ...
- ModelForm错误验证自定义钩子和全局钩子
当需要对model_class中字段作进一步验证,作进一步的约束时,需要使用到钩子,即claan_xxx和clean方法.其返回的errors有点不是那么好处理.看示例. 1.Model_clas ...
- django----利用Form 实现两次密码输入是否一样 ( 局部钩子和全局钩子 )
from django import forms # 导入表单模块 from django.core.exceptions import ValidationError class RegisterF ...
- form(form基础、标签渲染、错误显示 重置信息、form属性、局部钩子、全局钩子)
form基础 Django中的Form使用时一般有两种功能: 1.生成html标签 2.验证输入内容 要想使用django提供的form,要在views里导入form模块 from django im ...
- MFC线程钩子和全局钩子[HOOK DLL]
第一部分:API函数简介 1. SetWindowsHookEx函数 函数原型 HHOOK SetWindowsHookEx( int idHook, // hook typ ...
随机推荐
- LINK : fatal error LNK1104: cannot open file .exe' 重开application Experience 服务即可
这是一个坑, , 答案五花八门这个解决了我的痛点. 就这样了.
- Windows10 VS2017 C++ Server Socket简单服务器端与客户端
服务端: #include "pch.h" #include<iostream> #include<WinSock2.h> #include <Ws2 ...
- python 前端 html
web 服务本质: 浏览器发出请求--HTTP协议--服务端接收信息----服务端返回响应---服务端把HTML文件发给浏览器--浏览器渲染页面. HTML: 超文本标记语言是一种用于创建网页的标记语 ...
- Unable to resolve module crypto
Add rn-nodeify to your devDependencies in package.json: "devDependencies": { "rn-node ...
- JavaBasic_12
类 抽象:对象的共性 类:产生对象的模板(对于属性来讲,类规定有没有属性以及属性类型,具体的属性值因对象的不同而不同) 数据类型:数据集及基于这个数据集的操作 类:我们自定义类型(定义成员变量,基于数 ...
- 洛谷P1605:迷宫(DFS)
题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫中移动有上下左右 ...
- SQL-记录查询篇-009
在学习记录查询之前,学习一些关键字的使用: 1.逻辑运算符:and . or . not .is null select * from table_name where id>2 and ...
- ES6语法知识
let/const(常用) let,const用于声明变量,用来替代老语法的var关键字,与var不同的是,let/const会创建一个块级作用域(通俗讲就是一个花括号内是一个新的作用域) 这里外部的 ...
- java应用:向用户注册的邮箱发送邮件
实现功能 忘记密码,注册成功等向用户发送验证码信息或注册信息. 业务流程 忘记密码: 1.验证邮箱是否注册过: 2.向邮箱发送验证码: 3.验证验证码是否正确: 4.重新设置密码: 我这里着重介绍发送 ...
- LDAP解决多个服务器多个samba,不能指定多个samba域 的问题
问题:在创建账号的时候,必须指定一个sambaDomain,但是只能指定一个,但是我有多个samba域要集成,那怎么办呢,怎么弄都只能登陆一个samba,不能所有的都登,经过反复的测试,反复的测试,找 ...