本文主要目的是使用go语言实现github webhook的接收处理,对下文提及的go语言、github、webhook、hexo博客框架不做过多介绍,如果想要学习GO语言使用,想要了解什么是github以及gwebhook, 想要使用hexo框架快速搭建博客系统的可自行百度搜索学习。
1、背景
此前使用hexo框架搭建了一套博客系统,博客系统是放在群晖的docker中的,一般写完文章会push到VPS的git仓库中,再由nginx展示静态页。 今天心血来潮,将代码push到github的仓库里,然后使用github pages来展示页面,但vps 就接收不到了,虽然hexo框架支持一键部署到GitHub Pages,但正好最近在学习go语言,并且想了解一下Github webhook,所以使用go语言实现Github webhook接收处理。
2、解决思路
通过GO语言开发一个webhook接收处理的web服务,并部署在VPS上,该web服务用于接收github的webhook请求。当你将本地代码 push到github之后,github通过webhook请求VPS上部署的服务地址,发送一个push状态,VPS上的web服务接收请求并验证通过后自动pull github上的代码至nginx的根目录,从而实现VPS和github代码的同步更新。
3、实现代码
废话不多说,直接看代码实现。
/**
* @author zander
* @version 1.0
*/
package main
import (
"fmt"
"net/http"
"log"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"io/ioutil"
"os/exec"
)
//处理请求
func wsHandler(w http.ResponseWriter, r *http.Request) {
var Secret string ="xxxx"
if (r.Header.Get("x-github-event") == "push" || r.Header.Get("x-github-event") == "ping") {
bodyContent, _ := ioutil.ReadAll(r.Body)
r.Body.Close()
signature := r.Header.Get("X-Hub-Signature")
if VerifySignature(signature, string(bodyContent), Secret) { //检验 github 发过来的 签名
//("验证通过,启动部署任务")
//校验通过,执行shell命令
//pull最新代码至nginx根目录下
cmd := exec.Command("/bin/sh","-c","cd /www && git reset --hard master && git pull")
_, err := cmd.Output()
if err == nil {
fmt.Fprintln(w, "{\"code\":200, \"description\":\"false\"}")
} else {
fmt.Fprintln(w, "{\"code\":200, \"description\":\"OK\"}")
}
} else {
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Signature error\"}")
}
} else {
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Unmatch x-github-event\"}")
}
}
//main入口函数
func main() {
// 当有请求访问ws时,执行此回调方法
http.HandleFunc("/",wsHandler)
// 监听127.0.0.1:7777
err := http.ListenAndServe("127.0.0.1:7777", nil)
if err != nil {
log.Fatal("ListenAndServe", err.Error())
}
}
//根据 密钥和 body 生成签名
func generateHashSignature(message string, secret string) string {
h := hmac.New(sha1.New, []byte(secret))
h.Write([]byte(message))
return "sha1=" + hex.EncodeToString(h.Sum(nil))
}
// 比较签名结果
func VerifySignature(signature string, data string, secret string) bool {
return signature == generateHashSignature(string(data), secret)
}
注1、 上述代码exec.Command("/bin/sh","-c","cd /www && git reset --hard master && git pull")中的/www目录是 nginx 的root目录,我本地的静态页是直接pull到www的,所以该路径下存在.git文件,但是.git文件不应支持对外访问,因此nginx中需要屏蔽对.git的访问。
注2、上述代码的编写参考了:GitHub - moonagic/GoWebhook: github webhook service written by go.。
最后,在github的webhook中的payload url指定VPS上服务对应地址。