Skip to content

Commit d400f04

Browse files
author
bajins
committed
initial commit
1 parent d8a17e1 commit d400f04

File tree

10 files changed

+399
-0
lines changed

10 files changed

+399
-0
lines changed

.gitignore

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Created by .ignore support plugin (hsz.mobi)
2+
### TortoiseGit template
3+
# Project-level settings
4+
/.tgitconfig
5+
6+
### Go template
7+
# Binaries for programs and plugins
8+
*.exe
9+
*.exe~
10+
*.dll
11+
*.so
12+
*.dylib
13+
14+
# Test binary, built with `go test -c`
15+
*.test
16+
17+
# Output of the go coverage tool, specifically when used with LiteIDE
18+
*.out
19+
20+
# Dependency directories (remove the comment below to include it)
21+
# vendor/
22+
23+
### JetBrains template
24+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
25+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
26+
27+
# User-specific stuff
28+
.idea
29+
.idea/**/tasks.xml
30+
.idea/**/usage.statistics.xml
31+
.idea/**/dictionaries
32+
.idea/**/shelf
33+
34+
# Generated files
35+
.idea/**/contentModel.xml
36+
37+
# Sensitive or high-churn files
38+
.idea/**/dataSources/
39+
.idea/**/dataSources.ids
40+
.idea/**/dataSources.local.xml
41+
.idea/**/sqlDataSources.xml
42+
.idea/**/dynamic.xml
43+
.idea/**/uiDesigner.xml
44+
.idea/**/dbnavigator.xml
45+
46+
# Gradle
47+
.idea/**/gradle.xml
48+
.idea/**/libraries
49+
50+
# Gradle and Maven with auto-import
51+
# When using Gradle or Maven with auto-import, you should exclude module files,
52+
# since they will be recreated, and may cause churn. Uncomment if using
53+
# auto-import.
54+
# .idea/modules.xml
55+
# .idea/*.iml
56+
# .idea/modules
57+
# *.iml
58+
# *.ipr
59+
60+
# CMake
61+
cmake-build-*/
62+
63+
# Mongo Explorer plugin
64+
.idea/**/mongoSettings.xml
65+
66+
# File-based project format
67+
*.iws
68+
69+
# IntelliJ
70+
out/
71+
72+
# mpeltonen/sbt-idea plugin
73+
.idea_modules/
74+
75+
# JIRA plugin
76+
atlassian-ide-plugin.xml
77+
78+
# Cursive Clojure plugin
79+
.idea/replstate.xml
80+
81+
# Crashlytics plugin (for Android Studio and IntelliJ)
82+
com_crashlytics_export_strings.xml
83+
crashlytics.properties
84+
crashlytics-build.properties
85+
fabric.properties
86+
87+
# Editor-based Rest Client
88+
.idea/httpRequests
89+
90+
# Android studio 3.1+ serialized cache file
91+
.idea/caches/build_file_checksums.ser
92+

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# webhook-go
2+
23
支持GitHub、Gitea、Gogs
4+
5+
6+
> http://127.0.0.1:8000/webHooks?id=test

config.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
)
7+
8+
var (
9+
config map[string]Config
10+
)
11+
12+
type Config struct {
13+
Logfile string
14+
Secret string
15+
Commands []string
16+
}
17+
18+
// LoadConfig load the config
19+
func LoadConfig() error {
20+
result, err := ioutil.ReadFile("data/config.json")
21+
if err != nil {
22+
return err
23+
}
24+
json.Unmarshal(result, &config)
25+
return nil
26+
}
27+
28+
// 获取配置中的log文件名称
29+
func GetLogName(id string) string {
30+
logName := config[id].Logfile
31+
if logName == "" || len(logName) <= 0 {
32+
return id
33+
}
34+
return logName
35+
}

data/config.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"这里是id的值": {
3+
"logfile": "test-gitea-webhook.log",
4+
"secret": "在Webhooks中设定的secret",
5+
"commands": [
6+
"data/update_repo.sh"
7+
]
8+
}
9+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module webhook-go
2+
3+
go 1.13

