GitHub/GitLab Webhook 实践

Posted by Nutlee on 2016-12-19

码农这个行业里前端是个另类,在大牛眼里前端“毫无难度”可言,在小白眼里前端炫酷的“所见即所得”。也许正是因为前端的低门槛、不需要什么复杂的开发环境甚至有“记事本”就能开发,以至于很多小公司的前端工程师从未正视过前端部署问题(包括我所在的公司)。
小公司人力有限,所以多数小公司的技术团队配置都是后端居多,在后端眼里前端就切切页面然后往服务器丢一下就好了嘛。然而就是这个问题困扰了我很久,我一直觉着这样 FTP 的方式往服务器拖前端内容“非常傻”,又苦于没有太好的思路和解决方案(其实主要受制于不懂后端🌚)。
直到…….看到了 Webhook …..

原理

其实网上这类教程很多了,但是多数写的都是思路和大概的流程,毕竟咱都是小白爬上来的,深知宝宝们心里有多苦😢,所以本文就详细说一下吧。

基本原理是后端开一个单独的端口跑一个服务器,只监听固定的接口请求(A接口),通过 GitHub 的 Webhook 功能关联上此接口,这样每次向 GitHub 提交东西都是自动发出一个 post 请求到 A 接口,A接口收到 post 会执行一个 shell 脚本,自动通过 Git 方式拉取 GitHub 上的静态资源(HTML、CSS、JS)

所以分解一下就是三个过程

  1. 目标位置创建 Git 仓库
  2. shell 脚本
  3. 服务端监听接口post(Node、PHP等后端语言均可),GitHub/GitLab 开启 Webhook

操作

以 Node.js 的后端环境为例,假设正式服务器是基于 express 在 /root 目录下创建 testWebhook 项目

目标位置创建 Git 仓库

首先你要在你想要需要部署静态资源的位置创建 Git 仓库,这里可以直接使用 git clone 去直接 clone 并关联,不推荐 git initgit remote add,因为对于此处而言只是要不断的 检出 新版本,并不需要对远端仓库有什么提交性质的操作。具体的作用看下一步的 shell 脚本就明白了。

1
2
3
cd /root/testWebhook/public
git clone git@github.com:***/***.git

shell 脚本

新建 deploy.sh 内容如下,主要功能就是 GitHub post 过来的时候,目标路径去远端的 Git 仓库更新代码并 checkout 最新版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
WEB_PATH='/root/testWebhook/public/testWebhook'
WEB_USER='root'
WEB_USERGROUP='root'
echo "Start deployment"
cd $WEB_PATH
echo "pulling source code..."
git reset --hard origin/master
git clean -f
git pull
git checkout master
echo "changing permissions..."
chown -R $WEB_USER:$WEB_USERGROUP $WEB_PATH
echo "Finished."

开启服务器监听 post

创建一个简单的 Node 服务器,只监听 GitHub Webhook,每次接口被触发的时候执行本地 deploy.sh,具体步骤如下:

  • 初始化 npm,然后本地安装 github-webhook-handler 中间件,用来配置 GitHub Webhook ,此插件可以配置 secret 参数,这个参数是用来验证接口安全的,具体后面再说。

    其实这个地方很多教程都说要全局安装 github-webhook-handler,可是我实在不明白为什么要全局安装,而且我尝试过全局安装 require 的时候会提示模块不存在,还请知道的同学指点一二。

    1
    2
    3
    4
    npm init
    npm install github-webhook-handler --save-dev
    # GitLab 中
    # npm install gitlab-webhook-handler --save-dev

    你甚至可以将上一步的 shell 也放在这个文件夹中,方便统一管理。

  • 开启服务器监听 post

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    var http = require('http')
    var createHandler = require('github-webhook-handler')
    // GitLab 中
    // var createHandler = require('gitlab-webhook-handler')
    var handler = createHandler({ path: '/testwebhook', secret: 'testwebhook' })
    // 上面的 secret 保持和 GitHub 后台设置的一致
    function run_cmd(cmd, args, callback) {
    var spawn = require('child_process').spawn;
    var child = spawn(cmd, args);
    var resp = "";
    child.stdout.on('data', function(buffer) { resp += buffer.toString(); });
    child.stdout.on('end', function() { callback (resp) });
    }
    http.createServer(function (req, res) {
    handler(req, res, function (err) {
    res.statusCode = 404
    res.end('no such location')
    })
    }).listen(7777)
    handler.on('error', function (err) {
    console.error('Error:', err.message)
    })
    handler.on('push', function (event) {
    console.log('Received a push event for %s to %s',
    event.payload.repository.name,
    event.payload.ref);
    run_cmd('sh', ['./deploy.sh'], function(text){ console.log(text) });
    // 此处相对路径必须 './' 开头
    })
    /*
    handler.on('issues', function (event) {
    console.log('Received an issue event for % action=%s: #%d %s',
    event.payload.repository.name,
    event.payload.action,
    event.payload.issue.number,
    event.payload.issue.title)
    })
    */

    写好就可以启动了

    1
    2
    #启动监听
    node deploy.js
  • GitHub/GitLab 中的设置

    在 GitHub 中点击仓库的 Settings/Webhooks,如图

    PlayloadURL 上填你的服务器IP+端口+接口,如“ http://192.168.0.1:3000/testwebhook
    Secret 填约定密钥,这样保证只有你从 GitHub 往你的服务器 Post 时是可信赖的。

    填好后,GitHub 就立即 Post,你可以看到是否成功,如果显示为一个红色的 ”X“ ,那就要排查一下是不是哪里错了,通常都是 deploy.js 或者 deploy.sh 中的路径错误。

    如果你使用的 GitLab,设置基本相同。

补充黑科技

用 Node 的同学应该都会遇到和我一样的困惑,这个 Node 每次启动了都是前台运行啊,那岂不是我的 bash 一直不能关闭,一直开着??当然不是!这就要祭出 Linux 上常用到的黑科技了 —- forever 进程守护了

简单说 forever 可以挂起任务,让它们后台运行,使用也很简单

1
2
3
4
5
6
7
8
9
10
11
12
# 安装
$ npm install -g forever
# forever 列表
$ forever list
# 启动
$ forever deploy.js
# 根据列表中的 pid 结束任务
$ forever stop pid
# 重启
$ forever restart pid
# 重启
$ forever restartall

再次补充说明

如果用 CentOS6 的同学连不通的时候注意一下防火墙是否开放了端口,操作如下

1
2
3
4
5
6
7
8
# 开放7000端口
$ /sbin/iptables -I INPUT -p tcp --dport 7000 -j ACCEPT
# 保存
$ /etc/init.d/iptables save
# 查看端口状态
$ /etc/init.d/iptables status
# 重启防火墙
$ /etc/init.d/iptables restart

上述配置文件地址


参考资料:

使用 GitHub / GitLab 的 Webhooks 进行网站自动化部署
Webhook 实践 —— 自动部署
利用Github的Webhook功能和Node.js完成项目的自动部署
CentOS防火墙开放和关闭端口(iptables)