From 58cf8b51f8e3ebd5d87e89c0cfe01cb66b9e1bef Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Wed, 17 Jul 2019 22:44:43 -0700 Subject: [PATCH] determine vcs automatically. #26 and #27 --- handler.go | 104 +++++++++++++++++++++++++++++++++--------------- handler_test.go | 25 +++++++++++- 2 files changed, 95 insertions(+), 34 deletions(-) diff --git a/handler.go b/handler.go index f925306..8ffadc1 100644 --- a/handler.go +++ b/handler.go @@ -31,6 +31,15 @@ type Handler struct { PathConfigs } +// vcsPrefixMap provides defaults for VCS type if it's not provided. +// The list of strings is used in strings.HasPrefix(). +var vcsPrefixMap = map[string][]string{ + "git": []string{"https://git", "https://bitbucket"}, + "bzr": []string{"https://bazaar"}, + "hg": []string{"https://hg.", "https://mercurial"}, + "svn": []string{"https://svn."}, +} + // Config contains the config file data. type Config struct { Title string `yaml:"title,omitempty"` @@ -81,47 +90,77 @@ func newHandler(configData []byte) (*Handler, error) { if h.CacheAge != nil { cacheControl = fmt.Sprintf("public, max-age=%d", *h.CacheAge) } - - for p, e := range h.Paths { + for p := range h.Paths { h.Paths[p].Path = p - - if len(e.RedirPaths) < 1 { + if len(h.Paths[p].RedirPaths) < 1 { // was not provided, pass in global value. - e.RedirPaths = h.RedirPaths + h.Paths[p].RedirPaths = h.RedirPaths } - h.Paths[p].cacheControl = cacheControl - if e.CacheAge != nil { - // provided, override global value. - h.Paths[p].cacheControl = fmt.Sprintf("public, max-age=%d", *e.CacheAge) + h.Paths[p].setRepoCacheControl(cacheControl) + h.Paths[p].setRepoDisplay() + if err := h.Paths[p].setRepoVCS(); err != nil { + return nil, err } + h.PathConfigs = append(h.PathConfigs, h.Paths[p]) + } + sort.Sort(h.PathConfigs) + return h, nil +} + +func (p *PathConfig) setRepoCacheControl(cc string) { + p.cacheControl = cc + if p.CacheAge != nil { + // provided, override global value. + p.cacheControl = fmt.Sprintf("public, max-age=%d", *p.CacheAge) + } +} - switch { - case e.Display != "": - // Already filled in. - case strings.HasPrefix(e.Repo, "https://github.com/"): - h.Paths[p].Display = fmt.Sprintf("%v %v/tree/master{/dir} %v/blob/master{/dir}/{file}#L{line}", e.Repo, e.Repo, e.Repo) - case strings.HasPrefix(e.Repo, "https://bitbucket.org"): - h.Paths[p].Display = fmt.Sprintf("%v %v/src/default{/dir} %v/src/default{/dir}/{file}#{file}-{line}", e.Repo, e.Repo, e.Repo) +// Set display path. +func (p *PathConfig) setRepoDisplay() { + if p.Display != "" { + return + } + // github, gitlab, git, svn, hg, bzr - may need more tweaking for some of these. + p.Display = fmt.Sprintf("%v %v/tree/master{/dir} %v/blob/master{/dir}/{file}#L{line}", p.Repo, p.Repo, p.Repo) + if strings.HasPrefix(p.Repo, "https://bitbucket.org") { + // bitbucket is weird. + p.Display = fmt.Sprintf("%v %v/src/default{/dir} %v/src/default{/dir}/{file}#{file}-{line}", p.Repo, p.Repo, p.Repo) + } +} + +// setRepoVCS makes sure the provides VCS type is supported, +// or sets it automatically based on the repo's prefix. +func (p *PathConfig) setRepoVCS() error { + // Check and set VCS type. + switch { + case p.Repo == "" && p.Redir != "": + // Redirect-only can go anywhere. + case p.VCS == "github" || p.VCS == "gitlab" || p.VCS == "bitbucket": + p.VCS = "git" + case p.VCS == "": + // Try to figure it out. + var err error + p.VCS, err = findRepoVCS(p.Repo) + return err + default: + // Already filled in, make sure it's supported. + if _, ok := vcsPrefixMap[p.VCS]; !ok { + return fmt.Errorf("configuration for %v: unknown VCS %s", p, p.VCS) } + } + return nil +} - switch { - case e.VCS != "": - // Already filled in. - if e.VCS != "bzr" && e.VCS != "git" && e.VCS != "hg" && e.VCS != "svn" { - return nil, fmt.Errorf("configuration for %v: unknown VCS %s", p, e.VCS) +// findRepoVCS checks the vcsMapList for a supported vcs type based on a repo's prefix. +func findRepoVCS(repo string) (string, error) { + for vcs, prefixList := range vcsPrefixMap { + for _, prefix := range prefixList { + if strings.HasPrefix(repo, prefix) { + return vcs, nil } - case strings.HasPrefix(e.Repo, "https://github.com/"): - h.Paths[p].VCS = "git" - case e.Repo == "" && e.Redir != "": - // Redirect-only can go anywhere. - default: - return nil, fmt.Errorf("configuration for %v: cannot infer VCS from %s", p, e.Repo) } - - h.PathConfigs = append(h.PathConfigs, e) } - sort.Sort(h.PathConfigs) - return h, nil + return "", fmt.Errorf("cannot infer VCS from %s", repo) } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -169,7 +208,8 @@ func (h *Handler) Hostname(r *http.Request) string { } // StringInSlices checks if a string exists in a list of strings. -// Used to determine if a sub path shouuld be redirected or not. +// Used to determine if a sub path should be redirected or not. +// Not used for normal vanity URLs, only used for `redir`. func StringInSlices(str string, slice []string) bool { for _, s := range slice { if strings.Contains(str, s) { diff --git a/handler_test.go b/handler_test.go index eb87177..f5ddc52 100644 --- a/handler_test.go +++ b/handler_test.go @@ -108,6 +108,27 @@ func TestHandler(t *testing.T) { goImport: "example.com git https://github.com/rakyll/portmidi", goSource: "example.com https://github.com/rakyll/portmidi _ _", }, + { + name: "display Gitlab inference", + config: "host: example.com\n" + + "paths:\n" + + " /portmidi:\n" + + " repo: https://gitlab.com/rakyll/portmidi\n", + path: "/portmidi", + goImport: "example.com/portmidi git https://gitlab.com/rakyll/portmidi", + goSource: "example.com/portmidi https://gitlab.com/rakyll/portmidi https://gitlab.com/rakyll/portmidi/tree/master{/dir} https://gitlab.com/rakyll/portmidi/blob/master{/dir}/{file}#L{line}", + }, + { + name: "display Gitlab inference", + config: "host: example.com\n" + + "paths:\n" + + " /portmidi:\n" + + " vcs: gitlab\n" + + " repo: https://gitlab.com/rakyll/portmidi\n", + path: "/portmidi", + goImport: "example.com/portmidi git https://gitlab.com/rakyll/portmidi", + goSource: "example.com/portmidi https://gitlab.com/rakyll/portmidi https://gitlab.com/rakyll/portmidi/tree/master{/dir} https://gitlab.com/rakyll/portmidi/blob/master{/dir}/{file}#L{line}", + }, } for _, test := range tests { h, err := newHandler([]byte(test.config)) @@ -145,10 +166,10 @@ func TestBadConfigs(t *testing.T) { badConfigs := []string{ "paths:\n" + " /missingvcs:\n" + - " repo: https://bitbucket.org/zombiezen/gopdf\n", + " repo: https://unknownbucket.org/zombiezen/gopdf\n", "paths:\n" + " /unknownvcs:\n" + - " repo: https://bitbucket.org/zombiezen/gopdf\n" + + " repo: https://unknownbucket.org/zombiezen/gopdf\n" + " vcs: xyzzy\n", "cache_max_age: -1\n" + "paths:\n" +