2021-03-17 17:26:55 +00:00
|
|
|
// Package webhooks provides the data structures and utilities related to the hooks
|
2020-09-28 23:39:05 +00:00
|
|
|
package webhooks
|
|
|
|
|
|
|
|
import (
|
2021-02-21 11:25:42 +00:00
|
|
|
"encoding/base64"
|
2020-10-19 20:11:14 +00:00
|
|
|
"os"
|
|
|
|
"strings"
|
2021-02-21 11:25:42 +00:00
|
|
|
"time"
|
2020-10-19 20:11:14 +00:00
|
|
|
|
2020-09-28 23:39:05 +00:00
|
|
|
"github.com/go-chi/chi"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Ref represents typical git webhook info such as:
|
|
|
|
// HTTPSURL ex: https://git@git.example.com/example/example.git
|
|
|
|
// SSHURL ex: ssh://git@git.example.com/example/example.git
|
|
|
|
// Rev ex: 00000000
|
|
|
|
// Ref ex: /refs/heads/master
|
|
|
|
// Branch ex: master
|
|
|
|
// Repo ex: example
|
|
|
|
// Org ex: example
|
|
|
|
type Ref struct {
|
2021-02-21 11:25:42 +00:00
|
|
|
RepoID string `json:"repo_id"`
|
|
|
|
Timestamp time.Time `json:"timestamp"`
|
|
|
|
HTTPSURL string `json:"https_url"`
|
|
|
|
SSHURL string `json:"ssh_url"`
|
|
|
|
Rev string `json:"rev"`
|
|
|
|
Ref string `json:"ref"` // refs/tags/v0.0.1, refs/heads/master
|
|
|
|
RefType string `json:"ref_type"` // tag, branch
|
|
|
|
RefName string `json:"ref_name"`
|
|
|
|
Owner string `json:"repo_owner"`
|
|
|
|
Repo string `json:"repo_name"`
|
|
|
|
//Branch string `json:"branch"` // deprecated
|
|
|
|
//Tag string `json:"tag"` // deprecated
|
|
|
|
}
|
|
|
|
|
|
|
|
// RefID is a newtype string
|
|
|
|
type RefID string
|
|
|
|
|
|
|
|
// URLSafeRefID is a newtype string
|
|
|
|
type URLSafeRefID string
|
|
|
|
|
|
|
|
// RevID is a newtype string
|
|
|
|
type RevID string
|
|
|
|
|
|
|
|
// URLSafeRevID is a newtype string
|
|
|
|
type URLSafeRevID string
|
|
|
|
|
|
|
|
// URLSafeGitID is a newtype string
|
|
|
|
type URLSafeGitID string
|
|
|
|
|
|
|
|
// New returns a normalized Ref (Git reference)
|
|
|
|
func New(r Ref) *Ref {
|
|
|
|
if len(r.HTTPSURL) > 0 {
|
|
|
|
r.RepoID = getRepoID(r.HTTPSURL)
|
|
|
|
} else /*if len(r.SSHURL) > 0*/ {
|
|
|
|
r.RepoID = getRepoID(r.SSHURL)
|
|
|
|
}
|
|
|
|
r.Timestamp = getTimestamp(r.Timestamp)
|
|
|
|
|
|
|
|
return &r
|
|
|
|
}
|
|
|
|
|
|
|
|
// String prints object as git.example.com#branch@rev
|
|
|
|
func (h *Ref) String() string {
|
2021-03-17 17:26:55 +00:00
|
|
|
rev := h.Rev
|
|
|
|
if len(rev) > 7 {
|
|
|
|
rev = rev[:7]
|
|
|
|
}
|
|
|
|
return string(h.GetRefID()) + "@" + rev
|
2021-02-21 11:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetRefID returns a unique reference like "github.com/org/project#branch"
|
|
|
|
func (h *Ref) GetRefID() RefID {
|
|
|
|
return RefID(h.RepoID + "#" + h.RefName)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetURLSafeRefID returns the URL-safe Base64 encoding of the RefID
|
|
|
|
func (h *Ref) GetURLSafeRefID() URLSafeRefID {
|
|
|
|
return URLSafeRefID(
|
|
|
|
base64.RawURLEncoding.EncodeToString(
|
|
|
|
[]byte(h.GetRefID()),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRevID returns a unique reference like "github.com/org/project#abcd7890"
|
|
|
|
func (h *Ref) GetRevID() RevID {
|
2021-03-17 17:26:55 +00:00
|
|
|
rev := h.Rev
|
|
|
|
if len(rev) > 7 {
|
|
|
|
rev = rev[:7]
|
|
|
|
}
|
|
|
|
return RevID(h.RepoID + "#" + rev)
|
2021-02-21 11:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetURLSafeRevID returns the URL-safe Base64 encoding of the RevID
|
|
|
|
func (h *Ref) GetURLSafeRevID() URLSafeRevID {
|
|
|
|
return URLSafeRevID(
|
|
|
|
base64.RawURLEncoding.EncodeToString(
|
|
|
|
[]byte(h.GetRevID()),
|
|
|
|
),
|
|
|
|
)
|
2020-09-28 23:39:05 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// Providers is a map of the git webhook providers
|
2020-09-28 23:39:05 +00:00
|
|
|
var Providers = make(map[string]func())
|
2020-11-21 12:41:01 +00:00
|
|
|
|
|
|
|
// Webhooks is a map of routes
|
2020-09-28 23:39:05 +00:00
|
|
|
var Webhooks = make(map[string]func(chi.Router))
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// Hooks is a channel of incoming webhooks
|
2020-09-29 10:40:09 +00:00
|
|
|
var Hooks = make(chan Ref)
|
2020-09-28 23:39:05 +00:00
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// Hook will put a Git Ref on the queue
|
2020-09-28 23:39:05 +00:00
|
|
|
func Hook(r Ref) {
|
2020-09-29 10:40:09 +00:00
|
|
|
Hooks <- r
|
2020-09-28 23:39:05 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// Accept will pop a Git Ref off the queue
|
2020-09-28 23:39:05 +00:00
|
|
|
func Accept() Ref {
|
2020-09-29 10:40:09 +00:00
|
|
|
return <-Hooks
|
2020-09-28 23:39:05 +00:00
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// AddProvider registers a git webhook provider
|
2020-09-28 23:39:05 +00:00
|
|
|
func AddProvider(name string, initProvider func()) {
|
|
|
|
Providers[name] = initProvider
|
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// AddRouteHandler registers a git webhook route
|
2020-09-28 23:39:05 +00:00
|
|
|
func AddRouteHandler(name string, route func(router chi.Router)) {
|
|
|
|
Webhooks[name] = route
|
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// MustRegisterAll registers all webhook route functions
|
2020-09-28 23:39:05 +00:00
|
|
|
func MustRegisterAll() {
|
|
|
|
for _, addHandler := range Providers {
|
|
|
|
addHandler()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// RouteHandlers registers the webhook functions to the route
|
2020-09-28 23:39:05 +00:00
|
|
|
func RouteHandlers(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)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-10-19 20:11:14 +00:00
|
|
|
|
2020-11-21 12:41:01 +00:00
|
|
|
// ParseSecrets grabs secrets from the ENV at runtime
|
2020-10-19 20:11:14 +00:00
|
|
|
func ParseSecrets(providername, secretList, envname string) [][]byte {
|
|
|
|
if 0 == len(secretList) {
|
|
|
|
secretList = os.Getenv(envname)
|
|
|
|
}
|
|
|
|
if 0 == len(secretList) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var secrets [][]byte
|
|
|
|
for _, secret := range strings.Fields(strings.ReplaceAll(secretList, ",", " ")) {
|
|
|
|
if len(secret) > 0 {
|
|
|
|
secrets = append(secrets, []byte(secret))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return secrets
|
|
|
|
}
|
2021-02-21 11:25:42 +00:00
|
|
|
|
|
|
|
// https://git.example.com/example/project.git
|
|
|
|
// => git.example.com/example/project
|
|
|
|
func getRepoID(url string) string {
|
|
|
|
repoID := strings.TrimPrefix(url, "https://")
|
|
|
|
repoID = strings.TrimPrefix(repoID, "http://")
|
|
|
|
repoID = strings.TrimPrefix(repoID, "ssh://")
|
2021-03-17 17:39:47 +00:00
|
|
|
|
|
|
|
// "gitea@example.com:my-org/my-project" // removes gitea@
|
|
|
|
// "git/ea@example.com:my-org/my-project" // no change
|
|
|
|
// "gitea@" // empty string
|
|
|
|
firstSlash := strings.Index(repoID, "/")
|
|
|
|
firstAt := strings.Index(repoID, "@")
|
|
|
|
if firstSlash < 0 {
|
|
|
|
firstSlash = 999999
|
|
|
|
}
|
|
|
|
if firstAt >= 0 && firstAt < firstSlash && len(repoID) >= firstAt {
|
|
|
|
repoID = repoID[firstAt+1:]
|
|
|
|
repoID = strings.Replace(repoID, ":", "/", 1)
|
|
|
|
}
|
|
|
|
|
2021-02-21 11:25:42 +00:00
|
|
|
repoID = strings.TrimSuffix(repoID, ".git")
|
|
|
|
return repoID
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTimestamp(t time.Time) time.Time {
|
|
|
|
if t.IsZero() {
|
|
|
|
t = time.Now().UTC()
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|