使用GitHub的Webhooks实现代码的自动部署

前言

前段时间搞了个小项目放到GitHub上面,刚开始的时候,在本地push后,然后在到服务器上面执行pull拉取,还不觉得有多麻烦,但是后面更新比较频繁的时候,就觉得十分的繁琐,所以我就使用GitHub自带的Webhooks来实现自动部署。

流程

使用GitHub自带的Webhooks的自动部署逻辑。

- 本地修改代码,然后push提交
- github发送请求给你的网站服务器
- 网站服务器收到更新请求,执行自动部署脚本
- 自动部署脚本执行代码拉取等动作完成网站的更新部署

添加项目钩子

这里的钩子就是被请求的URL,如果你只有一个项目的话,你就可以把钩子直接写到你的项目里面,如果有多个项目要管理的话,最好是单独建一个项目去管理,这样比较好管理一些。

我只有一个项目所以,我就写到项目里面的,我用的是laravel框架。

这里我为什么要自己写一个执行shell的方法呢?因为shell_exec/exec/system/passthru这些函数对错误信息太不友好了。我大部分时间都耗在了排查错误上面。

//github 项目钩子
public function hook(Request $request){
    //webhook中设置的Secret
    $secret_token = env("WEBHOOK_SECRET_TOKEN");

    //验证签名
    $hub_signature = $request->header('X-Hub-Signature',"");//这个是GitHub返回来的签名
    if(empty($hub_signature)){
        Log::info("无效的钩子请求");
        return "";
    }
    //这是签名算法
    $signature = "sha1=".hash_hmac('sha1', $request->getContent(),$secret_token);
    //$request->getContent()是请求内容
    //如果你用的不是laravel,还可以使用file_get_contents("php://input")获取

    if(strcmp($signature,$hub_signature) == 0){
        //签名验证成功,执行shell命令
        $webPath = "/home/www/xxxxx";//生产环境下项目的目录地址
        $cmd = "cd $webPath && git pull origin master";//shell 命令
        $res = $this->doShell($cmd);
        Log::info("执行shell命令返回数据:".json_encode($res));

        //返回信息,给GitHub记录
        print_r(json_encode($res));
    }else{
        Log::info("钩子请求签名验证失败");
    }
}

//执行shell命令
private function doShell($cmd, $cwd = null) {
    $descriptorspec = array(
        0 => array("pipe", "r"), // stdin
        1 => array("pipe", "w"), // stdout
        2 => array("pipe", "w"), // stderr
    );
    $proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, null);
    // $proc为false,表明命令执行失败
    if ($proc == false) {
        Log::info("shell 命令:".$cmd." 执行失败");
        return false;
    } else {
        $stdout = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $stderr = stream_get_contents($pipes[2]);
        fclose($pipes[2]);
        $status = proc_close($proc); // 释放proc
    }
    $data = array(
        'stdout' => $stdout, // 标准输出
        'stderr' => $stderr, // 错误输出
        'retval' => $status, // 返回值
    );

    return $data;
}

GitHub官方建议不要把Secret放到代码里面,官方是建议把Secret放到环境变量里面

把这个代码放到任意一个控制器中就行,然后配置好路由就行。该方法是POST请求,所以你不能验证CSRF-TOKEN,我是直接把这个路由放到api路由下面的

如果你不只是拉取代码,如还需要打包等,就可以先写一个shell脚本,然后在pull后执行脚本,cd $webPath && git pull origin master && /bin/bash dabao.sh

设置Webhooks

这里面的坑

如果发现推送返回以下的错误信息:

Host key verification failed.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.

首先判断你执行脚本的用户(我的是www用户)的ssh-key是否加到了GitHub里面,如果没有就生成加入。

//为www用户生成ssh-key
sudo -Hu www ssh-keygen -t rsa -C "github账号的邮箱"# 请选择 "no passphrase",一直回车下去
//我是用的root用户给www用户生成ssh-key的,如果你是登录的就是www用户,执行下面的命令就行
ssh-keygen -t rsa -C "github账号的邮箱"

-Hu www 命令: 
-u 代表切换到哪一个用户,这里说的是www 
-H 代表切换HOME环境变量的值,也就是password文件中www用户对应的home目录

百度上面基本就是说没有配置ssh-key,但是我配置了的,还是会返回这个错误。

这个是因为你执行脚本的用户,是第一次连接GitHub,所以会弹出一个是否把远端地址加入到konw_host,我们执行的shell是回车操作,但是直接回车,则默认没有权限写入,必须输入yes才能正确写入 konw_host

解决方法:

  1. 1.我们可以在ssh_config配置文件中降低安全配置级别,不做提示,直接加入到konw_host

     #打开ssh_config
     /etc/ssh/ssh_config
     #找到:
     StrictHostKeyChecking ask
     修改为:
     StrictHostKeyChecking no

    但是我不建议这样做。或者当加入到konw_host后,你再把安全配置改回来。

  2. 2.登陆你执行脚本的用户,然后使用这个用户去项目库里面拉取一次,然后你输入yes就行了。

更多文章参考我的博客

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1
thebestxt

之前尝试过用钩子来实现自动部署,踩的坑主要是php执行shell命令的部分,主要是php、nginx、git的用户掰不清除了。后来自己做完了,也没来得及及时记录下来。建议作者把这部分的配置详细写一写呀。

另外附上一篇自己折腾自动部署时候写的文章 xtzero.me/2020/04/15/byegithub20200...

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!