先贴上代码
1 | #!/usr/bin/env ruby |
需求背景
我们发生过几次线上事故,就是带有 .test.51offer.com
的内容发布到正式环境,在内网很难主动发现这样的问题,所以需要对提交的代码做一些检查,自然的想到了 Git Hook
。
什么是 Git Hook
Hook
指的是钩子,这里可以理解成某种事件所触发的程序。比如:你在本地做 commit 或者 pull 时都可以触发 Git 中相应的 Hook 脚本。Hook 脚本又分客户端和服务端。它们响应不同的事件。 Hook脚本可以使用 Ruby, Python 和 Shell 来编写。因为分发的问题,解决上面的需求我选择了服务端 Hook,并把代码写在 update 事件中,希望仓库的分支在被更新时触发检查。
Git Hook 的详情介绍可以查看下面的地址:
https://git-scm.com/book/zh/v1/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git%E6%8C%82%E9%92%A9
技术关键点
最关键的是要在客户端在一次 push 中所有 commit 对象中找到你要的文件。连 Git 基本命令还在熟悉的我只能请教 stackoverflow.com
了。原文地址:http://stackoverflow.com/questions/1595631/how-to-get-a-list-of-all-blobs-in-a-repository-in-git
代码如下:
1 | $ git rev-list --objects --all | git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' | grep '^[^ ]* blob' | cut -d" " -f1,3- |
简化成我需要的:
1 | git rev-list --objects #{$oldrev}..#{$newrev} | git cat-file --batch-check='%(rest)' | egrep '\.(jsp|vm|html)$' |
使用了 rev-list
和 cat-file
两个 git 的底层命令,含义就是找出两个 commit id
之间所有类型的提交对象(commit,tree,blob),并一一查看他们,显示出他们对应的文件路径(除 blod
对象,其它类型是没有路径的),然后只拿带有那几项扩展名的路径。
有了这些路径,我只需要使用 git show
命令从最新版本中捞出这些文件内容就可。
这里一个细节:grep
需要加 -E
才能支持正则表达式,否则是使用通配符,egrep
是直接使用正则表达式来做匹配的。
如何部署
因为我们使用了 Git Lab ,我从官方上找来了安装指南:
http://doc.gitlab.com/ce/hooks/custom_hooks.html
setup 比较简单,创建后如下图:
需要给 custom_hooks
目录及文件给于 git
用户执行权限:
1 | mkdir custom_hooks |
在本地提交验证 hook 是否工作
以上方案还是只在一个仓库中部署,需要所有仓库布置这样的 Hook 如何处理?
第一图上已经给出答案:/opt/gitlab/embedded/service/gitlab-shell/hooks
这里是 Gitlab 自带的一些 hook 如下图:
显然使用 require_relative
就可以把我们要加入的内容塞进去。
至此,我们用 hook 处理了这样的需求。暂时只部署在下面的仓库中,大家可以玩一下: git@gitlab.51offer.inner:libo/pack-test.git
后记
整理了一个比较适合在 gitlib 中使用的版本,这里再次遇到一个编码问题,为了统一源码编码格式和读取外部文件的编码格式引用了 nkf
模块。
1 | #!/usr/bin/env ruby |
注意
update hook 只能对已经存在的文件进行处理,新提交的文件不会触发
update
新版的gitlab已经使用自定义 update.d
文件来支持自定义hook
了,这样的好处是,即使更新 gitlab 你的自定义文件还是存在的,不需要手动从系统全局的 hook
文件中添加