Created
May 2, 2024 02:37
-
-
Save imjasonh/9cb50c573dd3cf2ca26c4a4b931e2282 to your computer and use it in GitHub Desktop.
translate vanity import paths to source URLs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/xml" | |
"fmt" | |
"io" | |
"net/http" | |
"strings" | |
) | |
func main() { | |
fmt.Println(Repo("chainguard.dev/apko")) | |
fmt.Println(Repo("k8s.io/client-go")) | |
fmt.Println(Repo("knative.dev/pkg")) | |
} | |
func Repo(importpath string) (string, error) { | |
if strings.HasPrefix(importpath, "github.com/") { | |
return "https://" + importpath, nil | |
} | |
resp, err := http.Get("https://" + importpath + "?go-get=1") | |
if err != nil { | |
return "", err | |
} | |
defer resp.Body.Close() | |
metas, err := parseMetaGoImports(resp.Body) | |
if err != nil { | |
return "", err | |
} | |
return metas[0].RepoRoot, nil | |
} | |
// parseMetaGoImports returns meta imports from the HTML in r. | |
// Parsing ends at the end of the <head> section or the beginning of the <body>. | |
func parseMetaGoImports(r io.Reader) ([]metaImport, error) { | |
d := xml.NewDecoder(r) | |
d.CharsetReader = charsetReader | |
d.Strict = false | |
var imports []metaImport | |
for { | |
t, err := d.RawToken() | |
if err != nil { | |
if err != io.EOF && len(imports) == 0 { | |
return nil, err | |
} | |
break | |
} | |
if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { | |
break | |
} | |
if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { | |
break | |
} | |
e, ok := t.(xml.StartElement) | |
if !ok || !strings.EqualFold(e.Name.Local, "meta") { | |
continue | |
} | |
if attrValue(e.Attr, "name") != "go-import" { | |
continue | |
} | |
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { | |
imports = append(imports, metaImport{ | |
Prefix: f[0], | |
VCS: f[1], | |
RepoRoot: f[2], | |
}) | |
} | |
} | |
// Extract mod entries if we are paying attention to them. | |
var list []metaImport | |
// Append non-mod entries, ignoring those superseded by a mod entry. | |
for _, m := range imports { | |
if m.VCS != "mod" { | |
list = append(list, m) | |
} | |
} | |
return list, nil | |
} | |
// metaImport represents the parsed <meta name="go-import" | |
// content="prefix vcs reporoot" /> tags from HTML files. | |
type metaImport struct { | |
Prefix, VCS, RepoRoot string | |
} | |
// charsetReader returns a reader that converts from the given charset to UTF-8. | |
// Currently it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful | |
// error which is printed by go get, so the user can find why the package | |
// wasn't downloaded if the encoding is not supported. Note that, in | |
// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters | |
// greater than 0x7f are not rejected). | |
func charsetReader(charset string, input io.Reader) (io.Reader, error) { | |
switch strings.ToLower(charset) { | |
case "utf-8", "ascii": | |
return input, nil | |
default: | |
return nil, fmt.Errorf("can't decode XML document using charset %q", charset) | |
} | |
} | |
// attrValue returns the attribute value for the case-insensitive key | |
// `name', or the empty string if nothing is found. | |
func attrValue(attrs []xml.Attr, name string) string { | |
for _, a := range attrs { | |
if strings.EqualFold(a.Name.Local, name) { | |
return a.Value | |
} | |
} | |
return "" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment