handle github and github-like hooks
This commit is contained in:
parent
403001ff63
commit
f4d08c9ce0
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.15
|
|||
|
||||
require (
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/google/go-github/v32 v32.1.0
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
|
||||
|
|
9
go.sum
9
go.sum
|
@ -1,5 +1,10 @@
|
|||
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
|
||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||
|
@ -9,12 +14,15 @@ github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJV
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -28,3 +36,4 @@ golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4X
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
|
|
46
main.go
46
main.go
|
@ -46,7 +46,22 @@ var runFlags *flag.FlagSet
|
|||
var runOpts runOptions
|
||||
var initFlags *flag.FlagSet
|
||||
|
||||
var webhookProviders = make(map[string]func())
|
||||
var webhooks = make(map[string]func(chi.Router))
|
||||
var maxBodySize int64 = 1024 * 1024
|
||||
|
||||
var hooks chan webhook
|
||||
|
||||
type webhook struct {
|
||||
rev string
|
||||
ref string
|
||||
branch string
|
||||
repo string
|
||||
org string
|
||||
}
|
||||
|
||||
func init() {
|
||||
hooks = make(chan webhook)
|
||||
runOpts = runOptions{}
|
||||
runFlags = flag.NewFlagSet("run", flag.ExitOnError)
|
||||
runFlags.StringVar(&runOpts.listen, "listen", ":3000", "the address and port on which to listen")
|
||||
|
@ -84,6 +99,7 @@ func main() {
|
|||
initFlags.Parse(args[2:])
|
||||
case "run":
|
||||
runFlags.Parse(args[2:])
|
||||
registerWebhooks()
|
||||
serve()
|
||||
default:
|
||||
usage()
|
||||
|
@ -104,6 +120,7 @@ func serve() {
|
|||
}
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
var staticHandler http.HandlerFunc
|
||||
pub := http.FileServer(assets.Assets)
|
||||
|
@ -125,6 +142,8 @@ func serve() {
|
|||
}
|
||||
}
|
||||
|
||||
loadWebhooks(r)
|
||||
|
||||
r.Get("/*", staticHandler)
|
||||
|
||||
fmt.Println("Listening for http (with reasonable timeouts) on", runOpts.listen)
|
||||
|
@ -136,9 +155,36 @@ func serve() {
|
|||
WriteTimeout: 20 * time.Second,
|
||||
MaxHeaderBytes: 1024 * 1024, // 1MiB
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
hook := <-hooks
|
||||
// TODO os.Exec
|
||||
fmt.Println(hook.org)
|
||||
fmt.Println(hook.repo)
|
||||
fmt.Println(hook.branch)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := srv.ListenAndServe(); nil != err {
|
||||
fmt.Fprintf(os.Stderr, "%s", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func registerWebhooks() {
|
||||
for _, add := range webhookProviders {
|
||||
add()
|
||||
}
|
||||
}
|
||||
|
||||
func loadWebhooks(r chi.Router) {
|
||||
r.Route("/api/webhooks", func(r chi.Router) {
|
||||
for provider, handler := range webhooks {
|
||||
r.Route("/"+provider, func(r chi.Router) {
|
||||
handler(r)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
## explicit
|
||||
github.com/go-chi/chi
|
||||
github.com/go-chi/chi/middleware
|
||||
# github.com/google/go-github/v32 v32.1.0
|
||||
## explicit
|
||||
github.com/google/go-github/v32/github
|
||||
# github.com/google/go-querystring v1.0.0
|
||||
github.com/google/go-querystring/query
|
||||
# github.com/joho/godotenv v1.3.0
|
||||
## explicit
|
||||
github.com/joho/godotenv
|
||||
|
@ -13,5 +18,13 @@ github.com/shurcooL/httpfs/vfsutil
|
|||
## explicit
|
||||
github.com/shurcooL/vfsgen
|
||||
github.com/shurcooL/vfsgen/cmd/vfsgendev
|
||||
# golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/crypto/cast5
|
||||
golang.org/x/crypto/openpgp
|
||||
golang.org/x/crypto/openpgp/armor
|
||||
golang.org/x/crypto/openpgp/elgamal
|
||||
golang.org/x/crypto/openpgp/errors
|
||||
golang.org/x/crypto/openpgp/packet
|
||||
golang.org/x/crypto/openpgp/s2k
|
||||
# golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346
|
||||
## explicit
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
// // +build github
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/google/go-github/v32/github"
|
||||
)
|
||||
|
||||
func init() {
|
||||
githubSecret := ""
|
||||
runFlags.StringVar(&githubSecret, "github-secret", "", "secret for github webhooks (same as GITHUB_SECRET=)")
|
||||
webhookProviders["github"] = registerGithubish("github", &githubSecret, "GITHUB_SECRET")
|
||||
}
|
||||
|
||||
func registerGithubish(providername string, secret *string, envname string) func() {
|
||||
return func() {
|
||||
if "" == *secret {
|
||||
*secret = os.Getenv(envname)
|
||||
}
|
||||
if "" == *secret {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %s\n", envname)
|
||||
return
|
||||
}
|
||||
githubSecretB := []byte(*secret)
|
||||
webhooks[providername] = func(router chi.Router) {
|
||||
router.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
body := http.MaxBytesReader(w, r.Body, maxBodySize)
|
||||
defer func() {
|
||||
_ = body.Close()
|
||||
}()
|
||||
|
||||
payload, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
// if there's a read error, it should have been handled already by the MaxBytesReader
|
||||
return
|
||||
}
|
||||
|
||||
sig := r.Header.Get("X-Hub-Signature")
|
||||
if err := github.ValidateSignature(sig, payload, githubSecretB); nil != err {
|
||||
log.Printf("invalid github signature: error: %s\n", err)
|
||||
http.Error(w, "invalid github signature", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
hookType := github.WebHookType(r)
|
||||
event, err := github.ParseWebHook(hookType, payload)
|
||||
if err != nil {
|
||||
log.Printf("invalid github webhook payload: error: %s\n", err)
|
||||
http.Error(w, "invalid github webhook payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
switch e := event.(type) {
|
||||
case *github.PushEvent:
|
||||
// this is a commit push, do something with it
|
||||
|
||||
ref := e.GetRef() // *e.Ref
|
||||
branch := ref[len("refs/heads/"):]
|
||||
hooks <- webhook{
|
||||
rev: e.GetAfter(), // *e.After
|
||||
ref: ref,
|
||||
branch: branch,
|
||||
repo: e.GetRepo().GetName(), // *e.Repo.Name
|
||||
org: e.GetRepo().GetOrganization(), // *e.Repo.Organization
|
||||
}
|
||||
/*
|
||||
case *github.PullRequestEvent:
|
||||
// probably doesn't matter
|
||||
case *github.StatusEvent:
|
||||
// probably doesn't matter
|
||||
case *github.WatchEvent:
|
||||
// probably doesn't matter
|
||||
*/
|
||||
default:
|
||||
log.Printf("unknown event type %s\n", hookType)
|
||||
return
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue