handle github and github-like hooks

This commit is contained in:
AJ ONeal 2020-09-28 03:43:30 -06:00
parent 403001ff63
commit f4d08c9ce0
5 changed files with 158 additions and 0 deletions

1
go.mod
View File

@ -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
View File

@ -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
View File

@ -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)
})
}
})
}

13
vendor/modules.txt vendored
View File

@ -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

89
webhooks_github.go Normal file
View File

@ -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
}
})
}
}
}