github.com/motemen/ghq@v1.0.3/go_import.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "golang.org/x/net/html" 11 ) 12 13 // metaImport represents the parsed <meta name="go-import" 14 // content="prefix vcs reporoot" /> tags from HTML files. 15 type metaImport struct { 16 Prefix, VCS, RepoRoot string 17 } 18 19 func detectGoImport(u *url.URL) (string, *url.URL, error) { 20 goGetU := &url.URL{ // clone 21 Scheme: u.Scheme, 22 User: u.User, 23 Host: u.Host, 24 Path: u.Path, 25 RawQuery: u.RawQuery, 26 } 27 q := goGetU.Query() 28 q.Add("go-get", "1") 29 goGetU.RawQuery = q.Encode() 30 31 cli := &http.Client{ 32 CheckRedirect: func(req *http.Request, via []*http.Request) error { 33 // never follow redirection 34 return http.ErrUseLastResponse 35 }, 36 } 37 req, _ := http.NewRequest(http.MethodGet, goGetU.String(), nil) 38 req.Header.Add("User-Agent", fmt.Sprintf("ghq/%s (+https://github.com/motemen/ghq)", version)) 39 resp, err := cli.Do(req) 40 if err != nil { 41 return "", nil, err 42 } 43 defer resp.Body.Close() 44 45 return detectVCSAndRepoURL(resp.Body) 46 } 47 48 // find meta tag like following from html 49 // <meta name="go-import" content="gopkg.in/yaml.v2 git https://gopkg.in/yaml.v2"> 50 // ref. https://golang.org/cmd/go/#hdr-Remote_import_paths 51 func detectVCSAndRepoURL(r io.Reader) (string, *url.URL, error) { 52 doc, err := html.Parse(r) 53 if err != nil { 54 return "", nil, err 55 } 56 57 var mImport *metaImport 58 59 var f func(*html.Node) 60 f = func(n *html.Node) { 61 if mImport != nil { 62 return 63 } 64 if n.Type == html.ElementNode && n.Data == "meta" { 65 var ( 66 goImportMeta = false 67 content = "" 68 ) 69 for _, a := range n.Attr { 70 if a.Key == "name" && a.Val == "go-import" { 71 goImportMeta = true 72 continue 73 } 74 if a.Key == "content" { 75 content = a.Val 76 } 77 } 78 if f := strings.Fields(content); goImportMeta && len(f) == 3 && f[1] != "mod" { 79 mImport = &metaImport{ 80 Prefix: f[0], 81 VCS: f[1], 82 RepoRoot: f[2], 83 } 84 } 85 } 86 for c := n.FirstChild; c != nil; c = c.NextSibling { 87 f(c) 88 } 89 } 90 f(doc) 91 92 if mImport == nil { 93 return "", nil, fmt.Errorf("no go-import meta tags detected") 94 } 95 u, err := url.Parse(mImport.RepoRoot) 96 if err != nil { 97 return "", nil, err 98 } 99 return mImport.VCS, u, nil 100 }