使用 Git Hooks 实现项目自动部署
自动化部署解放双手,发展生产力,更重要的是可以减少部署过程中的错误操作。
之前使用git做为我博客的版本控制,使用Github Pages托管我的博客,所以部署方面都交给了github, 但是当我要部署另一个web应用时,显然要部署在自己的VPS上,把VPS做为git服务器的同时,每次push 代码到服务器上都要手动运行一次脚本更新服务,这样做简直劳神伤力。
幸运的是Git提供了Hook机制用来帮助我们实现自动部署。Hooks分为客户端和服务端,可以用来处理不同
的工作,这些hooks都被存储在 Git 目录下的hooks子目录中,
即大部分项目中的.git/hooks
。 Git 默认会放置一些脚本样本在这个目录中,除了可以作为hooks使用,
这些样本本身是可以独立使用的,这些样本名都是以.sample结尾,必须重新命名。
这次主要用到服务端的hooks: post-receive
。当用户在本地仓库执行git push
命令时,服务器上运端
仓库就会对应执行git receive pack
命令;在所有远程仓库的引用(ref)都更新后,这个钩子就会被调用。
与之对应的是pre-receive
,这个会在更新之前被调用。
环境要求:
- 要求客户端和服务端都有git环境,而且服务端最好已经部署好了;
- 能连上服务器
0x01 实践
我们的实践过程会按照下边的过程实施:
+------------------------+ +------------------------+
| | | |
| +-----------------+ | push | +-------------------+ |
| |local repository |---+----------+->| remote repository | |
| +-----------------+ | | +-------------------+ |
| | | | |
+------------------------+ | |pull |
| V |
local machine | +-------------------+ |
| | deployment | |
| +-------------------+ |
| |
+------------------------+
server
在server上初始化一个远程裸仓库:
$ cd ~
$ mkdir remoteRepo
$ cd remoteRepo
$ git init --bare webapp.git
在server上初始化一个本地仓库,做为web app的代码:
$ cd ~
$ mkdir deployment
$ cd deployment
$ git clone ~/remoteRepo/webapp.git webapp
为远程仓库添加hook:
$ cd ~/remoteRepo/webapp.git/hooks
$ vim post-receive
$ cat post-receive
post-receive
中的命令:
#!/bin/sh
# Check the remote git repository whether it is bare
IS_BARE=$(git rev-parse --is-bare-repository)
if [ -z "$IS_BARE" ]; then
echo >&2 "fatal: post-receive: IS_NOT_BARE"
exit 1
fi
unset GIT_DIR
# current user is git
DeployPath=/home/git/deployment/webapp
if [ ! -d $DeployPath ] ; then
echo >&2 "fatal: post-receive: DEPLOY_DIR_NOT_EXIST: \"$DeployPath\""
exit 1
fi
cd $DeployPath
git add . -A && git stash
git pull origin master
为post-receive
添加可执行权限
chmod +x post-receive
为local machined的本地仓库添加远程仓库源:
cd <your-local-repository-folder>
$ git remote add deploy git@<server.ip>:/home/git/remoteRepo/webapp.git
# then you need to merge conflict between local changes and deploy/master before you push it.
# 'git merge remotes/deploy/master' or some other git commands.
$ git push deploy master
或者从头开始创建一个项目:
git init
这样,当我们在本地完成更新并push到server上时,这些代码就会被自动更新。
0x02 后来
改进1
可以在最初在server上创建裸仓库时使用local machine上的现有项目,即将local machine上 的项目仓库导出为裸仓库 — 即一个不包含当前工作目录的仓库:
$ git clone --bare my_project my_project.git
或者
$ cp -Rf my_project/.git my_project.git
然后将这个裸仓库移到server上
$ scp -r my_project.git git@<server.ip>:/home/git/remoteRepo
之后,其他人要进行更新时就可以clone这个项目了:
$ git clone git@<server.ip>:/home/git/remoteRepo/my_project.git
改进2
有一种情况是当本地更新了webapp,结果push到远程仓库后这个更新被reset了(虽然我觉得这个问题应该避免,
但是还是有可能发生),这是,简单地在hook中使用git push deploy master
是无法完成这个过程的,因为
远端的代码版本低于deploy端的代码版本,再使用pull的时候就不能实现同步,这时就应该使用另一种方式
更新代码:
git fetch --all
git reset --hard origin/master
即git reset
把HEAD 指向了新下载的未合并的节点,也就是在local machine上reset之后的。