webhook搭建

webhook搭建

WebHook

网页开发中的网络钩子(Webhook)是一种通过自定义回调函数来增加或更改网页表现的方法。这些回调可被可能与原始网站或应用相关的第三方用户及开发者保存、修改与管理。术语“网络钩子”由杰夫·林德塞(Jeff Lindsay)于 2007 年通过给计算机编程术语“钩子”(Hook)加上前缀得来

为什么我需要搭建一个 webhook

那可要从搭建这个博客系统说起了,由于该博客使用 hexo 搭建,所以每次更新博客需要部署一下然后才能生效。而我又不愿意每次写完都去部署,这样就埋下了一个 webhook 的坑!其实 webhook 就是一个回调地址每次推送都访问下那个地址然后做部署

搭建 webhook

其实搭建这个有很多第三方的库已经支持了,但是偏偏我选择了 gitee(可恶啊!!!)

1. 设置 gitee 的签名


如果你是 java 或者 python 搭建 webhoook gitee 有事例代码
下面使用 go 语言实现

1
2
3
4
5
6
7
8
9
// Sign secret 为你在gitee设置的秘钥,time为请求头中的X-Gitee-Timestamp 也可以用当前收到请求的时间戳 但与请求调用时间误差不能超过1小时
func Sign(secret string, time string) string {
stringToSign := time + "\n" + secret
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(stringToSign))
signData := h.Sum(nil)
return base64.StdEncoding.EncodeToString(signData)
}

2. 搭建后端服务

使用 gin 搭建 webhook 的后端服务

1
2
3
4
5
func main() {
r := gin.Default()
router.RegisterRouter(r) // 就是你需要注册的路由
r.Run(":3000") // 监听并在 0.0.0.0:8080 上启动服务
}

router 中的代码以及目录结构

其实我就是简单的分了一下目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
webhook
├─Dockerfile // 打包
├─Makefile // 部署
├─boot.sh // sh脚本
├─main.go // 程序入口
├─utils
| ├─response // 通用响应体
├─internal
| ├─router
| | ├─gitee.go // gitee 的路由
| | └router.go // 总路由
| ├─handler // 处理函数
| | ├─blog.go
| | └webhook.go
├─gitee
| ├─errors.go // 错误常量码
| ├─event.go //一些事件的常量
| ├─gitee.go //gitee包
| ├─push_event_payload.go //推送事件的请求数据结构体
| ├─sign //签名包

3. 编写处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
type GiteeService struct {
hook *gitee.Webhook
}

// New 创建一个新的服务
func NewGitee() *GiteeService {
hook, _ := gitee.New(gitee.WithSecret(GITEESECRET))
return &GiteeService{
hook: hook,
}
}

func (g *GiteeService) Blog(c *gin.Context) {
payload, err := g.hook.Parse(c.Request, gitee.PushEvents)
if err != nil {
if err == gitee.ErrEventNotFound {
// ok event wasn;t one of the ones asked to be parsed
fmt.Printf("ErrEventNotFound %s\n", err)
response.Err(c, err)
return
}
// ok event wasn;t one of the ones asked to be parsed
fmt.Printf("unkonw error %s\n", err)
response.Err(c, err)
return
}
switch payload.(type) {
case gitee.PushEventPayload:
//release := payload.(gitee.PushEventPayload)
go func() {
cmd := exec.Command("sh", "/root/app/blog/webhook.sh")
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(output))
fmt.Printf("command error %s\n", err)
response.Err(c, err)
return
}
fmt.Println(string(output))
}()
response.Ok(c, nil)
}

}

上面就是一个简单的处理函数,但是值得说的一个点是 为什么需要使用携程去处理 由于打包部署的时间比较久但是 gitee 的 webhook 请求时长为 5s
所以我们这个请求得开一个携程去处理否则请求将请求超时

4. 部署 webhook

由于我需要执行其他需要自动部署的 app 的 shell 脚本,然而我有不想讲整个目录软连接进去所以我就直接二进制部署了就没有使用 docker, 可以看到 Dockerfile 都还在 呜呜呜

下面是我的部署 shell 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
path=/root/app/webhooks

cd $path
git pull

port=3000
# 找到占用 3000 端口的进程 ID
pid=$(lsof -t -i:$port)

if [ -n "$pid" ]; then
# 杀死进程
echo "Killing process $pid"
kill -9 $pid
fi

outlog="$path/out.log"
if [ -e "$outlog" ]; then
rm "$outlog"
fi

rm -rf $path/main
go build -o $path/main .
nohup $path/main >> $outlog &

先将之前的服务停止,然后再将日志清除,最后再后台运行。

5. gitee 配置 webhook

这个就简单了就将你的后台路由填进去然后测试一把就行

总结
以上就是搭建 webhook 的整个流程咯,实际上就是写个后端服务,然后当你出发 git 某个操作就会请求这个 url 然后执行你需要执行的代码。

作者

hml

发布于

2023-04-19

更新于

2024-12-24

许可协议

评论