这个周一直在研究如何整合Jenkins和git,也就是通过git项目的提交来触发Jenkins的部署。Jenkins支持github项目的分支监控,但如果是git Server的话,分支的提交就不能被监控了。
不过在Jenkins的部署触发模块(trigger build)中有一种是通过远程来触发(Trigger builds remotely)的,也就是当远程仓库收到更新之后访问某个URL来触发Jenkins的部署,在Jenkins的控制后台也提到了这一点。
One typical example for this feature would be to trigger new build from the source control system’s hook script, when somebody has just committed a change into the repository, or from a script that parses your source control email notifications.
基于此简单的看了一下git回调的使用,现总结如下:
(1) 无论是裸库还是非裸库,都有一个hooks文件夹,里面提供各种不同阶段的回调。有本地的,有远程的。以Mac下的git为例(version 2.2.1):
-rwxr-xr-x 1 allen staff 452 Mar 4 2016 applypatch-msg.sample
-rwxr-xr-x 1 allen staff 896 Mar 4 2016 commit-msg.sample
-rwxr-xr-x 1 allen staff 189 Mar 4 2016 post-update.sample
-rwxr-xr-x 1 allen staff 398 Mar 4 2016 pre-applypatch.sample
-rwxr-xr-x 1 allen staff 1642 Mar 4 2016 pre-commit.sample
-rwxr-xr-x 1 allen staff 1356 Mar 4 2016 pre-push.sample
-rwxr-xr-x 1 allen staff 4951 Mar 4 2016 pre-rebase.sample
-rwxr-xr-x 1 allen staff 1239 Mar 4 2016 prepare-commit-msg.sample
-rwxr-xr-x 1 allen staff 3611 Mar 4 2016 update.sample
默认都带了后缀sample, 使用的时候只要将后缀去掉即可。
(2) 以update.sample为例,里面提供关于tags,本地分支,远端分支改动的回调:
case "$refname", "$newrev_type" in
refs/tags/*,commit)
...
;;
refs/tags/*,delete)
...
;;
refs/tags/*,tag)
...
;;
refs/heads/*,commit)
...
;;
refs/heads/*,delete)
...
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
...
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
上面refname指向的就是我们进行改动的分支,如refs/heads/master, refs/heads/feature等。
结合上个例子提到的,我们可以在回调中访问Jenkins的某个地址来触发部署,因为作为共享代码库的一般是裸库,所以当开发人员将改动提交之后,对应的应该是裸库的本地分支的改动
case "$refname","$newrev_type" in
refs/heads/*,commit)
# branch
# 获取分支的简称,即去掉refs/heads/前缀 --> master
branch="$(git rev-parse --symbolic --abbrev-ref $refname)"
# Jenkins build trigger url
URL="http://xxxx/buildByToken/build?job=JOB_NAME/"${branch}"&token=TOKEN"
echo "${refname} receive some updates"
curl -sS $URL
;;
esac
由于hooks文件夹下的内容本质只是一堆脚本,所以我们可以通过修改脚本内容来添加自己所需要的回调。
另外要注意虽然叫回调,但它们的执行时机却是发生在真正的改动执行之前,它们会检查相应的更新如果符合要求,就进行反之则阻止。 就拿上面的update来说(如果是向远端分支提交新的改动),实际上是以新的对象(ref)去替换老的对象,如果引进回调, 那么就要在回调执行成功之后才会触发真正的更新。
上面获取分支名的做法对于新建的分支是不适用的,会报如下错误:
atal: ambiguous argument 'refs/heads/branch_name': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
个人的理解是因为结合上面提到的先通过检查再进行相应的操作,因为新建的分支提交上来的时候,refs/heads文件中根本没有这个分支相关的东西,所以rev-parse根本获取不到东西。对于获取分支名也可以采取另外一种方法,虽然有点Hack的味道:
# NF为属性的总个数 也就是输出最后一个
branch=$(echo "${refname}" | awk -F/ '{print $NF}')
实际上每种回调都是由特定的服务触发的,比如说update就是由git-receive-pack来触发的,关于它的底层实现可以参考git的 pack协议,git-receive-pack的运行机制。