本系列前面的文章,主要集中在国外的服务器,本文尝试在国内git托管平台Gitee上进行实验。
Gitee官网为:https://gitee.com。因其在国内,对于速度要求较高的团队,可以考虑该平台。
Gitee拥有很多第三方集成,如Jenkins、阿里云、华为、Azure等,同时也提供了WebHook(Web钩子)。WebHook可以理解为一个处理post请求的机制,甚至简单理解为一个http地址。当提交代码后,Gitee会自动回调这个地址,这个地址需要我们编写程序进响应,以便处理(如编译代码、发邮件,自动部署等)。WebHook支持很多种触发事件,如Push、Tag Push、Issue等,可根据需要选择。
本文抛却第三方集成,使用自己写程序的来测试WebHook。以一个简单的实用功能为例:当提交代码到Gitee仓库后,自动发送通知邮件。
本文需要具备云主机服务器,以便提供公网的响应地址。如无,考虑第三方集成服务。
WebHook数据格式说明在这里。
WebHook数据类型分头部(Request Headers)和数据体(Request Payload)。
头部说明如下:
Content-Type: application/json # 默认为 application/json , 若是旧版钩子(已不维护)为 application/x-www-form-urlencoded
User-Agent: git-oschina-hook # 固定为 git-oschina-hook,可用于标识为来自 gitee 的请求
X-Gitee-Token: webhook password # 用户新建 WebHook 时提供的密码
X-Gitee-Event: Merge Request Hook # 标识触发的钩子类型
不过在实测中没有找到方法读取。
不同的钩子类型,其数据体亦不同。具体参考官方示例,以Push类型为例进行简单说明:
当仓库有Push时,Gitee会自动将上述信息post到指定的地址,我们获取消息体并解析出来需要的字段:
仓库名称、提交者、提交日志、提交时间。
将这些信息组装后,发送到指定邮箱地址中。完成本文提到的功能。
下面使用NodeJS来实现。需要依赖的库有koa(提供web服务)以及nodemailer(提供email功能)。
package.json文件内容:
{
"name": "foobar",
"version": "0.0.1",
"description": "hello world",
"main": "server.js",
"scripts": {
"test": "run.sh"
},
"author": "Late Lee",
"license": "MIT",
"dependencies": {
"koa": "^2.11.0",
"koa-bodyparser": "^4.2.1",
"koa-router": "^7.4.0",
"nodemailer": "^6.3.1"
}
}
实现文件:
/*
文件名:server.js
功能:
gitee仓库Webhook应用实例:提交代码时发送邮件。
*/
const koa_router = require("koa-router");
const Koa = require("koa");
const koa_bodyparser = require("koa-bodyparser");
const router = koa_router();
const nodemailer = require("nodemailer");
const g_port = 4000;
// 创建与邮件对应列表
var g_list = [
["latelee/webhook.git", "[email protected]"],
["latelee/autoci.git", "[email protected]"],
["", "[email protected]"] // 最后一个为默认邮箱
];
// 参数:发件人名称,收件人,主题,正文(支持html格式)
function sendMail(aliasName, tos, subject, msg)
{
var from = "[email protected]";
const smtpTransport = nodemailer.createTransport({
host: 'smtp.exmail.qq.com',
secureConnection: true, // use SSL
secure: true,
port: 465,
auth: {
user: from,
pass: '1qaz@WSX',
}
});
smtpTransport.sendMail({
from : aliasName + ' ' + '<' + from + '>',
to : tos,
subject : subject,
html : msg
}, function(err, res) {
if (err)
{
console.log('error: ', err);
}
});
}
function nl2br(str, isXhtml) {
var breakTag = (isXhtml || typeof isXhtml === 'undefined') ? '
' : '
';
var str = (str + '').replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
};
// 响应地址为foobar
router.post("/foobar", async (ctx) => {
var ret = 0;
var passwd = ctx.request.body.password;
console.log(`got reuqest. body:`);
console.log(ctx.request.body);
// 在设置WebHook时指定,此处判断,不合法直接返回
if (passwd != "helloworldpasswd")
{
ret = -1;
}
else
{
var commit = ctx.request.body.commits[0];
var respo = ctx.request.body.repository;
var url = respo.git_http_url;
var output = new Object();
output = '' + respo.name + '仓库代码有提交,请及时更新。
地址为:' + url + '
';
output += 'Committer:
' + commit.committer.username + "
";
output += 'Commit time:
' + commit.timestamp + "
";
output += 'Commit log:
' + nl2br(commit.message) + "
";
console.log(output);
// 匹配仓库及对应的邮箱列表
for (var i = 0; i < g_list.length; i++)
{
//console.log(`item: ${item[0]}, ${item[1]}`);
var found = url.includes(g_list[i][0]);
if (found)
{
console.log(`found at ${i} of ${g_list.length}`);
break;
}
}
console.log(`will sendto: ${g_list[i][1]}`);
// 发邮件通知
// TODO:参数可配置
sendMail('CI自动邮件通知', g_list[i][1], respo.name + '仓库代码更新', output);
}
var res = new Object();
res['ret'] = ret;
res['timestamp'] = Date.now(); // 当前时间戳
ctx.body = res; // 返回的是json,以便gitee获取,否则可能认为失败
});
function main()
{
var app = new Koa();
app.use(koa_bodyparser({
enableTypes:["json","test","form"],
onerror:function (err,ctx){
console.log("api service body parse error",err);
ctx.throw(400,"body parse error");
},
}));
app.use(router.routes());
app.listen(g_port);
console.log('Running a koa server at localhost[v1.0]: ', g_port)
}
main();
代码说明:
在服务器运行:
npm install
node server.js
即可启动服务。
在Gitee上建立仓库后,进入WebHook配置界面,过程如图1所示:
图1
WebHook配置过程如图2所示:
图2
点击添加后,可以进行测试,如果没有运行服务,则连接测试失败。成功会显示返回值。
当提交代码后(触发Push钩子),服务器响应信息如图3所示:
图3
根据Gitee文档描述,执行的超时时长为5秒。笔者仅做简单测试,在其范围之内。
发件人无法自由快捷选择。只能通过列表进行匹配。
本文的方法,在小团队中可使用。如果复杂大型项目,考虑Jenkins等第三方服务。