|  | package vendor | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "go/parser" | 
|  | "go/token" | 
|  | "io" | 
|  | "io/ioutil" | 
|  | "log" | 
|  | "net/http" | 
|  | "os" | 
|  | "path" | 
|  | "path/filepath" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // ParseImports parses Go packages from a specific root returning a set of import paths. | 
|  | // vendorRoot is how deep to go looking for vendor folders, usually the repo root. | 
|  | // vendorPrefix is the vendorRoot import path. | 
|  | func ParseImports(root, vendorRoot, vendorPrefix string) (map[string]bool, error) { | 
|  | pkgs := make(map[string]bool) | 
|  |  | 
|  | var walkFn = func(p string, info os.FileInfo, err error) error { | 
|  | if info.IsDir() { | 
|  | name := info.Name() | 
|  | if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" { | 
|  | return filepath.SkipDir | 
|  | } | 
|  | return nil | 
|  | } | 
|  | if filepath.Ext(p) != ".go" { // Parse only go source files | 
|  | return nil | 
|  | } | 
|  |  | 
|  | fs := token.NewFileSet() | 
|  | f, err := parser.ParseFile(fs, p, nil, parser.ImportsOnly) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | for _, s := range f.Imports { | 
|  | pkg := strings.Replace(s.Path.Value, "\"", "", -1) | 
|  | if vp := findVendor(vendorRoot, filepath.Dir(p), pkg); vp != "" { | 
|  | pkg = path.Join(vendorPrefix, vp) | 
|  | } | 
|  | pkgs[pkg] = true | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | err := filepath.Walk(root, walkFn) | 
|  | return pkgs, err | 
|  | } | 
|  |  | 
|  | // findVendor looks for pkgName in a vendor folder at start/vendor or deeper, stopping | 
|  | // at root. start is expected to match or be a subfolder of root. | 
|  | // | 
|  | // It returns the path to pkgName inside the vendor folder, relative to root. | 
|  | func findVendor(root, start, pkgName string) string { | 
|  | if !strings.HasPrefix(start, root) { | 
|  | log.Fatalln("Assertion failed:", root, "prefix of", start) | 
|  | } | 
|  |  | 
|  | levels := strings.Split(strings.TrimPrefix(start, root), string(filepath.Separator)) | 
|  | for { | 
|  | candidate := filepath.Join(append(append([]string{root}, levels...), "vendor", pkgName)...) | 
|  |  | 
|  | files, err := ioutil.ReadDir(candidate) | 
|  | if err != nil { | 
|  | files = nil | 
|  | } | 
|  | isPackage := false | 
|  | for _, f := range files { | 
|  | if !f.IsDir() && filepath.Ext(f.Name()) == ".go" { | 
|  | isPackage = true | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | if isPackage { | 
|  | return strings.TrimPrefix(candidate, root) | 
|  | } | 
|  |  | 
|  | if len(levels) == 0 { | 
|  | return "" | 
|  | } | 
|  | levels = levels[:len(levels)-1] | 
|  | } | 
|  | } | 
|  |  | 
|  | // FetchMetadata fetchs the remote metadata for path. | 
|  | func FetchMetadata(path string, insecure bool) (rc io.ReadCloser, err error) { | 
|  | defer func() { | 
|  | if err != nil { | 
|  | err = fmt.Errorf("unable to determine remote metadata protocol: %s", err) | 
|  | } | 
|  | }() | 
|  | // try https first | 
|  | rc, err = fetchMetadata("https", path) | 
|  | if err == nil { | 
|  | return | 
|  | } | 
|  | // try http if supported | 
|  | if insecure { | 
|  | rc, err = fetchMetadata("http", path) | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | func fetchMetadata(scheme, path string) (io.ReadCloser, error) { | 
|  | url := fmt.Sprintf("%s://%s?go-get=1", scheme, path) | 
|  | switch scheme { | 
|  | case "https", "http": | 
|  | resp, err := http.Get(url) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("failed to access url %q", url) | 
|  | } | 
|  | return resp.Body, nil | 
|  | default: | 
|  | return nil, fmt.Errorf("unknown remote protocol scheme: %q", scheme) | 
|  | } | 
|  | } | 
|  |  | 
|  | // ParseMetadata fetchs and decodes remote metadata for path. | 
|  | func ParseMetadata(path string, insecure bool) (string, string, string, error) { | 
|  | rc, err := FetchMetadata(path, insecure) | 
|  | if err != nil { | 
|  | return "", "", "", err | 
|  | } | 
|  | defer rc.Close() | 
|  |  | 
|  | imports, err := parseMetaGoImports(rc) | 
|  | if err != nil { | 
|  | return "", "", "", err | 
|  | } | 
|  | match := -1 | 
|  | for i, im := range imports { | 
|  | if !strings.HasPrefix(path, im.Prefix) { | 
|  | continue | 
|  | } | 
|  | if match != -1 { | 
|  | return "", "", "", fmt.Errorf("multiple meta tags match import path %q", path) | 
|  | } | 
|  | match = i | 
|  | } | 
|  | if match == -1 { | 
|  | return "", "", "", fmt.Errorf("go-import metadata not found") | 
|  | } | 
|  | return imports[match].Prefix, imports[match].VCS, imports[match].RepoRoot, nil | 
|  | } |