server.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
"os"
8+
"strings"
9+
"webhook-go/utils"
10+
)
11+
12+
// 启动服务
13+
func StartService(address string, port string) error {
14+
http.HandleFunc("/", index)
15+
http.HandleFunc("/webHooks", webHooks)
16+
17+
utils.Log2file(fmt.Sprintf("service starting... %s:%s", address, port), "")
18+
return http.ListenAndServe(fmt.Sprintf("%s:%s", address, port), nil)
19+
}
20+
21+
// 首页
22+
func index(w http.ResponseWriter, r *http.Request) {
23+
utils.Log2file(string(r.URL.Host), "")
24+
fmt.Fprintln(w, "{\"code\":200, \"description\":\"service running...\"}")
25+
}
26+
27+
// 自动编译
28+
func webHooks(w http.ResponseWriter, r *http.Request) {
29+
if err := LoadConfig(); err != nil {
30+
utils.Log2file(err.Error(), "")
31+
os.Exit(1)
32+
}
33+
if strings.ToUpper(r.Method) != "POST" {
34+
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Error Method or unknow request url\"}")
35+
return
36+
}
37+
if !VerifyEvent(r.Header, "push") {
38+
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Unmatch x-github-event\"}")
39+
return
40+
}
41+
bodyContent, err := ioutil.ReadAll(r.Body)
42+
if err != nil {
43+
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Error Response Body\"}")
44+
return
45+
}
46+
defer r.Body.Close()
47+
48+
// 解析参数,填充到Form、PostForm
49+
r.ParseForm()
50+
id := r.Form["id"][0]
51+
if id == "" || len(id) <= 0 {
52+
fmt.Fprintln(w, "{\"code\":200, \"error\":\"id is empty\"}")
53+
return
54+
}
55+
56+
if !VerifySignature(r.Header, string(bodyContent), config[id].Secret) {
57+
utils.Log2file("验证失败", config[id].Logfile)
58+
fmt.Fprintln(w, "{\"code\":200, \"error\":\"Signature error\"}")
59+
}
60+
fmt.Fprintln(w, "{\"code\":200, \"description\":\"OK\"}")
61+
utils.Log2file("验证通过,启动部署任务", config[id].Logfile)
62+
AddNewTask(id)
63+
}
64+
65+
// 验证Signature
66+
func VerifySignature(header http.Header, data string, secret string) bool {
67+
signature := header.Get("X-Hub-Signature")
68+
if signature != "" && len(signature) > 0 {
69+
signature = strings.Split(signature, "=")[0]
70+
return signature == utils.ComputeHash1(data, secret)
71+
}
72+
signature = header.Get("X-Gitea-Signature")
73+
74+
if signature == "" || len(signature) <= 0 {
75+
signature = header.Get("X-Gogs-Signature")
76+
}
77+
78+
return signature == utils.ComputeHmacSha256(data, secret)
79+
}
80+
81+
// 验证Event
82+
func VerifyEvent(header http.Header, event string) bool {
83+
e := header.Get("X-Gitea-Event")
84+
85+
if e == "" || len(e) <= 0 {
86+
e = header.Get("X-Gogs-Event")
87+
}
88+
89+
if e == "" || len(e) <= 0 {
90+
e = header.Get("X-GitHub-Event")
91+
}
92+
93+
return event == strings.Trim(e, "UTF-8")
94+
}

task.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os/exec"
6+
"webhook-go/utils"
7+
)
8+
9+
var running = false
10+
var queue []*TaskQueue
11+
12+
type TaskQueue struct {
13+
Id string
14+
//Payload string
15+
}
16+
17+
// AddNewTask add new task
18+
func AddNewTask(id string) {
19+
queue = append(queue, &TaskQueue{id})
20+
checkoutTaskStatus()
21+
}
22+
23+
func checkoutTaskStatus() {
24+
if running {
25+
return
26+
}
27+
if len(queue) > 0 {
28+
go startTask(queue[0])
29+
}
30+
}
31+
32+
func startTask(task *TaskQueue) {
33+
commands := config[task.Id].Commands
34+
running = true
35+
for _, v := range commands {
36+
cmd := exec.Command("/bin/sh", v)
37+
_, err := cmd.Output()
38+
if err == nil {
39+
utils.Log2file(fmt.Sprintf("部署成功:%s", v), GetLogName(task.Id))
40+
} else {
41+
utils.Log2file(fmt.Sprintf("部署失败:%s %s", v, err), GetLogName(task.Id))
42+
}
43+
}
44+
queue = queue[:0]
45+
running = false
46+
checkoutTaskStatus()
47+
}

utils/log2file.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package utils
2+
3+
import (
4+
"os"
5+
"path"
6+
"time"
7+
)
8+
9+
var (
10+
logsDir = "logs"
11+
)
12+
13+
// Log2file write log to file
14+
func Log2file(content, logName string) {
15+
var err error
16+
17+
if _, err := os.Stat(logsDir); err != nil {
18+
err = os.MkdirAll(logsDir, 0711)
19+
if err != nil {
20+
return
21+
}
22+
}
23+
24+
if logName == "" || len(logName) <= 0 {
25+
logName = "webhook-go.log"
26+
}
27+
28+
logPath := path.Join(logsDir, logName)
29+
30+
if _, err := os.Stat(logPath); err != nil {
31+
_, err = os.Create(logPath)
32+
if err != nil {
33+
return
34+
}
35+
}
36+
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_WRONLY, 0600)
37+
if err != nil {
38+
return
39+
}
40+
timeString := time.Now().Format("2006-01-02 15:04:05")
41+
f.WriteString("[" + timeString + "]" + "" + content)
42+
f.WriteString("\n")
43+
}

utils/signature.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package utils
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha1"
6+
"crypto/sha256"
7+
"encoding/base64"
8+
"encoding/hex"
9+
)
10+
11+
// 计算hash1
12+
func ComputeHash1(message string, secret string) string {
13+
h := hmac.New(sha1.New, []byte(secret))
14+
h.Write([]byte(message))
15+
// 转成十六进制
16+
return hex.EncodeToString(h.Sum(nil))
17+
}
18+
19+
// 计算HmacSha256
20+
func ComputeHmacSha256(message string, secret string) string {
21+
key := []byte(secret)
22+
h := hmac.New(sha256.New, key)
23+
h.Write([]byte(message))
24+
// 转成十六进制
25+
return hex.EncodeToString(h.Sum(nil))
26+
27+
}
28+
29+
// 编码Base64
30+
func EncodeBase64(str string) string {
31+
return base64.StdEncoding.EncodeToString([]byte(str))
32+
}

0 commit comments

Comments
 (0)