Hugh's Blog

使用 GitHub Webhooks 部署网站

之前在更新文章的过程中,需要做的事情有两件:备份文章到 GitHub 和本地创建静态文件并提交到服务器 (Git Hooks),感觉上还是有点麻烦,而且 VPS 已经禁止密码登录 (SSH 密钥登录),而刚好 GitHub 为每个仓库提供了 Webhook 功能,简单来说,当 push 事件触发时,GitHub 会发送一个 POST 请求到我们配置的地址,这样当我们提交之后就可以通过脚本来自动化部署网站,部署过程也不麻烦,做个记录。

所有的脚本都是在当前用户 (非 root) 下进行的,这个可能需要注意下

以本网站的代码仓库为例:blog

服务器配置

由于生成静态文件需要 pull 最新的文件,所以把 Blog 仓库拉到服务器

# 进入到网站目录,假设网站目录是 /var/www/html/blog/static
cd /var/www/html/blog
git clone https://github.com/hughshen/blog.git repo

由于我是用 Node.js 来生成静态文件,所以就以 Node.js 来做 Webhook 响应,中间使用了第三方库 github-webhook-handler 来处理 push 事件,Node.js 安装可以看这里

mkdir ~/blog-build
cd ~/blog-build
# 初始化
npm init -y
npm install github-webhook-handler --save
# Node.js 端口监听文件
touch index.js
# 自动化部署脚本
touch build.sh
chmod a+x build.sh

index.js 内容

const http = require('http');
const port = 1234;
const exec = require('child_process').exec;
const createHandler = require('github-webhook-handler');
const handler = createHandler({
    path: '/webhook',
    secret: 'your_secret'
});

http.createServer((req, res) => {
    handler(req, res, (err) => {
        res.statusCode = 404;
        res.end('no such location');
    });
}).listen(port, 'localhost');

handler.on('error', (err) => {
    console.error('Error:', err.message);
});

handler.on('push', (event) => {
    exec('~/blog-build/build.sh', (err, stdout, stderr) => {
        console.log('stdout: ' + stdout);
        console.log('stderr: ' + stderr);
        if (err !== null) {
            console.log('exec error: ' + err);
        }
    });
    console.log('Received a push event for %s to %s',
        event.payload.repository.name,
        event.payload.ref
    );
});

build.sh 内容

#!/bin/bash

CurrentPath=`pwd`
GitPath='/var/www/html/blog/repo/'

cd $GitPath
git pull origin master
npm install
node build.js
cp -rf ./static/* ../static/
rm -rf ./static

cd $CurrentPath
exit 0

上面的 index.js 监听的 1234 端口只允许本服务器访问,还需要修改 nginx 的配置使得可以外部访问

# ...
location ~ /webhook {
	# 只允许 POST 请求
    if ($request_method !~ ^(POST)$) {
        return  444;
    }
    proxy_pass http://127.0.0.1:1234;
}
# ...

重启 nginx,接下来启动 index.js

nohup /usr/bin/node /home/user/blog-build/index.js >/dev/null 2>&1 &

添加到自启动

添加 index.js 到自启动我使用的是 crontab @reboot 命令,使用 crontab -e 添加下面一行,网上也有说可以用 su - user -c "command" 添加到 rc.local 的,但是我添加了重启过几次没有生效

# ...
@reboot nohup /usr/bin/node /home/user/blog-build/index.js >/dev/null 2>&1 &

GitHub 配置

GitHub 开启 Webhook 很简单,找到仓库 Settings -> Webhooks -> Add webhook

Payload URL: http://example.com/webhook

Content type: application/json

Secret: your_secret

添加完之后 GitHub 会发送一个测试的请求,失败的话可以找下问题,然后点击 Redeliver 再次发送

小结

总的来说,配置过程也简单,如果项目是依赖于 GitHub 或者有关的话,使用 Webhooks 是很方便的,一切都自动化,只需提交代码就行。


参考

使用Github的webhooks进行网站自动化部署

How do I use the nohup command without getting nohup.out?