一般公司都会使用git 或 svn 。现在的线上仓库比如 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.
总结出来几个点就是:
WebHook 的工作原理也是很简单的:
当我们 push 代码到线上仓库,线上仓库必然知道这个 push 操作,就会 hook(可以理解为回调)我们预留的 URL
而这个 URL 对应一段后台代码,这段代码执行了 git pull,这样就实现自动更新的操作。
准备工作
这里以 PHP 的代码为例,实际上用 Java、JavaScript 等都可以
我们需要在生产环境的服务器上装好 Git,这个应该是没有问题的
然后我们需要克隆代码下来,这里需要注意的是用户组和权限的问题
PHP 代码
Github、GitLab、Gitee 虽然都是 Git 仓库平台,但是发送的 WebHooks 请求的数据格式有些差别
请求头我们可以通过 $_SERVER 全局变量获得请求的值,比如 $_SERVER[‘X-Hub-Signature’]
然后看一下你的服务器支持不支持 shell_exec 这个 PHP 函数
exec()函数用来执行一个外部程序,我们再用这函数基本是在linux。
开启exec()函数:
exec()函数是被禁用的,要使用这个函数必须先开启。首先是 要关掉 安全模式 safe_mode = off。然后在看看 禁用函数列表
disable_functions = proc_open, popen, exec, system, shell_exec, passthru
这里要把 exec 去掉,重启 apache/nginx就OK了。
ini_set('date.timezone','Asia/Shanghai');
$target = '/home/www/Multithread'; // 生产环境web目录
//密钥
$secret = "123456";
//获取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 && sudo git pull 2>&1";
$res = shell_exec($cmd);
$res_log = 'Success:'.PHP_EOL;
$res_log .= $cmd . '结果' . $res .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 变量给的值,我这里是 123456
下面一个不用改,选择 Just the push event.,因为我们只需要 push 的时候进行回调,然后添加即可
然后 Github 会发送一个测试的请求,我们可以看一下 Response 是不是 200,然后看一下 Body 中有没有 success
第一次有个 Warning 是因为 count 这个函数的问题,Github 发送的测试请求没有 push 条数
这里需要注意的是用户组和权限的问题,php 程序默认的nginx 或 www 用户和用户组 所以你执行sudo时可能需要密码的问题。
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
sudo: no tty present and no askpass program specified
chmod u+w /etc/sudoers
visudo
** 命令,按i进入编辑模式后
,找到“root ALL=(ALL) ALL
”,在后面添加:"nginx ALL=(ALL) NOPASSWD: ALL
",再按一下“Esc”键退出编辑,输入“wq”(write quit)后,退出sudoers。“chmod u-w /etc/sudoers
”将文件的写入属性去掉sudo whoami
命令时,如果返回root,就可以使用了