点击查看源码
目的
通过github管理的项目,每次push后都需要到服务器pull代码,然后手动打包,重复的操作挺糟心的。但是github提供了Webhooks,可以实现本地push代码到远程后,服务器自动拉取远程代码,自动打包部署,节省大量时间。
原理
github的Webhooks允许用户设置post请求的接口地址,当本地代码推送到远程分支后,github将主动调用设置的接口,并将当前修改相关信息作为请求参数给到接口。接口在服务器上被调用的时候,就知道当前远程代码库有更新,此时可以调用脚本执行更新、打包等操作。
用到的技术
接下来我会通过nodejs (opens new window)及github-webhook-handler (opens new window)模块实现监听github代码push的事件,然后通过shell (opens new window)脚本实现代码更新打包的操作。
前提条件
- 具有公网ip的云服务器
- github仓库
提供接口给github
接口的地址path通过github-webhook-handler模块配置产生,不需要自己额外的定义路由地址;同时需要设置secret,用于github校验。
1、通过createHandler方法,可以配置path和secret,这两个参数自己随便定义就可以。
const createHandler = require('github-webhook-handler')
const handler = createHandler({ path: '/webhook', secret: 'sdsdfssdfsdf' })
2、监听push事件
handler.on('push', event=> {
// github推送通知到接口后,会触发该方法,并拿到github请求的参数
});
3、监听error事件
handler.on('error', function (err) {
console.error('Error:', err.message)
});
4、通过node的http模块启动服务
http.createServer( (req, res)=> {
handler(req, res, function (err) {
console.log('err',err);
res.statusCode = 404
res.end('api 404')
});
}).listen(3001,()=>{
console.log('running in http://127.0.0.1:3001/');
});
5、编写名为pull.sh的脚本
我的文章通过vuepress编写,所以我需要做的有三个步骤:
- 进入到服务器上的vuepress项目
- 通过git pull更新代码
- 通过npm run docs:build打包代码
当然,如果还有其他脚本也可以方进入,完整脚本如下:
#!/bin/bash
# 更新代码
cd /xxx/xxx/项目名
git pull
npm run docs:build
6、监听到push事件后的处理
当监听到push事件后,event的payload
参数中包含了github请求接口时的参数,而我只想处理master分支代码更新后,服务器自动拉取代码,因此只判断了event.payload.ref === 'refs/heads/master'
时运行脚本;
通过node的require('child_process').spawn
方法可以运行脚本,具体使用方法可见:child_process spawn(opens new window)
指定运行'sh'脚本,脚本路径为./bin/pull.sh
,即可上面写的脚本文件。
spawn('sh', ['./bin/pull.sh']);
handler.on('push', event=> {
try {
const {repository,ref} = event.payload;
const {full_name,name,private,size} = repository;
const autoRun = ref === 'refs/heads/master';
console.info(`
- 接收到仓库:【${full_name}】的推送消息;
- 修改分支:【${ref}】;
- 仓库是否私有:${private};
- 大小:【${size}】
- 是否需要自动部署:${autoRun}】;
`);
// 判断是否需要自动部署
if (!autoRun) {
return
}
console.log('开始执行脚本');
const s = spawn('sh', ['./bin/pull.sh']);
s.stdout.on('data', (data) => {
console.log(`${name}:${data}`);
});
s.stderr.on('data', (data) => {
console.log(`${name}: ${data}`);
});
console.log('has rebuild');
} catch (e) {
console.log('build error',e)
}
});
7、使用pm2运行项目
通过pm2可以让项目在服务器上一直运行,不会关闭服务器后项目就停。 在此,通过脚本实现接口项目的启动,在源码的bin/prod.sh (opens new window)中有如下脚本:
pm2 start npm -i 1 --name 'auto-run-github' -- run start
8、在github的webhooks中配置接口
如下图所示
- Payload URL为你的接口地址,结尾应该是github-webhook-handler设置的path参数结尾;
- Content type选择application/json;
-
Secret要和github配置保持一致;
完整代码如下:
const http = require('http')
const createHandler = require('github-webhook-handler')
const handler = createHandler({ path: '/webhook', secret: 'sdsdfssdfsdf' })
const spawn = require('child_process').spawn;
handler.on('error', function (err) {
console.error('Error:', err.message)
});
handler.on('push', event=> {
try {
const {repository,ref} = event.payload;
const {full_name,name,private,size} = repository;
const autoRun = ref === 'refs/heads/master';
console.info(`
- 接收到仓库:【${full_name}】的推送消息;
- 修改分支:【${ref}】;
- 仓库是否私有:${private};
- 大小:【${size}】
- 是否需要自动部署:${autoRun}】;
`);
// 判断是否需要自动部署
if (!autoRun) {
return
}
console.log('开始执行脚本');
const s = spawn('sh', ['./bin/pull.sh']);
s.stdout.on('data', (data) => {
console.log(`${name}:${data}`);
});
s.stderr.on('data', (data) => {
console.log(`${name}: ${data}`);
});
console.log('has rebuild');
} catch (e) {
console.log('build error',e)
}
});
http.createServer( (req, res)=> {
handler(req, res, function (err) {
console.log('err',err);
res.statusCode = 404
res.end('api 404')
});
}).listen(3001,()=>{
console.log('running in http://127.0.0.1:3001/');
});