背景描述
我的服务器上面部署了几个网站应用,拿我的应用在线书签管理系统来说,有一个PC版本,这个是后台跟前端代码在同一个项目的。每一次不管是更新后台代码还是前端代码,我都要使用ssh服务连接到服务器,更新代码然后重新执行命令pm2 restart my-bookmark
重启服务。还有一个Mobile版本的,这个版本只是一个前端代码,这个使用Vue
开发的,由于我的服务器内存只有1G,导致无法在服务器上打包,所以我需要在本地机器上打完包之后再把最终文件推送到Github,然后再登陆服务器把我推送到Github上的代码clone下来,完成更新。有时候要是经常更新,这种繁琐的重复性操作实在是无聊而且耽误时间。为了解决这题,今天研究了一下Github的Webhook服务,彻底解决此问题。做到的效果就是,当我对 Github 推送 push 代码的时候,我的服务器就自动 git pull 最新代码,然后重启服务(如果需要)。
Webhook配置
进入你的GitHub项目,点 Settings --> Webhooks
进入配置页面,大概如下所示:
稍微解释一下几个配置的意义:
- Payload URL:这是你服务器的处理url。比如
http://webhook.lcq.com
,http:://111.231.72.58:7777/webhook
。 - Content Type:按照自己需求选吧。我找的一个 NPM 包是需要选择
application/json
类型的。 - Secret:密钥。自己填就好了,待会儿服务器要用到的。有了这个密钥,其他人随便给你发的就会触发error了。
- Which events would you like to trigger this webhook:我其实只要 push 事件通知我就好了。测试的话,你可以选第二个全部试试。第三个就是更加精细的定制化了。
- Recent Deliveries:这是触发日志事件。成功失败都有详细信息提示。
更加详细的一些信息,参考 Github 的 Webhook 官方文档。
服务器实现
我自己也懒得写了,是用了一个别人写好的 NPM 包 github-webhook-handler。如果你不熟悉Node.js,那么你自己去找相应语言的开源实现,实在找不到,按照官方文档自己写一个也不难的。原理反正就是能接收Post请求就行了。我自己利用那个 NPM 的包服务端大概简单实现如下所示:
var http = require('http')
const { exec } = require("child_process");
var createHandler = require('github-webhook-handler')
// path 请按照实际情况填写,secret 就是你在GitHub设置webhook的时候自己填的
var handler = createHandler({ path: '/', secret: 'you secret' })
// 每个项目有推送的时候,相对应的命令执行。
const repUpdate = {
"webhook": "cd /var/www/webhook && git fetch && git reset --hard origin/master && pm2 restart webhook", // 不要忘了,自更新
"blog-static": "cd /var/www/static/blog && git fetch && git reset --hard origin/master",
"my-bookmark": "cd /var/www/my-bookmark && git fetch && git reset --hard origin/master && pm2 restart my-bookmark",
"mobile-my-bookmark-static": "cd /var/www/mobile-my-bookmark/dist && git fetch && git reset --hard origin/master"
}
function excute(repName) {
let cmd = repUpdate[repName];
if (!cmd) return;
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.log(`exec error: ${error}, ${stdout}, ${stderr}`);
}
});
}
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)
})
// push 事件触发
handler.on('push', function (event) {
console.log('Received a push event for %s to %s', event.payload.repository.name, event.payload.ref)
excute(event.payload.repository.name);
})
然后,简单测试了一下,满足了自己的需求。爽,哈哈哈!当然,这只是一种简单的需求,我只处理了push事件。还可以利用这个做一些复杂需求,比如团队中有谁提交,有谁发pull Request,都可以发邮件给其他成员等等。大家根据需求挖掘吧。