目的
通過github管理的項(xiàng)目嫌褪,每次push后都需要到服務(wù)器pull代碼嵌言,然后手動(dòng)打包梯浪,重復(fù)的操作挺糟心的涂屁。但是github提供了Webhooks,可以實(shí)現(xiàn)本地push代碼到遠(yuǎn)程后,服務(wù)器自動(dòng)拉取遠(yuǎn)程代碼洁桌,自動(dòng)打包部署尸折,節(jié)省大量時(shí)間。
原理
github的Webhooks允許用戶設(shè)置post請求的接口地址庆冕,當(dāng)本地代碼推送到遠(yuǎn)程分支后康吵,github將主動(dòng)調(diào)用設(shè)置的接口,并將當(dāng)前修改相關(guān)信息作為請求參數(shù)給到接口访递。接口在服務(wù)器上被調(diào)用的時(shí)候晦嵌,就知道當(dāng)前遠(yuǎn)程代碼庫有更新,此時(shí)可以調(diào)用腳本執(zhí)行更新拷姿、打包等操作惭载。
用到的技術(shù)
接下來我會(huì)通過nodejs (opens new window)及github-webhook-handler (opens new window)模塊實(shí)現(xiàn)監(jiān)聽github代碼push的事件,然后通過shell (opens new window)腳本實(shí)現(xiàn)代碼更新打包的操作响巢。
前提條件
- 具有公網(wǎng)ip的云服務(wù)器
- github倉庫
提供接口給github
接口的地址path通過github-webhook-handler模塊配置產(chǎn)生描滔,不需要自己額外的定義路由地址;同時(shí)需要設(shè)置secret踪古,用于github校驗(yàn)含长。
1靶衍、通過createHandler方法,可以配置path和secret茎芋,這兩個(gè)參數(shù)自己隨便定義就可以。
const createHandler = require('github-webhook-handler')
const handler = createHandler({ path: '/webhook', secret: 'sdsdfssdfsdf' })
2蜈出、監(jiān)聽push事件
handler.on('push', event=> {
// github推送通知到接口后田弥,會(huì)觸發(fā)該方法,并拿到github請求的參數(shù)
});
3铡原、監(jiān)聽error事件
handler.on('error', function (err) {
console.error('Error:', err.message)
});
4偷厦、通過node的http模塊啟動(dòng)服務(wù)
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編寫燕刻,所以我需要做的有三個(gè)步驟:
- 進(jìn)入到服務(wù)器上的vuepress項(xiàng)目
- 通過git pull更新代碼
- 通過npm run docs:build打包代碼
當(dāng)然只泼,如果還有其他腳本也可以方進(jìn)入,完整腳本如下:
#!/bin/bash
# 更新代碼
cd /xxx/xxx/項(xiàng)目名
git pull
npm run docs:build
6卵洗、監(jiān)聽到push事件后的處理
當(dāng)監(jiān)聽到push事件后请唱,event的payload
參數(shù)中包含了github請求接口時(shí)的參數(shù),而我只想處理master分支代碼更新后过蹂,服務(wù)器自動(dòng)拉取代碼十绑,因此只判斷了event.payload.ref === 'refs/heads/master'
時(shí)運(yùn)行腳本;
通過node的require('child_process').spawn
方法可以運(yùn)行腳本酷勺,具體使用方法可見:child_process spawn(opens new window)
指定運(yùn)行'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}】
- 是否需要自動(dòng)部署:${autoRun}】;
`);
// 判斷是否需要自動(dòng)部署
if (!autoRun) {
return
}
console.log('開始執(zhí)行腳本');
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運(yùn)行項(xiàng)目
通過pm2可以讓項(xiàng)目在服務(wù)器上一直運(yùn)行击胜,不會(huì)關(guān)閉服務(wù)器后項(xiàng)目就停亏狰。 在此,通過腳本實(shí)現(xiàn)接口項(xiàng)目的啟動(dòng)潜的,在源碼的bin/prod.sh (opens new window)中有如下腳本:
pm2 start npm -i 1 --name 'auto-run-github' -- run start
8骚揍、在github的webhooks中配置接口
如下圖所示
- Payload URL為你的接口地址,結(jié)尾應(yīng)該是github-webhook-handler設(shè)置的path參數(shù)結(jié)尾啰挪;
- 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}】
- 是否需要自動(dòng)部署:${autoRun}】;
`);
// 判斷是否需要自動(dòng)部署
if (!autoRun) {
return
}
console.log('開始執(zhí)行腳本');
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/');
});