62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
package vcs
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
errPrefixDoesNotMatch = errors.New("prefix does not match the module")
|
|
errMetaNotFound = errors.New("go-import meta tag not found")
|
|
)
|
|
|
|
func RepoRoot(ctx context.Context, module string) (root string, path string, err error) {
|
|
// For common VCS hosters we can figure out repo root by the URL
|
|
if strings.HasPrefix(module, "github.com/") || strings.HasPrefix(module, "bitbucket.org/") {
|
|
parts := strings.Split(module, "/")
|
|
if len(parts) < 3 {
|
|
return "", "", errors.New("bad module name")
|
|
}
|
|
return strings.Join(parts[0:3], "/"), strings.Join(parts[3:], "/"), nil
|
|
}
|
|
// Otherwise we shall make a `?go-get=1` HTTP request
|
|
// TODO: use context
|
|
res, err := http.Get("https://" + module + "?go-get=1")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
defer res.Body.Close()
|
|
html := struct {
|
|
HTML string `xml:"html"`
|
|
Head struct {
|
|
Meta []struct {
|
|
Name string `xml:"name,attr"`
|
|
Content string `xml:"content,attr"`
|
|
} `xml:"meta"`
|
|
} `xml:"head"`
|
|
}{}
|
|
dec := xml.NewDecoder(res.Body)
|
|
dec.Strict = false
|
|
dec.AutoClose = xml.HTMLAutoClose
|
|
dec.Entity = xml.HTMLEntity
|
|
if err := dec.Decode(&html); err != nil {
|
|
return "", "", err
|
|
}
|
|
for _, meta := range html.Head.Meta {
|
|
if meta.Name == "go-import" {
|
|
if f := strings.Fields(meta.Content); len(f) == 3 {
|
|
url := f[2]
|
|
if i := strings.Index(url, "://"); i >= 0 {
|
|
url = url[i+3:]
|
|
}
|
|
path = strings.TrimPrefix(strings.TrimPrefix(module, f[0]), "/")
|
|
return url, path, nil
|
|
}
|
|
}
|
|
}
|
|
return "", "", errMetaNotFound
|
|
}
|