作者:black丶烽火 | 来源:互联网 | 2023-09-10 21:46
GitHub的网络钩子(webhook)功能,可以很方便的实现自动化部署。本文记录了使用Node.js的开发部署过程,当项目的master分支被推时,将在服务器进行自动部署,完整代
GitHub 的网络钩子(webhook)功能,可以很方便的实现自动化部署。本文记录了使用 Node.js 的开发部署过程,当项目的 master 分支被推时,将在服务器进行自动部署,完整代码见 GitHub
添加网络钩子
在 GitHub 的相应项目首页,点击右上角菜单 Setting
, 点击左侧菜单 Webhooks
,点击右上角按钮 Add webhook
设置 Payload URL
为接收事件的地址,Content type
建议选择 applicaiton/json
,Secret
可选填任意字符串,Which events would you like to trigger this webhook?
设为 Just the push event.
,勾选 Active
,点击下方的 Add webhook
按钮
开发处理请求
接收请求
使用 Node.js 建立一个 http 服务器,接收 POST 请求并处理其提交数据
const { createServer } = require('http');
const port = process.env.GITHUB_WEBHOOK_PORT || '3000';
const server = createServer((req, res) => {
if('POST' === req.method){
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
});
}
})
server.listen(port, () => {
console.log(`Listening on ${port}`);
});
如果需要更改默认端口 3000,可以先运行以下命令添加环境变量(NUMBER 为任意端口)
export GITHUB_WEBHOOK_PORT=NUMBER
解析 Body
在 req 的 end 事件处理器中,把字符串 body 解析成对象
req.on('end', () => {
try{
body = JSON.parse(decodeURIComponent(body).replace(/^payload=/, ''));
}catch(e){
console.log(e)
}
如果 Content type
设置为 applicaiton/json
,只需要 body = JSON.parse(body)
即可,以上代码兼容了 Content type
设置为 application/x-www-form-urlencoded
的情况
拉取更新
根据 body 的 push 负载,提取项目和分支信息,如果是 master 分支,则执行进入对应项目,拉取分支的命令
if('object' === typeof body){
if('refs/heads/master' === body.ref){
const { exec } = require('child_process');
const command = `cd ../${body.repository.name} && git pull origin master`;
exec(command, (error, stdout, stderr) => {
});
注意这里的项目所在的目录,与此应用所在的目录,是在同一个父目录下的,如果不是可以相应调整命令的进入路径
验证密钥
以上步骤已经实现了自动拉取更新,不过存在安全性的问题,因为不仅仅 GitHub 可以发送这样的请求,所以最好设置 Secret 以进行安全验证
const secret = process.env.GITHUB_WEBHOOK_SECRET || '';
...
req.on('end', () => {
if('' !== secret){
const { createHmac } = require('crypto');
let signature = createHmac('sha1', secret).update(body).digest('hex');
if(req.headers['x-hub-signature'] !== `sha1=${signature}`){
console.log('Signature Error');
res.statusCode = 403;
res.end();
return;
}
}
运行应用前,先运行以下命令增加密钥变量(STRING 为任意字符串)
export GITHUB_WEBHOOK_SECRET=STRING
设置了 Secret 后,GitHub 在发送请求时,会在请求头增加 x-hub-signature 为 sha1=SIGNATURE, 其中 SIGNATURE 为 body 的 密钥为 Secret,算法为 sha1 的 HMAC 16 进制值
通过对 Secret 的检验,可以确保只有知道了 Secret,才能发送正确的带 x-hub-signature 头的请求,否则将拒绝请求
以上代码兼容了不设置 Secret 的情况,即如果没有增加变量 GITHUB_WEBHOOK_SECRET,则按原有逻辑处理,不会进行检验
本地钩子构建
如果项目在拉取更新后需要构建,那么可以 command 变量后面加上构建命令,例如 && npm run build
,但是不同项目的构建命令有可能是不一样的,而且有的项目的构建命令可能还比较复杂,这些情况下可以通过设置 git 的本地钩子进行处理
cd /PATH/TO/PROJECT/.git/hooks
nano post-merge
#!/bin/sh
SHELL_SCRIPT
chmod +x post-merge
其中 /PATH/TO/PROJECT/ 为项目的目录位置,SHELL_SCRIPT 可以为任意 Shell 脚本
因为 git pull 是 git fetch 和 git merge 的组合,所以拉取更新会触发 post-merge 钩子
默认新增的文件是没有执行权限的,所以需要通过 chmod
增加 x
位
部署应用上线
应用部署上线需要实现持久化和自动化,即项目应该一直在运行,如果服务器重启,项目应该自动启动
变量自动创建
/etc/profile.d/ 里的变量创建脚本会在服务器重启时自动运行,所以添加一个设置脚本进去
nono /etc/profile.d/github-webhook.sh
export GITHUB_WEBHOOK_PORT=NUMBER
export GITHUB_WEBHOOK_SECRET=STRING
运行以下命令可以使变量创建马上生效
source /etc/profile
pm2 运行应用
pm2 可以确保 Node 应用的持续运行,并可通过配置实现监控和热更新等功能
npm install pm2 -g
pm2 start app.js --name github-webhook
重启自动运行
pm2 还内置支持配置自启动原有应用,通过以下命令实现
pm2 startup
pm2 save
pm2 startup
会创建并开启开机自动运行的服务, pm2 save
会保存当前的 pm2 运行应用,作为重启后的恢复内容
总结
在基于 GitHub webhook 的自动化部署中,主要使用了以下技术:
Node.js 的 http,child_process 和 crypto 模块
Git 的 post-merge Shell 钩子
profile 的自动变量设置和 pm2 工具
利用 Github 网络钩子实现自动化部署的相关教程结束。