mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-02 13:59:50 +08:00
b4780a41d3
The webhook providers reside behind a small interface which determines if a provider should run. If a provider should run it delegates responsibility of the request to the provider. ghdeploy initial commit Added webhook functionality to the git middleware. The webhook providers reside behind a small interface which determines if a provider should run. If a provider should run it delegates responsibility of the request to the provider. Add tests Remove old implementation Fix inconsistency with git interval pulling. Remove '\n' from logging statements and put the initial pull into a startup function
208 lines
4.7 KiB
Go
208 lines
4.7 KiB
Go
package setup
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mholt/caddy/middleware"
|
|
"github.com/mholt/caddy/middleware/git"
|
|
"github.com/mholt/caddy/middleware/git/webhook"
|
|
)
|
|
|
|
// Git configures a new Git service routine.
|
|
func Git(c *Controller) (middleware.Middleware, error) {
|
|
repo, err := gitParse(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If a HookUrl is set, we switch to event based pulling.
|
|
// Install the url handler
|
|
if repo.HookUrl != "" {
|
|
|
|
c.Startup = append(c.Startup, func() error {
|
|
return repo.Pull()
|
|
})
|
|
|
|
webhook := &webhook.WebHook{Repo: repo}
|
|
return func(next middleware.Handler) middleware.Handler {
|
|
webhook.Next = next
|
|
return webhook
|
|
}, nil
|
|
|
|
} else {
|
|
c.Startup = append(c.Startup, func() error {
|
|
|
|
// Start service routine in background
|
|
git.Start(repo)
|
|
|
|
// Do a pull right away to return error
|
|
return repo.Pull()
|
|
})
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
func gitParse(c *Controller) (*git.Repo, error) {
|
|
repo := &git.Repo{Branch: "master", Interval: git.DefaultInterval, Path: c.Root}
|
|
|
|
for c.Next() {
|
|
args := c.RemainingArgs()
|
|
|
|
switch len(args) {
|
|
case 2:
|
|
repo.Path = filepath.Clean(c.Root + string(filepath.Separator) + args[1])
|
|
fallthrough
|
|
case 1:
|
|
repo.URL = args[0]
|
|
}
|
|
|
|
for c.NextBlock() {
|
|
switch c.Val() {
|
|
case "repo":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.URL = c.Val()
|
|
case "path":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.Path = filepath.Clean(c.Root + string(filepath.Separator) + c.Val())
|
|
case "branch":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.Branch = c.Val()
|
|
case "key":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.KeyPath = c.Val()
|
|
case "interval":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
t, _ := strconv.Atoi(c.Val())
|
|
if t > 0 {
|
|
repo.Interval = time.Duration(t) * time.Second
|
|
}
|
|
case "hook":
|
|
if !c.NextArg() {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.HookUrl = c.Val()
|
|
|
|
// optional secret for validation
|
|
if c.NextArg() {
|
|
repo.HookSecret = c.Val()
|
|
}
|
|
|
|
case "then":
|
|
thenArgs := c.RemainingArgs()
|
|
if len(thenArgs) == 0 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
repo.Then = strings.Join(thenArgs, " ")
|
|
default:
|
|
return nil, c.ArgErr()
|
|
}
|
|
}
|
|
}
|
|
|
|
// if repo is not specified, return error
|
|
if repo.URL == "" {
|
|
return nil, c.ArgErr()
|
|
}
|
|
|
|
// if private key is not specified, convert repository URL to https
|
|
// to avoid ssh authentication
|
|
// else validate git URL
|
|
// Note: private key support not yet available on Windows
|
|
var err error
|
|
if repo.KeyPath == "" {
|
|
repo.URL, repo.Host, err = sanitizeHTTP(repo.URL)
|
|
} else {
|
|
repo.URL, repo.Host, err = sanitizeGit(repo.URL)
|
|
// TODO add Windows support for private repos
|
|
if runtime.GOOS == "windows" {
|
|
return nil, fmt.Errorf("Private repository not yet supported on Windows")
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// validate git requirements
|
|
if err = git.Init(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return repo, repo.Prepare()
|
|
}
|
|
|
|
// sanitizeHTTP cleans up repository URL and converts to https format
|
|
// if currently in ssh format.
|
|
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
|
// and possible error
|
|
func sanitizeHTTP(repoURL string) (string, string, error) {
|
|
url, err := url.Parse(repoURL)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
if url.Host == "" && strings.HasPrefix(url.Path, "git@") {
|
|
url.Path = url.Path[len("git@"):]
|
|
i := strings.Index(url.Path, ":")
|
|
if i < 0 {
|
|
return "", "", fmt.Errorf("Invalid git url %s", repoURL)
|
|
}
|
|
url.Host = url.Path[:i]
|
|
url.Path = "/" + url.Path[i+1:]
|
|
}
|
|
|
|
repoURL = "https://" + url.Host + url.Path
|
|
|
|
// add .git suffix if missing
|
|
if !strings.HasSuffix(repoURL, ".git") {
|
|
repoURL += ".git"
|
|
}
|
|
|
|
return repoURL, url.Host, nil
|
|
}
|
|
|
|
// sanitizeGit cleans up repository url and converts to ssh format for private
|
|
// repositories if required.
|
|
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
|
// and possible error
|
|
func sanitizeGit(repoURL string) (string, string, error) {
|
|
repoURL = strings.TrimSpace(repoURL)
|
|
|
|
// check if valid ssh format
|
|
if !strings.HasPrefix(repoURL, "git@") || strings.Index(repoURL, ":") < len("git@a:") {
|
|
// check if valid http format and convert to ssh
|
|
if url, err := url.Parse(repoURL); err == nil && strings.HasPrefix(url.Scheme, "http") {
|
|
repoURL = fmt.Sprintf("git@%v:%v", url.Host, url.Path[1:])
|
|
} else {
|
|
return "", "", fmt.Errorf("Invalid git url %s", repoURL)
|
|
}
|
|
}
|
|
hostURL := repoURL[len("git@"):]
|
|
i := strings.Index(hostURL, ":")
|
|
host := hostURL[:i]
|
|
|
|
// add .git suffix if missing
|
|
if !strings.HasSuffix(repoURL, ".git") {
|
|
repoURL += ".git"
|
|
}
|
|
|
|
return repoURL, host, nil
|
|
}
|