caddy/config/setup/git.go
2015-06-07 20:49:17 -04:00

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
}