一般公司的项目都会使用 Git 或者 SVN 进行版本控制,在本地开发之后push
上去,然后再使用ssh
连接线上服务器去手动拉取代码。甚至于有些公司还在使用传统的更新代码:使用ftp/sftp
进行上传覆盖
现在的线上仓库比如 Github、Gitlab、Gitee 等都支持hook
技术,可以很方便的实现代码的自动化管理
这里以我经常使用的 Github 为例,监听dev
分支有push
动作时,可以自动通过设置的hook
通知生产环境中的脚本执行git pull
拉取代码,自动更新,非常方便
关于WebHooks
让我们看看 官方 关于Github webhooks的解释:
Webhooks allow you to build or set up integrations which subscribe to certain events on GitHub.com.
总结出来几个点就是:
- 必须是 Github 上面的项目
- 订阅了确定的事件(包括 push/pull 等命令)
- 自动触发
其他线上仓库也是一样的,我们要达到的目的是:当有新的本地commit push
到线上仓库时,服务器仓库自动pull
最线上仓库新的代码
WebHook的工作原理也是很简单的:
当我们push
代码到线上仓库,线上仓库必然知道这个push操作,就会hook
(可以理解为回调)我们预留的URL
而这个URL对应一段后台代码,这段代码执行了git pull
,这样就实现自动更新的操作
准备工作
这里以 PHP 的代码为例,实际上用 Java、JavaScript 等都可以
我们需要在生产环境的服务器上装好 Git,这个应该是没有问题的
然后我们需要克隆代码下来,这里需要注意的是用户组和权限的问题
PHP 一般使用www
或者nginx
用户运行,PHP通过脚本执行系统命令也是用这个用户,所以必须确保在该用户家目录(一般是/home/www或/home/nginx)下有.ssh
目录
我们生成SSH
和克隆代码的时候,需要加上
sudo -Hu www ssh-keygen -t rsa -C "email@address.com"
sudo -Hu www git clone git@github.com:sy-records/WordPress-tools.git
我的服务器是www
用户权限,所以我加的是www
,你可以看一下你的服务器是那个用户在运行 PHP
SSH
生成好之后用户家目录是有.ssh
目录的
如果不确定的话,测试一下连接
代码语言:javascript复制sudo -Hu www ssh -T git@github.com
在哪里克隆的代码,查看一下你的目录,因为脚本里要用
PHP代码
Github、GitLab、Gitee 虽然都是Git
仓库平台,但是发送的WebHooks请求的数据格式有些差别
- Github支持
application/json
和application/x-www-form-urlencoded
两种格式,安全token
需通过请求头X-Hub-Signature
加密发给URL,服务器需要解密后验证。了解更多 - GitLab支持
application/json
格式,安全token
通过请求头HTTP_X_GITLAB_TOKEN
明文发给URL。了解更多 - Gitee也支持
application/json
和application/x-www-form-urlencoded
两种格式,安全token
放在请求体明文发给URL,名称是password
。了解更多
请求头我们可以通过$_SERVER
全局变量获得请求的值,比如$_SERVER['X-Hub-Signature']
然后看一下你的服务器支持不支持shell_exec
这个 PHP 函数
确保PHP正常执行系统命令,写一个PHP文件,内容:
代码语言:javascript复制echo shell_exec('ls -la');
在通过浏览器访问这个文件,能够输出目录结构说明PHP可以运行系统命令
新建一个 PHP 文件
代码语言:javascript复制$target = '/www/wwwroot/WordPress-tools'; // 生产环境web目录
//密钥
$secret = "test6666";
//获取GitHub发送的内容
$json = file_get_contents('php://input');
$content = json_decode($json, true);
//github发送过来的签名
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE'];
if (!$signature) {
return http_response_code(404);
}
list($algo, $hash) = explode('=', $signature, 2);
//计算签名
$payloadHash = hash_hmac($algo, $json, $secret);
// 判断签名是否匹配
if ($hash === $payloadHash) {
$cmd = "cd $target && git pull";
$res = shell_exec($cmd);
$res_log = 'Success:'.PHP_EOL;
$res_log .= $content['head_commit']['author']['name'] . ' 在' . date('Y-m-d H:i:s') . '向' . $content['repository']['name'] . '项目的' . $content['ref'] . '分支push了' . count($content['commits']) . '个commit:' . PHP_EOL;
$res_log .= $res.PHP_EOL;
$res_log .= '======================================================================='.PHP_EOL;
echo $res_log;
} else {
$res_log = 'Error:'.PHP_EOL;
$res_log .= $content['head_commit']['author']['name'] . ' 在' . date('Y-m-d H:i:s') . '向' . $content['repository']['name'] . '项目的' . $content['ref'] . '分支push了' . count($content['commits']) . '个commit:' . PHP_EOL;
$res_log .= '密钥不正确不能pull'.PHP_EOL;
$res_log .= '======================================================================='.PHP_EOL;
echo $res_log;
}
在执行的命令后面加上2>&1可以输出详细信息,确定错误位置,我这里没加,保存即可
打开你的 Github 仓库项目地址,进入Webhooks
点击Add webhook
,添加一个webhook
Payload URL 填写可以访问你刚才保存的那个文件的地址,建议放在一个可以访问的目录即可,不需要在你的项目目录中,放在项目目录中会提示你有新文件,很烦人的。当然你也可以把它当做项目的文件去提交上去
Content type 我们选择application/json
Secret 就是我们刚才的$secret
变量给的值,我这里是test6666
下面一个不用改,选择Just the push event.
,因为我们只需要push
的时候进行回调,然后添加即可
然后 Github 会发送一个测试的请求,我们可以看一下Response
是不是 200,然后看一下Body
中有没有success
第一次有个 Warning 是因为count
这个函数的问题,Github 发送的测试请求没有push
条数
然后我们可以在本地push
一下,再去测试一下,有什么问题评论讨论吧