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