正 文:
前提:
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 [email protected]:li/test.git /storage/wwwroot/qy.a.net/
如果上面的第3步不做的话,可以模拟www用户身份执行:sudo -u www git clone [email protected]:li/test.git /storage/wwwroot/qy.a.net/
5、先cd到项目根目录,后续再抓取直接pullgit pull
模拟www用户身份执行:sudo -u www git pull
6、网站根目录下放置 git-webhook-handler.php 自动部署文件,然后Git服务器的对应项目的 webhooks 配置上该文件的网址(Push events),需启用php的shell_exec函数。
其中Webhooks的push推送事件发生时,会向设置的URL上推送一个json消息,消息格式如下:
Push events
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": "[email protected]",
"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":"[email protected]: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":"[email protected]:mike/diaspora.git",
"ssh_url":"[email protected]:mike/diaspora.git",
"http_url":"http://example.com/mike/diaspora.git"
},
"repository":{
"name": "Diaspora",
"url": "[email protected]:mike/diaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"[email protected]: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": "[email protected]"
},
"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 代码如下:
//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
然后就可以进行各种正常操作了。
【参考】: