前提:
1、Git服务器是指已经安装了Gitlab的Git管理服务器(Github的很多功能和Gitlab类似);
2、生产服务器是指配置好网站运行环境的测试服务器,默认apache的运行用户身份是www用户。
Git webhook以www用户实现自动部署的步骤:
1、在Git服务器上创建一个普通git用户(无需密码),如server-115.159.10.10,然后把这个用户加入到目标项目里的成员里,只需要有pull权限即可,飘易一般设置为Reporter。
2、在生产服务器上为www用户生成 SSH 公钥,路径一般在 /home/www/.ssh/,把生成的公钥 id_rsa.pub 复制粘贴到Git服务器上第一步里的普通用户的ssh公钥里
生成命令:
sudo -u www ssh-keygen
3、【可选】切换用户身份从root变成www,注意要把www的默认shell从/sbin/nologin改成/bin/bash,不然无法cd和git pull等;注:www用户没有密码,而Linux的pam验证拒绝没有密码的用户登录系统,所以我们不用担心www用户可以直接登录系统。
su - www
4、首次从git服务器上抓取资源用clone,指定目录需要为空
git clone git@git.a.net:li/test.git /storage/wwwroot/qy.a.net/
如果上面的第3步不做的话,可以模拟www用户身份执行:
sudo -u www git clone git@git.a.net:li/test.git /storage/wwwroot/qy.a.net/
5、先cd到项目根目录,后续再抓取直接pull
git pull
模拟www用户身份执行:
sudo -u www git pull
6、网站根目录下放置 git-webhook-handler.php 自动部署文件,然后Git服务器的对应项目的 webhooks 配置上该文件的网址(Push events),需启用php的shell_exec函数。
其中Webhooks的push推送事件发生时,会向设置的URL上推送一个json消息,消息格式如下:
Triggered when you push to the repository except when pushing tags.
Request header:
X-Gitlab-Event: Push Hook
Request body:
{ "object_kind": "push", "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "ref": "refs/heads/master", "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "user_id": 4, "user_name": "John Smith", "user_email": "john@example.com", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "project_id": 15, "project":{ "name":"Diaspora", "description":"", "web_url":"http://example.com/mike/diaspora", "avatar_url":null, "git_ssh_url":"git@example.com:mike/diaspora.git", "git_http_url":"http://example.com/mike/diaspora.git", "namespace":"Mike", "visibility_level":0, "path_with_namespace":"mike/diaspora", "default_branch":"master", "homepage":"http://example.com/mike/diaspora", "url":"git@example.com:mike/diaspora.git", "ssh_url":"git@example.com:mike/diaspora.git", "http_url":"http://example.com/mike/diaspora.git" }, "repository":{ "name": "Diaspora", "url": "git@example.com:mike/diaspora.git", "description": "", "homepage": "http://example.com/mike/diaspora", "git_http_url":"http://example.com/mike/diaspora.git", "git_ssh_url":"git@example.com:mike/diaspora.git", "visibility_level":0 }, "commits": [ { "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "message": "Update Catalan translation to e38cb41.", "timestamp": "2011-12-12T14:27:31+02:00", "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "author": { "name": "Jordi Mallach", "email": "jordi@softcatala.org" }, "added": ["CHANGELOG"], "modified": ["app/controller/application.rb"], "removed": [] }, { "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "message": "fixed readme", "timestamp": "2012-01-03T23:36:29+02:00", "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" }, "added": ["CHANGELOG"], "modified": ["app/controller/application.rb"], "removed": [] } ], "total_commits_count": 4 }
自动部署文件 git-webhook-handler.php 代码如下:
<?php //git webhook 自动部署脚本 //项目存放物理路径 $path = "/storage/wwwroot/qy.a.net/"; $requestBody = file_get_contents("php://input"); if (empty($requestBody)) { die('send fail'); } $content = json_decode($requestBody, true); //若是主分支且提交数大于0 if ($content['ref']=='refs/heads/master' && $content['total_commits_count']>0) { $res = shell_exec("cd {$path} && git pull 2>&1");//以www用户运行 $res_log = '-------------------------'.PHP_EOL; $res_log .= $content['user_name'] . ' 在' . date('Y-m-d H:i:s') . '向' . $content['repository']['name'] . '项目的' . $content['ref'] . '分支push了' . $content['total_commits_count'] . '个commit:' . PHP_EOL; $res_log .= $res.PHP_EOL; file_put_contents("git-webhook.txt", $res_log, FILE_APPEND);//追加写入 }
如果本地有修改,希望强制用远程的库更新:
git fetch --all git reset --hard origin/master
当我们希望不使用cd命令先到项目根目录的时候(对应上面不做第3步),可以使用git参数 --git-dir 和 --work-tree:
以www用户身份执行: shell_exec("cd {$path} && git pull 2>&1") 改成: shell_exec("git --git-dir={$path}.git fetch && git --git-dir={$path}.git --work-tree={$path} merge origin/master 2>&1")
注意,centos6上默认的git版本是1.7,而git 1.7在使用git pull时是有bug的,所以需要我们先fetch再merge!
cd / sudo -u www git --git-dir=/storage/wwwroot/test/.git fetch sudo -u www git --git-dir=/storage/wwwroot/test/.git --work-tree=/storage/wwwroot/test/ merge origin/master
而在git 1.8.5以上则可以直接使用下面的命令:
sudo -u www git --work-tree=/storage/wwwroot/test/ --git-dir=/storage/wwwroot/test/.git pull
上述的
shell_exec("cd {$path} && git pull 2>&1")
也可以使用下面的命令:
cd $path git reset --hard origin/master git clean -f git pull git checkout master
php里执行:
shell_exec("cd {$path} && git reset --hard origin/master && git clean -f && git pull 2>&1 && git checkout master")
【补充】:如何 clone git 项目到一个非空目录
如果我们往一个非空的目录下 clone git 项目,就会提示错误信息:
fatal: destination path '.' already exists and is not an empty directory.
解决的办法是:
1. 进入非空目录,假设是cd /workdir/proj1
2. git clone --no-checkout https://git.oschina.net/NextApp/platform.git tmp
3. mv tmp/.git . #将 tmp 目录下的 .git 目录移到当前目录
4. rmdir tmp
5. git reset --hard HEAD
然后就可以进行各种正常操作了。
【参考】:
1、通过gitlab的webhook进行项目的自动部署所遇问题总结
2、利用gitlab的web hook和钉钉做代码review工作
3、使用 GitHub / GitLab 的 Webhooks 进行网站自动化部署