cmd/go/internal/modfetch/github: fetch from github
diff --git a/vendor/cmd/go/internal/modfetch/github/fetch.go b/vendor/cmd/go/internal/modfetch/github/fetch.go
new file mode 100644
index 0000000..a88f0e5
--- /dev/null
+++ b/vendor/cmd/go/internal/modfetch/github/fetch.go
@@ -0,0 +1,318 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "cmd/go/internal/modfetch/codehost"
+ web "cmd/go/internal/web2"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+ f := strings.Split(path, "/")
+ if len(f) < 3 || f[0] != "github.com" {
+ return nil, fmt.Errorf("github repo must be github.com/org/project")
+ }
+
+ // Check for moved, renamed, or incorrect case in repository.
+ // Otherwise the API calls all appear to work.
+ // We need to do better here, but it's unclear exactly what.
+ var data struct {
+ FullName string `json:"full_name"`
+ }
+ err := web.Get("https://api.github.com/repos/"+url.PathEscape(f[1])+"/"+url.PathEscape(f[2]), web.DecodeJSON(&data))
+ if err != nil {
+ return nil, err
+ }
+ myFullName := f[1] + "/" + f[2]
+ if myFullName != data.FullName {
+ why := "moved"
+ if strings.EqualFold(myFullName, data.FullName) {
+ why = "wrong case"
+ }
+ return nil, fmt.Errorf("module path of repo is github.com/%s, not %s (%s)", data.FullName, path, why)
+ }
+
+ return newRepo(f[1], f[2]), nil
+}
+
+func newRepo(owner, repository string) codehost.Repo {
+ return &repo{owner, repository}
+}
+
+type repo struct {
+ owner string
+ repo string
+}
+
+func (r *repo) Root() string {
+ return "github.com/" + r.owner + "/" + r.repo
+}
+
+func (r *repo) Tags(prefix string) ([]string, error) {
+ var tags []string
+ u := "https://api.github.com/repos/" + url.PathEscape(r.owner) + "/" + url.PathEscape(r.repo) + "/tags"
+ for u != "" {
+ var data []struct {
+ Name string
+ }
+ var hdr http.Header
+ err := web.Get(u, web.Header(&hdr), web.DecodeJSON(&data))
+ if err != nil {
+ return nil, err
+ }
+ for _, t := range data {
+ if strings.HasPrefix(t.Name, prefix) {
+ tags = append(tags, t.Name)
+ }
+ }
+ last := u
+ u = ""
+ for _, link := range parseLinks(hdr.Get("Link")) {
+ if link.Rel == "next" && link.URL != last {
+ u = link.URL
+ }
+ }
+ }
+ return tags, nil
+}
+
+func (r *repo) LatestAt(t time.Time, branch string) (*codehost.RevInfo, error) {
+ var commits []struct {
+ SHA string
+ Commit struct {
+ Committer struct {
+ Date string
+ }
+ }
+ }
+ err := web.Get(
+ "https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(branch)+"&until="+url.QueryEscape(t.UTC().Format("2006-01-02T15:04:05Z"))+"&per_page=2",
+ web.DecodeJSON(&commits),
+ )
+ if err != nil {
+ return nil, err
+ }
+ if len(commits) == 0 {
+ return nil, fmt.Errorf("no commits")
+ }
+ d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
+ if err != nil {
+ return nil, err
+ }
+
+ info := &codehost.RevInfo{
+ Name: commits[0].SHA,
+ Short: codehost.ShortenSHA1(commits[0].SHA),
+ Time: d,
+ }
+ return info, nil
+}
+
+func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
+ var tag string
+ if !codehost.AllHex(rev) {
+ // Resolve tag to rev
+ tag = rev
+ var ref struct {
+ Ref string
+ Object struct {
+ Type string
+ SHA string
+ URL string
+ }
+ }
+ err := web.Get(
+ "https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/git/refs/tags/"+tag,
+ web.DecodeJSON(&ref),
+ )
+ if err != nil {
+ return nil, err
+ }
+ switch ref.Object.Type {
+ default:
+ return nil, fmt.Errorf("invalid tag %q: not a commit or tag (%q)", tag, ref.Object.Type)
+
+ case "commit":
+ rev = ref.Object.SHA
+
+ case "tag":
+ var info struct {
+ Object struct {
+ SHA string
+ Type string
+ }
+ }
+ err = web.Get(
+ ref.Object.URL,
+ web.DecodeJSON(&info),
+ )
+ if err != nil {
+ return nil, err
+ }
+ if info.Object.Type != "commit" {
+ return nil, fmt.Errorf("invalid annotated tag %q: not a commit (%q)", tag, info.Object.Type)
+ }
+ rev = info.Object.SHA
+ }
+ if rev == "" {
+ return nil, fmt.Errorf("invalid tag %q: missing SHA in GitHub response", tag)
+ }
+ }
+
+ var commits []struct {
+ SHA string
+ Commit struct {
+ Committer struct {
+ Date string
+ }
+ }
+ }
+ err := web.Get(
+ "https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(rev)+"&per_page=2",
+ web.DecodeJSON(&commits),
+ )
+ if err != nil {
+ return nil, err
+ }
+ if len(commits) == 0 {
+ return nil, fmt.Errorf("no commits")
+ }
+ if !strings.HasPrefix(commits[0].SHA, rev) {
+ return nil, fmt.Errorf("wrong rev returned by server")
+ }
+ d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
+ if err != nil {
+ return nil, err
+ }
+ info := &codehost.RevInfo{
+ Name: commits[0].SHA,
+ Short: codehost.ShortenSHA1(commits[0].SHA),
+ Version: tag,
+ Time: d,
+ }
+ return info, nil
+}
+
+func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+ var meta struct {
+ Type string
+ Size int64
+ Name string
+ DownloadURL string `json:"download_url"`
+ }
+ err := web.Get(
+ "https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/contents/"+url.PathEscape(file)+"?ref="+url.QueryEscape(rev),
+ web.DecodeJSON(&meta),
+ )
+ if err != nil {
+ return nil, err
+ }
+ if meta.DownloadURL == "" {
+ return nil, fmt.Errorf("no download URL")
+ }
+
+ // TODO: Use maxSize.
+ var body []byte
+ err = web.Get(meta.DownloadURL, web.ReadAllBody(&body))
+ if err != nil {
+ return nil, err
+ }
+ return body, nil
+}
+
+func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+ // TODO: Make web.Get copy to file for us, with limit.
+ var body io.ReadCloser
+ err = web.Get(
+ "https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/zipball/"+url.PathEscape(rev),
+ web.Body(&body),
+ )
+ if err != nil {
+ if body != nil {
+ body.Close()
+ }
+ return nil, "", err
+ }
+ return body, "", nil
+}
+
+type link struct {
+ URL string
+ Rel string
+}
+
+func parseLinks(text string) []link {
+ var links []link
+ for text != "" {
+ text = strings.TrimSpace(text)
+ if !strings.HasPrefix(text, "<") {
+ break
+ }
+ i := strings.Index(text, ">")
+ if i < 0 {
+ break
+ }
+ u := text[1:i]
+ text = strings.TrimSpace(text[i+1:])
+ Attrs:
+ for strings.HasPrefix(text, ";") {
+ text = text[1:]
+ j := 0
+ for j < len(text) && text[j] != '=' {
+ if text[j] == ',' || text[j] == ';' || text[j] == '"' {
+ break Attrs
+ }
+ j++
+ }
+ if j >= len(text) {
+ break Attrs
+ }
+ key := strings.TrimSpace(text[:j])
+ text = strings.TrimSpace(text[j+1:])
+ var val string
+ if len(text) > 0 && text[0] == '"' {
+ k := 1
+ for k < len(text) && text[k] != '"' {
+ if text[k] == '\\' && k+1 < len(text) {
+ k++
+ }
+ k++
+ }
+ if k >= len(text) {
+ break Attrs
+ }
+ val, text = text[1:k], text[k+1:]
+ } else {
+ k := 0
+ for k < len(text) && text[k] != ';' && text[k] != ',' {
+ if text[k] == '"' {
+ break Attrs
+ }
+ k++
+ }
+ if k >= len(text) {
+ break Attrs
+ }
+ val, text = text[:k], text[k:]
+ }
+ if key == "rel" {
+ links = append(links, link{URL: u, Rel: strings.TrimSpace(val)})
+ }
+ }
+ text = strings.TrimSpace(text)
+ if !strings.HasPrefix(text, ",") {
+ break
+ }
+ text = text[1:]
+ }
+ return links
+}