这篇文章的使用场景前提:本地开发,然后同步到github仓库,再在服务器上通过git pull从仓库拉取更新的内容。这里的github仓库相当于一个中转站,连接本地和远程服务器的一个桥梁。每次提交到远程仓github库的代码文件,还需要再进入服务器拉取一下,略显的麻烦,如果在本地更改的文件代码提交到github仓库之后,仓库自动的再下发到服务器那么不是非常爽?那就按照如下方式来操作:
首先,服务器环境是lnmp,所以创建一个webhook.php的文件,用来接收github仓库更新的通知,并且做出相应git pull操作。
<?php //服务器本地仓库路径,填写仓库入口目录 $local = '/alidata1/www/web/'; //github远程仓库地址 $remote = 'https://github.com/example/examplegit'; //密钥,github是密钥,验证方式跟gitee不一样 //请注意,这里设置的密钥稍后在github网站设置的时候需要用到 $secret = 'password'; //获取请求参数 $request = file_get_contents('php://input'); if (empty($request)) { die('request is empty'); } //获取http 头 $headers = getHeaders(); //github发送过来的签名 $hubSignature = $headers['X-Hub-Signature']; list($algo, $hash) = explode('=', $hubSignature, 2); // 计算签名 $payloadHash = hash_hmac($algo, $request, $secret); // 判断签名是否匹配 if ($hash != $payloadHash) { die('secret is error'); } // echo shell_exec("cd {$local} && /usr/bin/git pull {$remote} 2>&1"); // /root/github_synch.sh 是在服务器上面的一个拉取仓库文件的脚本。 echo shell_exec("/root/github_synch.sh"); die('done ' . date('Y-m-d H:i:s', time())); /** * @todo 获取头信息 */ function getHeaders() { $headers = array(); //Apache服务器才支持getallheaders函数 if (!function_exists('getallheaders')) { foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; } } } else { $headers = getallheaders(); } return $headers; }
请将以上文件放置到服务器的web目录内(比如/alidata1/www/web/webhook.php),由于在github网站稍后的配置中,github网站需要访问这个文件,所以给这个文件的访问配置一个URL,比如:
https://github.example.com/webhook.php
因此,在服务器上就应当配置一个对应的“主机”,并做好DNS解析工作。
然后,前面提到的那个脚本文件(github_synch.sh):
#!/bin/bash # github_synch.sh # 进入服务器本地仓库目录 cd /alidata1/www/web/ # 从github拉取更新 git pull origin master # 防止拉取更新之后目录权限变更 chown -R www.www /alidata1/www/web # 防止 修正 webhook.php 可执行权限 chmod +x /alidata1/www/web/webhook.php
这个脚本文件的属主和属组是root,权限是755。
上面这个脚本相当于模拟真人在服务器操作连接github,所以需要配置必备的账户密码等信息:
请在root的家目录创建文件(.git-credentials):
https://用户名:密码@github.com //github的配置
同样在shell下执行下面的操作:
git config --global user.name "用户名" git config --global user.email 邮箱
上面两行操作是配置服务器访问github仓库之用的。如果已经配置过那么是可以跳过的。
接着:
git config --global credential.helper store
这个时候root的家目录下的.gitconfig文件是这样的:
对root用户的配置就完成了,接着是对WWW用户的,因为php-fpm运行的是www用户,非常简单,将root用户家目录下面的.gitconfig和.git-credentials这两个文件复制到www用户的家目录/home/www/内,并且修改这两个文件的属性:
cp ~/.gitconfig /home/www/ cp ~/.git-credentials /home/www/ cd /home/www/ chown www.www .gitconfig chown www.www .git-credentials
完成以上的内容之后,服务器端的配置就完成了。
然后,在进入github网站开启webhook:
进入仓库点击右侧的设置:
然后,再点击左侧的Webhooks:
然后点击右侧的 Add webhook
payload URL 填写的就是需要访问的钩子文件的URL
content type 选择 application/json
Secret 这里写的密钥和前面webhook.php脚本里面的要一致
如果前面设置的URL是https协议访问的,这里会出现SSL验证选项,如果你的HTTPS是免费的一定选择禁止SSL验证:
然后后面的trigger 选择默认即可 Just the push event
以及最后的Active。
当全部配置完的时候,github会发送一条测试信息,根据Response的内容可以判断是否成功:
虽然,前面是打了对勾,但是返回的信息提示有函数被禁止执行了,这个函数正好是服务器拉取更新需要的功能,所以前往php配置文件将这个被禁止的函数解除禁止,并且重启php服务器。
这个时候你以为就真的大功告成了?错!
最后还是拉取更新没成功,因为PHP脚本是通过php-fpm去执行的,而php-fpm的运行身份是www,www是没有权限去执行root可以执行的动作(git),这里必须让www有root的权限去执行它,而且不需要填写密码。于是,要这么做。编辑修改 /etc/sudoers 文件,配置这么一段:
## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL
www ALL=(ALL) NOPASSWD:ALL
由于环境和安装的差异这里的git路径请填写真确。到这里你可能以为这回真的没问题了,可惜你又错了。博主在调试和对比了几个场景发现,还有下面两个隐藏的坑:
1. webhook.php那个是由php-fpm(www)来执行运行的,但是它里面执行调用的shell脚本确是在root的家目录里面
/root/github_synch.sh
www用户是没有权限进入root的家目录的,所以应该把github_synch.sh这脚本搬到www用户可以访问的目录内,比如:/home/www/。所以webhook.php里面执行的shell脚本的路径也需要相应修改。
2.一定确保仓库里面的.git目录下的config配置文件使用的是https访问的:(repo/.git repo是服务器上面的仓库目录)
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = https://github.com/example/example.git [branch "master"] remote = origin merge = refs/heads/master
上面的关于URL那里,后面必须是https的地址。如果是以SSH协议形式访问的
url = ssh:git@github.com:example/example.git
一定改成https的地址,不然报错
git error: unable to unlink old (Permission denied)
最后,完美收工,当然,最好实际进行测试一下为好。
参考资料:
https://blog.kinggui.com/archives/469
https://www.jianshu.com/p/50ea356152ac