通过Git Hooks实现自动部署

在没有接触到Git Hook之前,我一直都是在本地将代码push到远程仓库,然后再ssh到服务器上git pull,想起来都心酸。这样手工操作不仅繁琐,还非常容易出错。好在Git为我们提供了hook这种好东西,能够在特定的事件触发时执行我们写好的脚本,实现自动化部署。

为什么需要自动化部署?

  • 当在本地计算机完成服务器应用程序开发之后,需要把程序安装到服务器上,这样的安装过程一般称之为部署。
  • 部署一般分为文件复制、启动服务、安装依赖等。
  • 每次开发完成一个版本都需要部署一次。手动部署属于多次重复劳动。
  • 身为合格的程序员,应该把一切能够自动化的劳动自动化。

通过Git Hooks实现的自动化部署,将实现敲入git push命令后,自动完成整个部署过程。

认识Hooks

Git Hooks提供了多种形式的Hook,以pre-commit为例,该Hook将拦截git commit操作,运行名叫pre-commit的脚本,且仅当脚本返回值为0时进行真正的commit操作。

那么自动部署所需使用的Hook名为post-receive. 该Hook将在服务器端的bare repository接收到push信息并完成push操作后,进行执行;无法中断客户端(Client)的push过程。

可能浏览完上面的介绍,还是不太明白Hook是什么。简单地说,Hook是一种特殊的脚本(代码),仅在满足特定条件时执行。Git Hooks分别有对应各种操作的Hook,可以在git repository的.git/hooks目录下看到。

1
2
3
4
applypatch-msg.sample     pre-commit.sample         prepare-commit-msg.sample
commit-msg.sample pre-push.sample update.sample
post-update.sample pre-rebase.sample
pre-applypatch.sample pre-receive.sample

以上的脚本文件就是Hook了。可以看到脚本文件的后缀名都是sample,也就是说,这些都是Git自带的Hook示例,并不会真正地被执行。要想真正地被执行,只需要去掉sample后缀名即可。例如要启用pre-push的Hook(在push操作前执行脚本,脚本返回值为0时执行push操作),在hooks目录下新建一个pre-push的文件(没有后缀名)。

在脚本中,你可以写Bash、Python、JavaScript等代码,Git通过Shebang来选择执行代码的解释器。如果要写Bash,Shebang可以是这样:

1
#!/usr/bin/bash

使用Windows的读者请注意,如果脚本文件含有BOM(字节序标识符),可能会导致一些问题。

当完成脚本编写后,Git Hooks便完成了。

Git Hooks与自动部署

要保证不是git服务端和要部署的网站目录在同一台服务器,并且最好都属于git用户,这样不需要考虑操作权限问题了。

  • git hooks部署

裸仓库接收push之后怎么自动去执行pull操作,git仓库中有个.git文件夹,里面有个hooks文件夹,git 钩子就藏在这。里面有很多文件,其中有个post-receive.sample文件(没有的话也没有关系,新建一个post-receive文件,注意不带sample后缀,带上这个后缀默认是不触发执行的),我们要做的脚本代码就在这里个文件里面完成。

下面的示例是使用nodejs生成的静态网站,先执行git pull获取最新代码,然后检查node_modules文件夹是否存在,如果不存在,执行npm install命令。然后检查package.json文件,如果变动,执行npm install命令,最后执行npm run build命令生成静态网站文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash
WORKTREE=/var/www/xxxweb
CONFIG=package.json

while read oldrev newrev ref # post-receive 将读取这三个变量
do
if [[ $ref =~ .*/master$ ]]; # 仅允许master分支进行部署
then
echo "Pull to worktree..."
#echo "$oldrev $newrev"
cd $WORKTREE
unset GIT_DIR
git pull &> /dev/null

if [ ! -d "/node_modules" ] # 如果node_modules文件夹不存在
then
echo "install packages..."
npm install
fi

git diff --quiet $oldrev $newrev -- $CONFIG
if [ "$?" -eq "1" ] # 当package.json被修改时,安装依赖
then
echo "package.json changed"
export LC_ALL=C # 去除所有本地化的设置
echo "install packages..."
npm install
else
echo "package.json does not changed"
fi

#如果是需要PM2启动服务
pm2 show api &> /dev/null #查看PM2是否启动
if [ "$?" -eq "1" ]
then
pm2 start app.js -n 'api'
else
pm2 restart api
fi

#如果是生成网页
npm run build # 生成dist
echo "build complete"
else
echo "This is not master branch, and it will not be deployed"
fi
done

对了,编辑完post-receive文件,要赋予其执行权限,chmod +x post-receive,并且改变文件所有者chown git:git post-receive

  • 权限处理
    在上述代码示例中,我部署了的网站工作目录是/var/www/xxxweb,所属用户是git,如果不是,执行
    1
    chown -R git:git xxxweb
    因为所属用户是git,钩子脚本的所有者也是git,所以权限是没有问题的(如果不是同属于git用户,命令需要sudo,同时需要配置权限,比较麻烦,不建议)。可能有些时候需要在git用户下执行ssh -T git@127.0.0.1添加known_hosts。