tg := testgo(t)
defer tg.cleanup()
tg.runFail("build", "-v", "./testdata/testinternal")
- tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrong error message for testdata/testinternal")
+ tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package net/http/internal not allowed`, "wrong error message for testdata/testinternal")
}
func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.runFail("build", "-v", "./testdata/testinternal2")
- tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrote error message for testdata/testinternal2")
+ tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package .*internal/w not allowed`, "wrote error message for testdata/testinternal2")
}
func TestRunInternal(t *testing.T) {
tg.setenv("GOPATH", dir)
tg.run("run", filepath.Join(dir, "src/run/good.go"))
tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
- tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package not allowed`, "unexpected error for run/bad.go")
+ tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package run/subdir/internal/private not allowed`, "unexpected error for run/bad.go")
}
func TestRunPkg(t *testing.T) {
--- /dev/null
+// 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 dirhash defines hashes over directory trees.
+package dirhash
+
+import (
+ "archive/zip"
+ "crypto/sha256"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+var DefaultHash = Hash1
+
+type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
+
+func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
+ h := sha256.New()
+ files = append([]string(nil), files...)
+ sort.Strings(files)
+ for _, file := range files {
+ if strings.Contains(file, "\n") {
+ return "", errors.New("filenames with newlines are not supported")
+ }
+ r, err := open(file)
+ if err != nil {
+ return "", err
+ }
+ hf := sha256.New()
+ _, err = io.Copy(hf, r)
+ r.Close()
+ if err != nil {
+ return "", err
+ }
+ fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file)
+ }
+ return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
+}
+
+func HashDir(dir, prefix string, hash Hash) (string, error) {
+ files, err := DirFiles(dir, prefix)
+ if err != nil {
+ return "", err
+ }
+ osOpen := func(name string) (io.ReadCloser, error) {
+ return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
+ }
+ return hash(files, osOpen)
+}
+
+func DirFiles(dir, prefix string) ([]string, error) {
+ var files []string
+ dir = filepath.Clean(dir)
+ err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if info.IsDir() {
+ return nil
+ }
+ rel := file
+ if dir != "." {
+ rel = file[len(dir)+1:]
+ }
+ f := filepath.Join(prefix, rel)
+ files = append(files, filepath.ToSlash(f))
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return files, nil
+}
+
+func HashZip(zipfile string, hash Hash) (string, error) {
+ z, err := zip.OpenReader(zipfile)
+ if err != nil {
+ return "", err
+ }
+ defer z.Close()
+ var files []string
+ zfiles := make(map[string]*zip.File)
+ for _, file := range z.File {
+ files = append(files, file.Name)
+ zfiles[file.Name] = file
+ }
+ zipOpen := func(name string) (io.ReadCloser, error) {
+ f := zfiles[name]
+ if f == nil {
+ return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
+ }
+ return f.Open()
+ }
+ return hash(files, zipOpen)
+}
--- /dev/null
+// 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 dirhash
+
+import (
+ "archive/zip"
+ "crypto/sha256"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func h(s string) string {
+ return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
+}
+
+func htop(k string, s string) string {
+ sum := sha256.Sum256([]byte(s))
+ return k + ":" + base64.StdEncoding.EncodeToString(sum[:])
+}
+
+func TestHash1(t *testing.T) {
+ files := []string{"xyz", "abc"}
+ open := func(name string) (io.ReadCloser, error) {
+ return ioutil.NopCloser(strings.NewReader("data for " + name)), nil
+ }
+ want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz"))
+ out, err := Hash1(files, open)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if out != want {
+ t.Errorf("Hash1(...) = %s, want %s", out, want)
+ }
+
+ _, err = Hash1([]string{"xyz", "a\nbc"}, open)
+ if err == nil {
+ t.Error("Hash1: expected error on newline in filenames")
+ }
+}
+
+func TestHashDir(t *testing.T) {
+ dir, err := ioutil.TempDir("", "dirhash-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
+ out, err := HashDir(dir, "prefix", Hash1)
+ if err != nil {
+ t.Fatalf("HashDir: %v", err)
+ }
+ if out != want {
+ t.Errorf("HashDir(...) = %s, want %s", out, want)
+ }
+}
+
+func TestHashZip(t *testing.T) {
+ f, err := ioutil.TempFile("", "dirhash-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+
+ z := zip.NewWriter(f)
+ w, err := z.Create("prefix/xyz")
+ if err != nil {
+ t.Fatal(err)
+ }
+ w.Write([]byte("data for xyz"))
+ w, err = z.Create("prefix/abc")
+ if err != nil {
+ t.Fatal(err)
+ }
+ w.Write([]byte("data for abc"))
+ if err := z.Close(); err != nil {
+ t.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
+ out, err := HashZip(f.Name(), Hash1)
+ if err != nil {
+ t.Fatalf("HashDir: %v", err)
+ }
+ if out != want {
+ t.Errorf("HashDir(...) = %s, want %s", out, want)
+ }
+}
+
+func TestDirFiles(t *testing.T) {
+ dir, err := ioutil.TempDir("", "dirfiles-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ prefix := "foo/bar@v2.3.4"
+ out, err := DirFiles(dir, prefix)
+ if err != nil {
+ t.Fatalf("DirFiles: %v", err)
+ }
+ for _, file := range out {
+ if !strings.HasPrefix(file, prefix) {
+ t.Errorf("Dir file = %s, want prefix %s", file, prefix)
+ }
+ }
+}
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/vgo"
"cmd/go/internal/work"
)
{Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOOS", Value: cfg.Goos},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
+ {Name: "GOPROXY", Value: os.Getenv("GOPROXY")},
{Name: "GORACE", Value: os.Getenv("GORACE")},
{Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
{Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
{Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
{Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
+ {Name: "VGOMODROOT", Value: vgo.ModRoot},
}
}
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
+ "cmd/go/internal/vgo"
+ "fmt"
+ "os"
)
var CmdFix = &base.Command{
}
func runFix(cmd *base.Command, args []string) {
+ printed := false
for _, pkg := range load.Packages(args) {
+ if vgo.Enabled() && !pkg.Module.Top {
+ if !printed {
+ fmt.Fprintf(os.Stderr, "vgo: not fixing packages in dependency modules\n")
+ printed = true
+ }
+ continue
+ }
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.
package fmtcmd
import (
+ "fmt"
"os"
"path/filepath"
"runtime"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
+ "cmd/go/internal/vgo"
)
func init() {
}
func runFmt(cmd *base.Command, args []string) {
+ printed := false
gofmt := gofmtPath()
procs := runtime.GOMAXPROCS(0)
var wg sync.WaitGroup
}()
}
for _, pkg := range load.PackagesAndErrors(args) {
+ if vgo.Enabled() && !pkg.Module.Top {
+ if !printed {
+ fmt.Fprintf(os.Stderr, "vgo: not formatting packages in dependency modules\n")
+ printed = true
+ }
+ continue
+ }
if pkg.Error != nil {
if strings.HasPrefix(pkg.Error.Err, "build constraints exclude all Go files") {
// Skip this error, as we will format
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/vgo"
"cmd/go/internal/work"
)
}
}
// Even if the arguments are .go files, this loop suffices.
+ printed := false
for _, pkg := range load.Packages(args) {
+ if vgo.Enabled() && !pkg.Module.Top {
+ if !printed {
+ fmt.Fprintf(os.Stderr, "vgo: not generating in packages in dependency modules\n")
+ printed = true
+ }
+ continue
+ }
+
pkgName := pkg.Name
for _, file := range pkg.InternalGoFiles() {
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/search"
"cmd/go/internal/str"
+ "cmd/go/internal/vgo"
"cmd/go/internal/web"
"cmd/go/internal/work"
)
}
func runGet(cmd *base.Command, args []string) {
+ if vgo.Enabled() {
+ base.Fatalf("go get: vgo not implemented")
+ }
+
work.BuildInit()
if *getF && !*getU {
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
func downloadPaths(args []string) []string {
- args = load.ImportPathsNoDotExpansion(args)
+ args = load.ImportPathsForGoGet(args)
var out []string
for _, a := range args {
if strings.Contains(a, "...") {
// warnings. They will be printed by the
// eventual call to importPaths instead.
if build.IsLocalImport(a) {
- expand = load.MatchPackagesInFS(a)
+ expand = search.MatchPackagesInFS(a)
} else {
- expand = load.MatchPackages(a)
+ expand = search.MatchPackages(a)
}
if len(expand) > 0 {
out = append(out, expand...)
// for p has been replaced in the package cache.
if wildcardOkay && strings.Contains(arg, "...") {
if build.IsLocalImport(arg) {
- args = load.MatchPackagesInFS(arg)
+ args = search.MatchPackagesInFS(arg)
} else {
- args = load.MatchPackages(arg)
+ args = search.MatchPackages(arg)
}
isWildcard = true
}
var usageTemplate = `Go is a tool for managing Go source code.
+This is vgo, an experimental go command with support for package versioning.
+Even though you are invoking it as vgo, most of the messages printed will
+still say "go", not "vgo". Sorry.
+
Usage:
go command [arguments]
--- /dev/null
+// 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.
+
+// Copied from Go distribution src/go/build/build.go, syslist.go
+
+package imports
+
+import (
+ "bytes"
+ "strings"
+ "unicode"
+)
+
+var slashslash = []byte("//")
+
+// ShouldBuild reports whether it is okay to use this file,
+// The rule is that in the file's leading run of // comments
+// and blank lines, which must be followed by a blank line
+// (to avoid including a Go package clause doc comment),
+// lines beginning with '// +build' are taken as build directives.
+//
+// The file is accepted only if each such line lists something
+// matching the file. For example:
+//
+// // +build windows linux
+//
+// marks the file as applicable only on Windows and Linux.
+//
+// If tags["*"] is true, then ShouldBuild will consider every
+// build tag except "ignore" to be both true and false for
+// the purpose of satisfying build tags, in order to estimate
+// (conservatively) whether a file could ever possibly be used
+// in any build.
+//
+func ShouldBuild(content []byte, tags map[string]bool) bool {
+ // Pass 1. Identify leading run of // comments and blank lines,
+ // which must be followed by a blank line.
+ end := 0
+ p := content
+ for len(p) > 0 {
+ line := p
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, p = line[:i], p[i+1:]
+ } else {
+ p = p[len(p):]
+ }
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 { // Blank line
+ end = len(content) - len(p)
+ continue
+ }
+ if !bytes.HasPrefix(line, slashslash) { // Not comment line
+ break
+ }
+ }
+ content = content[:end]
+
+ // Pass 2. Process each line in the run.
+ p = content
+ allok := true
+ for len(p) > 0 {
+ line := p
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, p = line[:i], p[i+1:]
+ } else {
+ p = p[len(p):]
+ }
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, slashslash) {
+ continue
+ }
+ line = bytes.TrimSpace(line[len(slashslash):])
+ if len(line) > 0 && line[0] == '+' {
+ // Looks like a comment +line.
+ f := strings.Fields(string(line))
+ if f[0] == "+build" {
+ ok := false
+ for _, tok := range f[1:] {
+ if matchTags(tok, tags) {
+ ok = true
+ }
+ }
+ if !ok {
+ allok = false
+ }
+ }
+ }
+ }
+
+ return allok
+}
+
+// matchTags reports whether the name is one of:
+//
+// tag (if tags[tag] is true)
+// !tag (if tags[tag] is false)
+// a comma-separated list of any of these
+//
+func matchTags(name string, tags map[string]bool) bool {
+ if name == "" {
+ return false
+ }
+ if i := strings.Index(name, ","); i >= 0 {
+ // comma-separated list
+ ok1 := matchTags(name[:i], tags)
+ ok2 := matchTags(name[i+1:], tags)
+ return ok1 && ok2
+ }
+ if strings.HasPrefix(name, "!!") { // bad syntax, reject always
+ return false
+ }
+ if strings.HasPrefix(name, "!") { // negation
+ return len(name) > 1 && matchTag(name[1:], tags, false)
+ }
+ return matchTag(name, tags, true)
+}
+
+// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
+func matchTag(name string, tags map[string]bool, want bool) bool {
+ // Tags must be letters, digits, underscores or dots.
+ // Unlike in Go identifiers, all digits are fine (e.g., "386").
+ for _, c := range name {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ return false
+ }
+ }
+
+ if tags["*"] && name != "" && name != "ignore" {
+ // Special case for gathering all possible imports:
+ // if we put * in the tags map then all tags
+ // except "ignore" are considered both present and not
+ // (so we return true no matter how 'want' is set).
+ return true
+ }
+
+ have := tags[name]
+ if name == "linux" {
+ have = have || tags["android"]
+ }
+ return have == want
+}
+
+// MatchFile returns false if the name contains a $GOOS or $GOARCH
+// suffix which does not match the current system.
+// The recognized name formats are:
+//
+// name_$(GOOS).*
+// name_$(GOARCH).*
+// name_$(GOOS)_$(GOARCH).*
+// name_$(GOOS)_test.*
+// name_$(GOARCH)_test.*
+// name_$(GOOS)_$(GOARCH)_test.*
+//
+// An exception: if GOOS=android, then files with GOOS=linux are also matched.
+//
+// If tags["*"] is true, then MatchFile will consider all possible
+// GOOS and GOARCH to be available and will consequently
+// always return true.
+func MatchFile(name string, tags map[string]bool) bool {
+ if tags["*"] {
+ return true
+ }
+ if dot := strings.Index(name, "."); dot != -1 {
+ name = name[:dot]
+ }
+
+ // Before Go 1.4, a file called "linux.go" would be equivalent to having a
+ // build tag "linux" in that file. For Go 1.4 and beyond, we require this
+ // auto-tagging to apply only to files with a non-empty prefix, so
+ // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
+ // systems, such as android, to arrive without breaking existing code with
+ // innocuous source code in "android.go". The easiest fix: cut everything
+ // in the name before the initial _.
+ i := strings.Index(name, "_")
+ if i < 0 {
+ return true
+ }
+ name = name[i:] // ignore everything before first _
+
+ l := strings.Split(name, "_")
+ if n := len(l); n > 0 && l[n-1] == "test" {
+ l = l[:n-1]
+ }
+ n := len(l)
+ if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+ return tags[l[n-2]] && tags[l[n-1]]
+ }
+ if n >= 1 && knownOS[l[n-1]] {
+ return tags[l[n-1]]
+ }
+ if n >= 1 && knownArch[l[n-1]] {
+ return tags[l[n-1]]
+ }
+ return true
+}
+
+var knownOS = make(map[string]bool)
+var knownArch = make(map[string]bool)
+
+func init() {
+ for _, v := range strings.Fields(goosList) {
+ knownOS[v] = true
+ }
+ for _, v := range strings.Fields(goarchList) {
+ knownArch[v] = true
+ }
+}
+
+const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos "
+const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "
--- /dev/null
+// Copyright 2012 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.
+
+// Copied from Go distribution src/go/build/read.go.
+
+package imports
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "unicode/utf8"
+)
+
+type importReader struct {
+ b *bufio.Reader
+ buf []byte
+ peek byte
+ err error
+ eof bool
+ nerr int
+}
+
+func isIdent(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
+}
+
+var (
+ errSyntax = errors.New("syntax error")
+ errNUL = errors.New("unexpected NUL in input")
+)
+
+// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
+func (r *importReader) syntaxError() {
+ if r.err == nil {
+ r.err = errSyntax
+ }
+}
+
+// readByte reads the next byte from the input, saves it in buf, and returns it.
+// If an error occurs, readByte records the error in r.err and returns 0.
+func (r *importReader) readByte() byte {
+ c, err := r.b.ReadByte()
+ if err == nil {
+ r.buf = append(r.buf, c)
+ if c == 0 {
+ err = errNUL
+ }
+ }
+ if err != nil {
+ if err == io.EOF {
+ r.eof = true
+ } else if r.err == nil {
+ r.err = err
+ }
+ c = 0
+ }
+ return c
+}
+
+// peekByte returns the next byte from the input reader but does not advance beyond it.
+// If skipSpace is set, peekByte skips leading spaces and comments.
+func (r *importReader) peekByte(skipSpace bool) byte {
+ if r.err != nil {
+ if r.nerr++; r.nerr > 10000 {
+ panic("go/build: import reader looping")
+ }
+ return 0
+ }
+
+ // Use r.peek as first input byte.
+ // Don't just return r.peek here: it might have been left by peekByte(false)
+ // and this might be peekByte(true).
+ c := r.peek
+ if c == 0 {
+ c = r.readByte()
+ }
+ for r.err == nil && !r.eof {
+ if skipSpace {
+ // For the purposes of this reader, semicolons are never necessary to
+ // understand the input and are treated as spaces.
+ switch c {
+ case ' ', '\f', '\t', '\r', '\n', ';':
+ c = r.readByte()
+ continue
+
+ case '/':
+ c = r.readByte()
+ if c == '/' {
+ for c != '\n' && r.err == nil && !r.eof {
+ c = r.readByte()
+ }
+ } else if c == '*' {
+ var c1 byte
+ for (c != '*' || c1 != '/') && r.err == nil {
+ if r.eof {
+ r.syntaxError()
+ }
+ c, c1 = c1, r.readByte()
+ }
+ } else {
+ r.syntaxError()
+ }
+ c = r.readByte()
+ continue
+ }
+ }
+ break
+ }
+ r.peek = c
+ return r.peek
+}
+
+// nextByte is like peekByte but advances beyond the returned byte.
+func (r *importReader) nextByte(skipSpace bool) byte {
+ c := r.peekByte(skipSpace)
+ r.peek = 0
+ return c
+}
+
+// readKeyword reads the given keyword from the input.
+// If the keyword is not present, readKeyword records a syntax error.
+func (r *importReader) readKeyword(kw string) {
+ r.peekByte(true)
+ for i := 0; i < len(kw); i++ {
+ if r.nextByte(false) != kw[i] {
+ r.syntaxError()
+ return
+ }
+ }
+ if isIdent(r.peekByte(false)) {
+ r.syntaxError()
+ }
+}
+
+// readIdent reads an identifier from the input.
+// If an identifier is not present, readIdent records a syntax error.
+func (r *importReader) readIdent() {
+ c := r.peekByte(true)
+ if !isIdent(c) {
+ r.syntaxError()
+ return
+ }
+ for isIdent(r.peekByte(false)) {
+ r.peek = 0
+ }
+}
+
+// readString reads a quoted string literal from the input.
+// If an identifier is not present, readString records a syntax error.
+func (r *importReader) readString(save *[]string) {
+ switch r.nextByte(true) {
+ case '`':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ if r.nextByte(false) == '`' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof {
+ r.syntaxError()
+ }
+ }
+ case '"':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ c := r.nextByte(false)
+ if c == '"' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof || c == '\n' {
+ r.syntaxError()
+ }
+ if c == '\\' {
+ r.nextByte(false)
+ }
+ }
+ default:
+ r.syntaxError()
+ }
+}
+
+// readImport reads an import clause - optional identifier followed by quoted string -
+// from the input.
+func (r *importReader) readImport(imports *[]string) {
+ c := r.peekByte(true)
+ if c == '.' {
+ r.peek = 0
+ } else if isIdent(c) {
+ r.readIdent()
+ }
+ r.readString(imports)
+}
+
+// ReadComments is like ioutil.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func ReadComments(f io.Reader) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+ r.peekByte(true)
+ if r.err == nil && !r.eof {
+ // Didn't reach EOF, so must have found a non-space byte. Remove it.
+ r.buf = r.buf[:len(r.buf)-1]
+ }
+ return r.buf, r.err
+}
+
+// ReadImports is like ioutil.ReadAll, except that it expects a Go file as input
+// and stops reading the input once the imports have completed.
+func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+
+ r.readKeyword("package")
+ r.readIdent()
+ for r.peekByte(true) == 'i' {
+ r.readKeyword("import")
+ if r.peekByte(true) == '(' {
+ r.nextByte(false)
+ for r.peekByte(true) != ')' && r.err == nil {
+ r.readImport(imports)
+ }
+ r.nextByte(false)
+ } else {
+ r.readImport(imports)
+ }
+ }
+
+ // If we stopped successfully before EOF, we read a byte that told us we were done.
+ // Return all but that last byte, which would cause a syntax error if we let it through.
+ if r.err == nil && !r.eof {
+ return r.buf[:len(r.buf)-1], nil
+ }
+
+ // If we stopped for a syntax error, consume the whole file so that
+ // we are sure we don't change the errors that go/parser returns.
+ if r.err == errSyntax && !reportSyntaxError {
+ r.err = nil
+ for r.err == nil && !r.eof {
+ r.readByte()
+ }
+ }
+
+ return r.buf, r.err
+}
--- /dev/null
+// Copyright 2012 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.
+
+// Copied from Go distribution src/go/build/read.go.
+
+package imports
+
+import (
+ "io"
+ "strings"
+ "testing"
+)
+
+const quote = "`"
+
+type readTest struct {
+ // Test input contains ℙ where readImports should stop.
+ in string
+ err string
+}
+
+var readImportsTests = []readTest{
+ {
+ `package p`,
+ "",
+ },
+ {
+ `package p; import "x"`,
+ "",
+ },
+ {
+ `package p; import . "x"`,
+ "",
+ },
+ {
+ `package p; import "x";ℙvar x = 1`,
+ "",
+ },
+ {
+ `package p
+
+ // comment
+
+ import "x"
+ import _ "x"
+ import a "x"
+
+ /* comment */
+
+ import (
+ "x" /* comment */
+ _ "x"
+ a "x" // comment
+ ` + quote + `x` + quote + `
+ _ /*comment*/ ` + quote + `x` + quote + `
+ a ` + quote + `x` + quote + `
+ )
+ import (
+ )
+ import ()
+ import()import()import()
+ import();import();import()
+
+ ℙvar x = 1
+ `,
+ "",
+ },
+}
+
+var readCommentsTests = []readTest{
+ {
+ `ℙpackage p`,
+ "",
+ },
+ {
+ `ℙpackage p; import "x"`,
+ "",
+ },
+ {
+ `ℙpackage p; import . "x"`,
+ "",
+ },
+ {
+ `// foo
+
+ /* bar */
+
+ /* quux */ // baz
+
+ /*/ zot */
+
+ // asdf
+ ℙHello, world`,
+ "",
+ },
+}
+
+func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
+ for i, tt := range tests {
+ var in, testOut string
+ j := strings.Index(tt.in, "ℙ")
+ if j < 0 {
+ in = tt.in
+ testOut = tt.in
+ } else {
+ in = tt.in[:j] + tt.in[j+len("ℙ"):]
+ testOut = tt.in[:j]
+ }
+ r := strings.NewReader(in)
+ buf, err := read(r)
+ if err != nil {
+ if tt.err == "" {
+ t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
+ continue
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
+ continue
+ }
+ continue
+ }
+ if err == nil && tt.err != "" {
+ t.Errorf("#%d: success, expected %q", i, tt.err)
+ continue
+ }
+
+ out := string(buf)
+ if out != testOut {
+ t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
+ }
+ }
+}
+
+func TestReadImports(t *testing.T) {
+ testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
+}
+
+func TestReadComments(t *testing.T) {
+ testRead(t, readCommentsTests, ReadComments)
+}
+
+var readFailuresTests = []readTest{
+ {
+ `package`,
+ "syntax error",
+ },
+ {
+ "package p\n\x00\nimport `math`\n",
+ "unexpected NUL in input",
+ },
+ {
+ `package p; import`,
+ "syntax error",
+ },
+ {
+ `package p; import "`,
+ "syntax error",
+ },
+ {
+ "package p; import ` \n\n",
+ "syntax error",
+ },
+ {
+ `package p; import "x`,
+ "syntax error",
+ },
+ {
+ `package p; import _`,
+ "syntax error",
+ },
+ {
+ `package p; import _ "`,
+ "syntax error",
+ },
+ {
+ `package p; import _ "x`,
+ "syntax error",
+ },
+ {
+ `package p; import .`,
+ "syntax error",
+ },
+ {
+ `package p; import . "`,
+ "syntax error",
+ },
+ {
+ `package p; import . "x`,
+ "syntax error",
+ },
+ {
+ `package p; import (`,
+ "syntax error",
+ },
+ {
+ `package p; import ("`,
+ "syntax error",
+ },
+ {
+ `package p; import ("x`,
+ "syntax error",
+ },
+ {
+ `package p; import ("x"`,
+ "syntax error",
+ },
+}
+
+func TestReadFailures(t *testing.T) {
+ // Errors should be reported (true arg to readImports).
+ testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
+}
+
+func TestReadFailuresIgnored(t *testing.T) {
+ // Syntax errors should not be reported (false arg to readImports).
+ // Instead, entire file should be the output and no error.
+ // Convert tests not to return syntax errors.
+ tests := make([]readTest, len(readFailuresTests))
+ copy(tests, readFailuresTests)
+ for i := range tests {
+ tt := &tests[i]
+ if !strings.Contains(tt.err, "NUL") {
+ tt.err = ""
+ }
+ }
+ testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) })
+}
--- /dev/null
+// 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 imports
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
+ infos, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, nil, err
+ }
+ var files []string
+ for _, info := range infos {
+ name := info.Name()
+ if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
+ files = append(files, filepath.Join(dir, name))
+ }
+ }
+ return scanFiles(files, tags, false)
+}
+
+func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) {
+ return scanFiles(files, tags, true)
+}
+
+func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) {
+ imports := make(map[string]bool)
+ testImports := make(map[string]bool)
+ numFiles := 0
+ for _, name := range files {
+ r, err := os.Open(name)
+ if err != nil {
+ return nil, nil, err
+ }
+ var list []string
+ data, err := ReadImports(r, false, &list)
+ r.Close()
+ if err != nil {
+ return nil, nil, fmt.Errorf("reading %s: %v", name, err)
+ }
+ if !explicitFiles && !ShouldBuild(data, tags) {
+ continue
+ }
+ numFiles++
+ m := imports
+ if strings.HasSuffix(name, "_test.go") {
+ m = testImports
+ }
+ for _, p := range list {
+ q, err := strconv.Unquote(p)
+ if err != nil {
+ continue
+ }
+ m[q] = true
+ }
+ }
+ if numFiles == 0 {
+ return nil, nil, ErrNoGo
+ }
+ return keys(imports), keys(testImports), nil
+}
+
+var ErrNoGo = fmt.Errorf("no Go source files")
+
+func keys(m map[string]bool) []string {
+ var list []string
+ for k := range m {
+ list = append(list, k)
+ }
+ sort.Strings(list)
+ return list
+}
--- /dev/null
+// 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 imports
+
+import (
+ "internal/testenv"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+func TestScan(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags())
+ if err != nil {
+ t.Fatal(err)
+ }
+ foundBase64 := false
+ for _, p := range imports {
+ if p == "encoding/base64" {
+ foundBase64 = true
+ }
+ if p == "encoding/binary" {
+ // A dependency but not an import
+ t.Errorf("json reported as importing encoding/binary but does not")
+ }
+ if p == "net/http" {
+ // A test import but not an import
+ t.Errorf("json reported as importing encoding/binary but does not")
+ }
+ }
+ if !foundBase64 {
+ t.Errorf("json missing import encoding/base64 (%q)", imports)
+ }
+
+ foundHTTP := false
+ for _, p := range testImports {
+ if p == "net/http" {
+ foundHTTP = true
+ }
+ if p == "unicode/utf16" {
+ // A package import but not a test import
+ t.Errorf("json reported as test-importing unicode/utf16 but does not")
+ }
+ }
+ if !foundHTTP {
+ t.Errorf("json missing test import net/http (%q)", testImports)
+ }
+}
+
+func TestScanStar(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := []string{"import1", "import2", "import3", "import4"}
+ if !reflect.DeepEqual(imports, want) {
+ t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want)
+ }
+}
--- /dev/null
+// 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 imports
+
+import "cmd/go/internal/cfg"
+
+var tags map[string]bool
+
+func Tags() map[string]bool {
+ if tags == nil {
+ tags = loadTags()
+ }
+ return tags
+}
+
+func loadTags() map[string]bool {
+ tags := map[string]bool{
+ cfg.BuildContext.GOOS: true,
+ cfg.BuildContext.GOARCH: true,
+ cfg.BuildContext.Compiler: true,
+ }
+ if cfg.BuildContext.CgoEnabled {
+ tags["cgo"] = true
+ }
+ // TODO: Should read these out of GOROOT source code?
+ for _, tag := range cfg.BuildContext.BuildTags {
+ tags[tag] = true
+ }
+ for _, tag := range cfg.BuildContext.ReleaseTags {
+ tags[tag] = true
+ }
+ return tags
+}
--- /dev/null
+package x
+
+import "import1"
--- /dev/null
+// +build blahblh
+// +build linux
+// +build !linux
+// +build windows
+// +build darwin
+
+package x
+
+import "import4"
--- /dev/null
+package xxxx
+
+import "import3"
--- /dev/null
+package x
+
+import "import2"
return filepath.ToSlash(dir[len(root):]), true
}
-// hasPathPrefix reports whether the path s begins with the
-// elements in prefix.
-func hasPathPrefix(s, prefix string) bool {
- switch {
- default:
- return false
- case len(s) == len(prefix):
- return s == prefix
- case len(s) > len(prefix):
- if prefix != "" && prefix[len(prefix)-1] == '/' {
- return strings.HasPrefix(s, prefix)
- }
- return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
- }
-}
-
// expandPath returns the symlink-expanded form of path.
func expandPath(p string) string {
x, err := filepath.EvalSymlinks(p)
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/modinfo"
+ "cmd/go/internal/search"
"cmd/go/internal/str"
+ "cmd/go/internal/vgo"
)
var IgnoreImports bool // control whether we ignore imports in packages
TestImports []string `json:",omitempty"` // imports from TestGoFiles
XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
+
+ Module *modinfo.ModulePublic `json:",omitempty"` // info about package module
}
// AllFiles returns the names of all the files considered for the package.
CoverVars map[string]*CoverVar // variables created by coverage analysis
OmitDebug bool // tell linker not to write debug information
GobinSubdir bool // install target would be subdir of GOBIN
+ BuildInfo string // add this info to package main
TestmainGo *[]byte // content for _testmain.go
Asmflags []string // -asmflags for this package
// TODO? Target
p.Goroot = pp.Goroot
- p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath)
+ p.Standard = p.Goroot && p.ImportPath != "" && search.IsStandardImportPath(p.ImportPath)
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
}
}
-// isStandardImportPath reports whether $GOROOT/src/path should be considered
-// part of the standard distribution. For historical reasons we allow people to add
-// their own code to $GOROOT instead of using $GOPATH, but we assume that
-// code will start with a domain name (dot in the first element).
-func isStandardImportPath(path string) bool {
- i := strings.Index(path, "/")
- if i < 0 {
- i = len(path)
- }
- elem := path[:i]
- return !strings.Contains(elem, ".")
-}
-
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
importPath := path
origPath := path
isLocal := build.IsLocalImport(path)
+ var vgoDir string
+ var vgoErr error
if isLocal {
importPath = dirToImportPath(filepath.Join(srcDir, path))
+ } else if vgo.Enabled() {
+ parentPath := ""
+ if parent != nil {
+ parentPath = parent.ImportPath
+ }
+ var p string
+ vgoDir, p, vgoErr = vgo.Lookup(parentPath, path)
+ if vgoErr == nil {
+ importPath = p
+ }
} else if mode&ResolveImport != 0 {
// We do our own path resolution, because we want to
// find out the key to use in packageCache without the
// Load package.
// Import always returns bp != nil, even if an error occurs,
// in order to return partial information.
- buildMode := build.ImportComment
- if mode&ResolveImport == 0 || path != origPath {
- // Not vendoring, or we already found the vendored path.
- buildMode |= build.IgnoreVendor
+ var bp *build.Package
+ var err error
+ if vgoDir != "" {
+ bp, err = cfg.BuildContext.ImportDir(vgoDir, 0)
+ } else if vgoErr != nil {
+ bp = new(build.Package)
+ err = fmt.Errorf("unknown import path %q: %v", importPath, vgoErr)
+ } else {
+ buildMode := build.ImportComment
+ if mode&ResolveImport == 0 || path != origPath {
+ // Not vendoring, or we already found the vendored path.
+ buildMode |= build.IgnoreVendor
+ }
+ bp, err = cfg.BuildContext.Import(path, srcDir, buildMode)
}
- bp, err := cfg.BuildContext.Import(path, srcDir, buildMode)
bp.ImportPath = importPath
if cfg.GOBIN != "" {
bp.BinDir = cfg.GOBIN
+ } else if vgo.Enabled() {
+ bp.BinDir = vgo.BinDir()
}
- if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
+ if vgoDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
}
p = setErrorPos(p, importPos)
}
- if origPath != cleanImport(origPath) {
+ if vgoDir == "" && origPath != cleanImport(origPath) {
p.Error = &PackageError{
ImportStack: stk.Copy(),
Err: fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)),
// If vendor expansion doesn't trigger, then the path is also subject to
// Go 1.11 vgo legacy conversion (golang.org/issue/25069).
func ResolveImportPath(parent *Package, path string) (found string) {
+ if vgo.Enabled() {
+ parentPath := ""
+ if parent != nil {
+ parentPath = parent.ImportPath
+ }
+ if _, p, e := vgo.Lookup(parentPath, path); e == nil {
+ return p
+ }
+ return path
+ }
found = VendoredImportPath(parent, path)
if found != path {
return found
perr := *p
perr.Error = &PackageError{
ImportStack: stk.Copy(),
- Err: "use of internal package not allowed",
+ Err: "use of internal package " + p.ImportPath + " not allowed",
}
perr.Incomplete = true
return &perr
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
+ if p.Internal.Build.BinDir == "" && vgo.Enabled() {
+ p.Internal.Build.BinDir = vgo.BinDir()
+ }
if p.Internal.Build.BinDir != "" {
// Install to GOBIN or bin of GOPATH entry.
p.Target = filepath.Join(p.Internal.Build.BinDir, elem)
setError(fmt.Sprintf("case-insensitive import collision: %q and %q", p.ImportPath, other))
return
}
+
+ if vgo.Enabled() {
+ p.Module = vgo.PackageModuleInfo(p.ImportPath)
+ if p.Name == "main" {
+ p.Internal.BuildInfo = vgo.PackageBuildInfo(p.ImportPath, p.Deps)
+ }
+ }
}
// SafeArg reports whether arg is a "safe" command-line argument,
return pkgs
}
+func ImportPaths(args []string) []string {
+ if cmdlineMatchers == nil {
+ SetCmdlinePatterns(search.CleanImportPaths(args))
+ }
+ return vgo.ImportPaths(args)
+}
+
+func ImportPathsForGoGet(args []string) []string {
+ if cmdlineMatchers == nil {
+ SetCmdlinePatterns(search.CleanImportPaths(args))
+ }
+ return search.ImportPathsNoDotExpansion(args)
+}
+
// packagesForBuild is like 'packages' but fails if any of
// the packages or their dependencies have errors
// (cannot be built).
}
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
+ vgo.AddImports(gofiles)
+
var err error
if dir == "" {
dir = base.Cwd
}
if cfg.GOBIN != "" {
pkg.Target = filepath.Join(cfg.GOBIN, exe)
+ } else if vgo.Enabled() {
+ pkg.Target = filepath.Join(vgo.BinDir(), exe)
}
}
package load
import (
- "cmd/go/internal/cfg"
- "fmt"
- "go/build"
- "log"
- "os"
- "path"
"path/filepath"
- "regexp"
"runtime"
"strings"
-)
-
-// allPackages returns all the packages that can be found
-// under the $GOPATH directories and $GOROOT matching pattern.
-// The pattern is either "all" (all packages), "std" (standard packages),
-// "cmd" (standard commands), or a path including "...".
-func allPackages(pattern string) []string {
- pkgs := MatchPackages(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
- }
- return pkgs
-}
-
-// allPackagesInFS is like allPackages but is passed a pattern
-// beginning ./ or ../, meaning it should scan the tree rooted
-// at the given directory. There are ... in the pattern too.
-func allPackagesInFS(pattern string) []string {
- pkgs := MatchPackagesInFS(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
- }
- return pkgs
-}
-
-// MatchPackages returns a list of package paths matching pattern
-// (see go help packages for pattern syntax).
-func MatchPackages(pattern string) []string {
- match := func(string) bool { return true }
- treeCanMatch := func(string) bool { return true }
- if !IsMetaPackage(pattern) {
- match = matchPattern(pattern)
- treeCanMatch = treeCanMatchPattern(pattern)
- }
-
- have := map[string]bool{
- "builtin": true, // ignore pseudo-package that exists only for documentation
- }
- if !cfg.BuildContext.CgoEnabled {
- have["runtime/cgo"] = true // ignore during walk
- }
- var pkgs []string
- for _, src := range cfg.BuildContext.SrcDirs() {
- if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
- continue
- }
- src = filepath.Clean(src) + string(filepath.Separator)
- root := src
- if pattern == "cmd" {
- root += "cmd" + string(filepath.Separator)
- }
- filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
- if err != nil || path == src {
- return nil
- }
-
- want := true
- // Avoid .foo, _foo, and testdata directory trees.
- _, elem := filepath.Split(path)
- if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
- want = false
- }
-
- name := filepath.ToSlash(path[len(src):])
- if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
- // The name "std" is only the standard library.
- // If the name is cmd, it's the root of the command tree.
- want = false
- }
- if !treeCanMatch(name) {
- want = false
- }
-
- if !fi.IsDir() {
- if fi.Mode()&os.ModeSymlink != 0 && want {
- if target, err := os.Stat(path); err == nil && target.IsDir() {
- fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
- }
- }
- return nil
- }
- if !want {
- return filepath.SkipDir
- }
-
- if have[name] {
- return nil
- }
- have[name] = true
- if !match(name) {
- return nil
- }
- pkg, err := cfg.BuildContext.ImportDir(path, 0)
- if err != nil {
- if _, noGo := err.(*build.NoGoError); noGo {
- return nil
- }
- }
-
- // If we are expanding "cmd", skip main
- // packages under cmd/vendor. At least as of
- // March, 2017, there is one there for the
- // vendored pprof tool.
- if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
- return nil
- }
-
- pkgs = append(pkgs, name)
- return nil
- })
- }
- return pkgs
-}
-
-// MatchPackagesInFS returns a list of package paths matching pattern,
-// which must begin with ./ or ../
-// (see go help packages for pattern syntax).
-func MatchPackagesInFS(pattern string) []string {
- // Find directory to begin the scan.
- // Could be smarter but this one optimization
- // is enough for now, since ... is usually at the
- // end of a path.
- i := strings.Index(pattern, "...")
- dir, _ := path.Split(pattern[:i])
-
- // pattern begins with ./ or ../.
- // path.Clean will discard the ./ but not the ../.
- // We need to preserve the ./ for pattern matching
- // and in the returned import paths.
- prefix := ""
- if strings.HasPrefix(pattern, "./") {
- prefix = "./"
- }
- match := matchPattern(pattern)
-
- var pkgs []string
- filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
- return nil
- }
- if path == dir {
- // filepath.Walk starts at dir and recurses. For the recursive case,
- // the path is the result of filepath.Join, which calls filepath.Clean.
- // The initial case is not Cleaned, though, so we do this explicitly.
- //
- // This converts a path like "./io/" to "io". Without this step, running
- // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
- // package, because prepending the prefix "./" to the unclean path would
- // result in "././io", and match("././io") returns false.
- path = filepath.Clean(path)
- }
-
- // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
- _, elem := filepath.Split(path)
- dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
- if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
- return filepath.SkipDir
- }
-
- name := prefix + filepath.ToSlash(path)
- if !match(name) {
- return nil
- }
-
- // We keep the directory if we can import it, or if we can't import it
- // due to invalid Go source files. This means that directories containing
- // parse errors will be built (and fail) instead of being silently skipped
- // as not matching the pattern. Go 1.5 and earlier skipped, but that
- // behavior means people miss serious mistakes.
- // See golang.org/issue/11407.
- if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
- if _, noGo := err.(*build.NoGoError); !noGo {
- log.Print(err)
- }
- return nil
- }
- pkgs = append(pkgs, name)
- return nil
- })
- return pkgs
-}
-
-// treeCanMatchPattern(pattern)(name) reports whether
-// name or children of name can possibly match pattern.
-// Pattern is the same limited glob accepted by matchPattern.
-func treeCanMatchPattern(pattern string) func(name string) bool {
- wildCard := false
- if i := strings.Index(pattern, "..."); i >= 0 {
- wildCard = true
- pattern = pattern[:i]
- }
- return func(name string) bool {
- return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
- wildCard && strings.HasPrefix(name, pattern)
- }
-}
-
-// matchPattern(pattern)(name) reports whether
-// name matches pattern. Pattern is a limited glob
-// pattern in which '...' means 'any string' and there
-// is no other special syntax.
-// Unfortunately, there are two special cases. Quoting "go help packages":
-//
-// First, /... at the end of the pattern can match an empty string,
-// so that net/... matches both net and packages in its subdirectories, like net/http.
-// Second, any slash-separted pattern element containing a wildcard never
-// participates in a match of the "vendor" element in the path of a vendored
-// package, so that ./... does not match packages in subdirectories of
-// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
-// Note, however, that a directory named vendor that itself contains code
-// is not a vendored package: cmd/vendor would be a command named vendor,
-// and the pattern cmd/... matches it.
-func matchPattern(pattern string) func(name string) bool {
- // Convert pattern to regular expression.
- // The strategy for the trailing /... is to nest it in an explicit ? expression.
- // The strategy for the vendor exclusion is to change the unmatchable
- // vendor strings to a disallowed code point (vendorChar) and to use
- // "(anything but that codepoint)*" as the implementation of the ... wildcard.
- // This is a bit complicated but the obvious alternative,
- // namely a hand-written search like in most shell glob matchers,
- // is too easy to make accidentally exponential.
- // Using package regexp guarantees linear-time matching.
-
- const vendorChar = "\x00"
-
- if strings.Contains(pattern, vendorChar) {
- return func(name string) bool { return false }
- }
-
- re := regexp.QuoteMeta(pattern)
- re = replaceVendor(re, vendorChar)
- switch {
- case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
- re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
- case re == vendorChar+`/\.\.\.`:
- re = `(/vendor|/` + vendorChar + `/\.\.\.)`
- case strings.HasSuffix(re, `/\.\.\.`):
- re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
- }
- re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
-
- reg := regexp.MustCompile(`^` + re + `$`)
-
- return func(name string) bool {
- if strings.Contains(name, vendorChar) {
- return false
- }
- return reg.MatchString(replaceVendor(name, vendorChar))
- }
-}
+ "cmd/go/internal/search"
+)
// MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
func MatchPackage(pattern, cwd string) func(*Package) bool {
dir = filepath.Join(cwd, dir)
if pattern == "" {
return func(p *Package) bool {
+ // TODO(rsc): This is wrong. See golang.org/issue/25878.
if runtime.GOOS != "windows" {
return p.Dir == dir
}
return strings.EqualFold(p.Dir, dir)
}
}
- matchPath := matchPattern(pattern)
+ matchPath := search.MatchPattern(pattern)
return func(p *Package) bool {
// Compute relative path to dir and see if it matches the pattern.
rel, err := filepath.Rel(dir, p.Dir)
case pattern == "cmd":
return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") }
default:
- matchPath := matchPattern(pattern)
+ matchPath := search.MatchPattern(pattern)
return func(p *Package) bool { return matchPath(p.ImportPath) }
}
}
-
-// replaceVendor returns the result of replacing
-// non-trailing vendor path elements in x with repl.
-func replaceVendor(x, repl string) string {
- if !strings.Contains(x, "vendor") {
- return x
- }
- elem := strings.Split(x, "/")
- for i := 0; i < len(elem)-1; i++ {
- if elem[i] == "vendor" {
- elem[i] = repl
- }
- }
- return strings.Join(elem, "/")
-}
-
-// ImportPaths returns the import paths to use for the given command line.
-func ImportPaths(args []string) []string {
- args = ImportPathsNoDotExpansion(args)
- var out []string
- for _, a := range args {
- if strings.Contains(a, "...") {
- if build.IsLocalImport(a) {
- out = append(out, allPackagesInFS(a)...)
- } else {
- out = append(out, allPackages(a)...)
- }
- continue
- }
- out = append(out, a)
- }
- return out
-}
-
-// ImportPathsNoDotExpansion returns the import paths to use for the given
-// command line, but it does no ... expansion.
-func ImportPathsNoDotExpansion(args []string) []string {
- if cmdlineMatchers == nil {
- SetCmdlinePatterns(args)
- }
- if len(args) == 0 {
- return []string{"."}
- }
- var out []string
- for _, a := range args {
- // Arguments are supposed to be import paths, but
- // as a courtesy to Windows developers, rewrite \ to /
- // in command-line arguments. Handles .\... and so on.
- if filepath.Separator == '\\' {
- a = strings.Replace(a, `\`, `/`, -1)
- }
-
- // Put argument in canonical form, but preserve leading ./.
- if strings.HasPrefix(a, "./") {
- a = "./" + path.Clean(a)
- if a == "./." {
- a = "."
- }
- } else {
- a = path.Clean(a)
- }
- if IsMetaPackage(a) {
- out = append(out, allPackages(a)...)
- continue
- }
- out = append(out, a)
- }
- return out
-}
-
-// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
-func IsMetaPackage(name string) bool {
- return name == "std" || name == "cmd" || name == "all"
-}
--- /dev/null
+// 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 modconv
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+)
+
+func ParseGopkgLock(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ var r *module.Version
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ if i := strings.Index(line, "#"); i >= 0 {
+ line = line[:i]
+ }
+ line = strings.TrimSpace(line)
+ if line == "[[projects]]" {
+ list = append(list, module.Version{})
+ r = &list[len(list)-1]
+ continue
+ }
+ if strings.HasPrefix(line, "[") {
+ r = nil
+ continue
+ }
+ if r == nil {
+ continue
+ }
+ i := strings.Index(line, "=")
+ if i < 0 {
+ continue
+ }
+ key := strings.TrimSpace(line[:i])
+ val := strings.TrimSpace(line[i+1:])
+ if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
+ q, err := strconv.Unquote(val) // Go unquoting, but close enough for now
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err)
+ }
+ val = q
+ }
+ switch key {
+ case "name":
+ r.Path = val
+ case "revision", "version":
+ // Note: key "version" should take priority over "revision",
+ // and it does, because dep writes toml keys in alphabetical order,
+ // so we see version (if present) second.
+ if key == "version" {
+ if !semver.IsValid(val) || semver.Canonical(val) != val {
+ break
+ }
+ }
+ r.Version = val
+ }
+ }
+ for _, r := range list {
+ if r.Path == "" || r.Version == "" {
+ return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path)
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "cmd/go/internal/module"
+ "strings"
+)
+
+func ParseGlideLock(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ imports := false
+ name := ""
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ if line == "" {
+ continue
+ }
+ if strings.HasPrefix(line, "imports:") {
+ imports = true
+ } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' {
+ imports = false
+ }
+ if !imports {
+ continue
+ }
+ if strings.HasPrefix(line, "- name:") {
+ name = strings.TrimSpace(line[len("- name:"):])
+ }
+ if strings.HasPrefix(line, " version:") {
+ version := strings.TrimSpace(line[len(" version:"):])
+ if name != "" && version != "" {
+ list = append(list, module.Version{Path: name, Version: version})
+ }
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "strings"
+
+ "cmd/go/internal/module"
+)
+
+func ParseGLOCKFILE(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ f := strings.Fields(line)
+ if len(f) >= 2 && f[0] != "cmd" {
+ list = append(list, module.Version{Path: f[0], Version: f[1]})
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "encoding/json"
+
+ "cmd/go/internal/module"
+)
+
+func ParseGodepsJSON(file string, data []byte) ([]module.Version, error) {
+ var cfg struct {
+ ImportPath string
+ Deps []struct {
+ ImportPath string
+ Rev string
+ }
+ }
+ if err := json.Unmarshal(data, &cfg); err != nil {
+ return nil, err
+ }
+ var list []module.Version
+ for _, d := range cfg.Deps {
+ list = append(list, module.Version{Path: d.ImportPath, Version: d.Rev})
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import "cmd/go/internal/module"
+
+var Converters = map[string]func(string, []byte) ([]module.Version, error){
+ "GLOCKFILE": ParseGLOCKFILE,
+ "Godeps/Godeps.json": ParseGodepsJSON,
+ "Gopkg.lock": ParseGopkgLock,
+ "dependencies.tsv": ParseDependenciesTSV,
+ "glide.lock": ParseGlideLock,
+ "vendor.conf": ParseVendorConf,
+ "vendor.yml": ParseVendorYML,
+ "vendor/manifest": ParseVendorManifest,
+ "vendor/vendor.json": ParseVendorJSON,
+}
+
+// Prefix is a line we write at the top of auto-converted go.mod files
+// for dependencies before caching them.
+// In case of bugs in the converter, if we bump this version number,
+// then all the cached copies will be ignored.
+const Prefix = "//vgo 0.0.4\n"
--- /dev/null
+// 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 modconv
+
+import (
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "path/filepath"
+ "testing"
+)
+
+var extMap = map[string]string{
+ ".dep": "Gopkg.lock",
+ ".glide": "glide.lock",
+ ".glock": "GLOCKFILE",
+ ".godeps": "Godeps/Godeps.json",
+ ".tsv": "dependencies.tsv",
+ ".vconf": "vendor.conf",
+ ".vjson": "vendor/vendor.json",
+ ".vyml": "vendor.yml",
+ ".vmanifest": "vendor/manifest",
+}
+
+func Test(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tests, _ := filepath.Glob("testdata/*")
+ if len(tests) == 0 {
+ t.Fatalf("no tests found")
+ }
+ for _, test := range tests {
+ file := filepath.Base(test)
+ ext := filepath.Ext(file)
+ if ext == ".out" {
+ continue
+ }
+ t.Run(file, func(t *testing.T) {
+ if extMap[ext] == "" {
+ t.Fatal("unknown extension")
+ }
+ if Converters[extMap[ext]] == nil {
+ t.Fatalf("Converters[%q] == nil", extMap[ext])
+ }
+ data, err := ioutil.ReadFile(test)
+ if err != nil {
+ t.Fatal(err)
+ }
+ out, err := Converters[extMap[ext]](test, data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want, err := ioutil.ReadFile(test[:len(test)-len(ext)] + ".out")
+ if err != nil {
+ t.Error(err)
+ }
+ var buf bytes.Buffer
+ for _, r := range out {
+ fmt.Fprintf(&buf, "%s %s\n", r.Path, r.Version)
+ }
+ if !bytes.Equal(buf.Bytes(), want) {
+ t.Errorf("have:\n%s\nwant:\n%s", buf.Bytes(), want)
+ }
+ })
+ }
+}
--- /dev/null
+cmd github.com/cockroachdb/c-protobuf/cmd/protoc
+cmd github.com/cockroachdb/yacc
+cmd github.com/gogo/protobuf/protoc-gen-gogo
+cmd github.com/golang/lint/golint
+cmd github.com/jteeuwen/go-bindata/go-bindata
+cmd github.com/kisielk/errcheck
+cmd github.com/robfig/glock
+cmd github.com/tebeka/go2xunit
+cmd golang.org/x/tools/cmd/goimports
+cmd golang.org/x/tools/cmd/stringer
+github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671
+github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128
+github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809
+github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3
+github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54
+github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5
+github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514
+github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc
+github.com/docker/docker 7374852be9def787921aea2ca831771982badecf
+github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9
+github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813
+github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4
+github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046
+github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7
+github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259
+github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be
+github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45
+github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529
+github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced
+github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9
+github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7
+github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804
+github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085
+github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40
+golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e
+golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09
+golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5
+golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6
+gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de
--- /dev/null
+github.com/agtorre/gocolorize f42b554bf7f006936130c9bb4f971afd2d87f671
+github.com/biogo/store e1f74b3c58befe661feed7fa4cf52436de753128
+github.com/cockroachdb/c-lz4 6e71f140a365017bbe0904710007f8725fd3f809
+github.com/cockroachdb/c-protobuf 0f9ab7b988ca7474cf76b9a961ab03c0552abcb3
+github.com/cockroachdb/c-rocksdb 7fc876fe79b96de0e25069c9ae27e6444637bd54
+github.com/cockroachdb/c-snappy 618733f9e5bab8463b9049117a335a7a1bfc9fd5
+github.com/cockroachdb/yacc 572e006f8e6b0061ebda949d13744f5108389514
+github.com/coreos/etcd 18ecc297bc913bed6fc093d66b1fa22020dba7dc
+github.com/docker/docker 7374852be9def787921aea2ca831771982badecf
+github.com/elazarl/go-bindata-assetfs 3dcc96556217539f50599357fb481ac0dc7439b9
+github.com/gogo/protobuf 98e73e511a62a9c232152f94999112c80142a813
+github.com/golang/lint 7b7f4364ff76043e6c3610281525fabc0d90f0e4
+github.com/google/btree cc6329d4279e3f025a53a83c397d2339b5705c45
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/jteeuwen/go-bindata dce55d09e24ac40a6e725c8420902b86554f8046
+github.com/julienschmidt/httprouter 6aacfd5ab513e34f7e64ea9627ab9670371b34e7
+github.com/kisielk/errcheck 50b84cf7fa18ee2985b8c63ba3de5edd604b9259
+github.com/kisielk/gotool d678387370a2eb9b5b0a33218bc8c9d8de15b6be
+github.com/lib/pq a8d8d01c4f91602f876bf5aa210274e8203a6b45
+github.com/montanaflynn/stats 44fb56da2a2a67d394dec0e18a82dd316f192529
+github.com/peterh/liner 1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced
+github.com/robfig/glock cb3c3ec56de988289cab7bbd284eddc04dfee6c9
+github.com/samalba/dockerclient 12570e600d71374233e5056ba315f657ced496c7
+github.com/spf13/cobra 66816bcd0378e248c613e3c443c020f544c28804
+github.com/spf13/pflag 67cbc198fd11dab704b214c1e629a97af392c085
+github.com/tebeka/go2xunit d45000af2242dd0e7b8c7b07d82a1068adc5fd40
+golang.org/x/crypto cc04154d65fb9296747569b107cfd05380b1ea3e
+golang.org/x/net 8bfde94a845cb31000de3266ac83edbda58dab09
+golang.org/x/text d4cc1b1e16b49d6dafc4982403b40fe89c512cd5
+golang.org/x/tools d02228d1857b9f49cd0252788516ff5584266eb6
+gopkg.in/yaml.v1 9f9df34309c04878acc86042b16630b0f696e1de
--- /dev/null
+{
+ "ImportPath": "github.com/docker/machine",
+ "GoVersion": "go1.4.2",
+ "Deps": [
+ {
+ "ImportPath": "code.google.com/p/goauth2/oauth",
+ "Comment": "weekly-56",
+ "Rev": "afe77d958c701557ec5dc56f6936fcc194d15520"
+ },
+ {
+ "ImportPath": "github.com/MSOpenTech/azure-sdk-for-go",
+ "Comment": "v1.1-17-g515f3ec",
+ "Rev": "515f3ec74ce6a5b31e934cefae997c97bd0a1b1e"
+ },
+ {
+ "ImportPath": "github.com/cenkalti/backoff",
+ "Rev": "9831e1e25c874e0a0601b6dc43641071414eec7a"
+ },
+ {
+ "ImportPath": "github.com/codegangsta/cli",
+ "Comment": "1.2.0-64-ge1712f3",
+ "Rev": "e1712f381785e32046927f64a7c86fe569203196"
+ },
+ {
+ "ImportPath": "github.com/digitalocean/godo",
+ "Comment": "v0.5.0",
+ "Rev": "5478aae80694de1d2d0e02c386bbedd201266234"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/dockerversion",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/engine",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/archive",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/fileutils",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/ioutils",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/mflag",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/parsers",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/pools",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/promise",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/system",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/term",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/timeutils",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/units",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/pkg/version",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar",
+ "Comment": "v1.5.0",
+ "Rev": "a8a31eff10544860d2188dddabdee4d727545796"
+ },
+ {
+ "ImportPath": "github.com/docker/libtrust",
+ "Rev": "c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41"
+ },
+ {
+ "ImportPath": "github.com/google/go-querystring/query",
+ "Rev": "30f7a39f4a218feb5325f3aebc60c32a572a8274"
+ },
+ {
+ "ImportPath": "github.com/mitchellh/mapstructure",
+ "Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"
+ },
+ {
+ "ImportPath": "github.com/rackspace/gophercloud",
+ "Comment": "v1.0.0-558-ce0f487",
+ "Rev": "ce0f487f6747ab43c4e4404722df25349385bebd"
+ },
+ {
+ "ImportPath": "github.com/skarademir/naturalsort",
+ "Rev": "983d4d86054d80f91fd04dd62ec52c1d078ce403"
+ },
+ {
+ "ImportPath": "github.com/smartystreets/go-aws-auth",
+ "Rev": "1f0db8c0ee6362470abe06a94e3385927ed72a4b"
+ },
+ {
+ "ImportPath": "github.com/stretchr/testify/assert",
+ "Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
+ },
+ {
+ "ImportPath": "github.com/pyr/egoscale/src/egoscale",
+ "Rev": "bbaa67324aeeacc90430c1fe0a9c620d3929512e"
+ },
+ {
+ "ImportPath": "github.com/tent/http-link-go",
+ "Rev": "ac974c61c2f990f4115b119354b5e0b47550e888"
+ },
+ {
+ "ImportPath": "github.com/vmware/govcloudair",
+ "Comment": "v0.0.2",
+ "Rev": "66a23eaabc61518f91769939ff541886fe1dceef"
+ },
+ {
+ "ImportPath": "golang.org/x/crypto/ssh",
+ "Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e"
+ },
+ {
+ "ImportPath": "google.golang.org/api/compute/v1",
+ "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5"
+ },
+ {
+ "ImportPath": "google.golang.org/api/googleapi",
+ "Rev": "aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5"
+ }
+ ]
+}
--- /dev/null
+code.google.com/p/goauth2/oauth afe77d958c701557ec5dc56f6936fcc194d15520
+github.com/MSOpenTech/azure-sdk-for-go 515f3ec74ce6a5b31e934cefae997c97bd0a1b1e
+github.com/cenkalti/backoff 9831e1e25c874e0a0601b6dc43641071414eec7a
+github.com/codegangsta/cli e1712f381785e32046927f64a7c86fe569203196
+github.com/digitalocean/godo 5478aae80694de1d2d0e02c386bbedd201266234
+github.com/docker/docker/dockerversion a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/engine a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/archive a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/fileutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/ioutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/mflag a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/parsers a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/pools a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/promise a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/system a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/term a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/timeutils a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/units a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/pkg/version a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar a8a31eff10544860d2188dddabdee4d727545796
+github.com/docker/libtrust c54fbb67c1f1e68d7d6f8d2ad7c9360404616a41
+github.com/google/go-querystring/query 30f7a39f4a218feb5325f3aebc60c32a572a8274
+github.com/mitchellh/mapstructure 740c764bc6149d3f1806231418adb9f52c11bcbf
+github.com/rackspace/gophercloud ce0f487f6747ab43c4e4404722df25349385bebd
+github.com/skarademir/naturalsort 983d4d86054d80f91fd04dd62ec52c1d078ce403
+github.com/smartystreets/go-aws-auth 1f0db8c0ee6362470abe06a94e3385927ed72a4b
+github.com/stretchr/testify/assert e4ec8152c15fc46bd5056ce65997a07c7d415325
+github.com/pyr/egoscale/src/egoscale bbaa67324aeeacc90430c1fe0a9c620d3929512e
+github.com/tent/http-link-go ac974c61c2f990f4115b119354b5e0b47550e888
+github.com/vmware/govcloudair 66a23eaabc61518f91769939ff541886fe1dceef
+golang.org/x/crypto/ssh 1fbbd62cfec66bd39d91e97749579579d4d3037e
+google.golang.org/api/compute/v1 aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5
+google.golang.org/api/googleapi aa91ac681e18e52b1a0dfe29b9d8354e88c0dcf5
--- /dev/null
+hash: ead3ea293a6143fe41069ebec814bf197d8c43a92cc7666b1f7e21a419b46feb
+updated: 2016-06-20T21:53:35.420817456Z
+imports:
+- name: github.com/BurntSushi/toml
+ version: f0aeabca5a127c4078abb8c8d64298b147264b55
+- name: github.com/cpuguy83/go-md2man
+ version: a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
+ subpackages:
+ - md2man
+- name: github.com/fsnotify/fsnotify
+ version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+- name: github.com/hashicorp/hcl
+ version: da486364306ed66c218be9b7953e19173447c18b
+ subpackages:
+ - hcl/ast
+ - hcl/parser
+ - hcl/token
+ - json/parser
+ - hcl/scanner
+ - hcl/strconv
+ - json/scanner
+ - json/token
+- name: github.com/inconshreveable/mousetrap
+ version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+- name: github.com/magiconair/properties
+ version: c265cfa48dda6474e208715ca93e987829f572f8
+- name: github.com/mitchellh/mapstructure
+ version: d2dd0262208475919e1a362f675cfc0e7c10e905
+- name: github.com/russross/blackfriday
+ version: 1d6b8e9301e720b08a8938b8c25c018285885438
+- name: github.com/shurcooL/sanitized_anchor_name
+ version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
+- name: github.com/spf13/cast
+ version: 27b586b42e29bec072fe7379259cc719e1289da6
+- name: github.com/spf13/jwalterweatherman
+ version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
+- name: github.com/spf13/pflag
+ version: dabebe21bf790f782ea4c7bbd2efc430de182afd
+- name: github.com/spf13/viper
+ version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
+- name: golang.org/x/sys
+ version: 62bee037599929a6e9146f29d10dd5208c43507d
+ subpackages:
+ - unix
+- name: gopkg.in/yaml.v2
+ version: a83829b6f1293c91addabc89d0571c246397bbf4
+- name: github.com/spf13/cobra
+ repo: https://github.com/dnephin/cobra
+ subpackages:
+ - doc
+ version: v1.3
+devImports: []
--- /dev/null
+github.com/BurntSushi/toml f0aeabca5a127c4078abb8c8d64298b147264b55
+github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
+github.com/fsnotify/fsnotify 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+github.com/hashicorp/hcl da486364306ed66c218be9b7953e19173447c18b
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/magiconair/properties c265cfa48dda6474e208715ca93e987829f572f8
+github.com/mitchellh/mapstructure d2dd0262208475919e1a362f675cfc0e7c10e905
+github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
+github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
+github.com/spf13/cast 27b586b42e29bec072fe7379259cc719e1289da6
+github.com/spf13/jwalterweatherman 33c24e77fb80341fe7130ee7c594256ff08ccc46
+github.com/spf13/pflag dabebe21bf790f782ea4c7bbd2efc430de182afd
+github.com/spf13/viper c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
+golang.org/x/sys 62bee037599929a6e9146f29d10dd5208c43507d
+gopkg.in/yaml.v2 a83829b6f1293c91addabc89d0571c246397bbf4
+github.com/spf13/cobra v1.3
--- /dev/null
+github.com/davecgh/go-xdr/xdr2 4930550ba2e22f87187498acfd78348b15f4e7a8
+github.com/google/uuid 6a5e28554805e78ea6141142aba763936c4761c0
+github.com/kr/pretty 2ee9d7453c02ef7fa518a83ae23644eb8872186a
+github.com/kr/pty 95d05c1eef33a45bd58676b6ce28d105839b8d0b
+github.com/vmware/vmw-guestinfo 25eff159a728be87e103a0b8045e08273f4dbec4
--- /dev/null
+{
+ "version": 0,
+ "dependencies": [
+ {
+ "importpath": "github.com/davecgh/go-xdr/xdr2",
+ "repository": "https://github.com/rasky/go-xdr",
+ "vcs": "git",
+ "revision": "4930550ba2e22f87187498acfd78348b15f4e7a8",
+ "branch": "improvements",
+ "path": "/xdr2",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/google/uuid",
+ "repository": "https://github.com/google/uuid",
+ "vcs": "git",
+ "revision": "6a5e28554805e78ea6141142aba763936c4761c0",
+ "branch": "master",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/kr/pretty",
+ "repository": "https://github.com/dougm/pretty",
+ "vcs": "git",
+ "revision": "2ee9d7453c02ef7fa518a83ae23644eb8872186a",
+ "branch": "govmomi",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/kr/pty",
+ "repository": "https://github.com/kr/pty",
+ "vcs": "git",
+ "revision": "95d05c1eef33a45bd58676b6ce28d105839b8d0b",
+ "branch": "master",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/vmware/vmw-guestinfo",
+ "repository": "https://github.com/vmware/vmw-guestinfo",
+ "vcs": "git",
+ "revision": "25eff159a728be87e103a0b8045e08273f4dbec4",
+ "branch": "master",
+ "notests": true
+ }
+ ]
+}
--- /dev/null
+github.com/Azure/azure-sdk-for-go 902d95d9f311ae585ee98cfd18f418b467d60d5a
+github.com/Azure/go-autorest 6f40a8acfe03270d792cb8155e2942c09d7cff95
+github.com/ajstarks/svgo 89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518
+github.com/altoros/gosigma 31228935eec685587914528585da4eb9b073c76d
+github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
+github.com/bmizerany/pat c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c
+github.com/coreos/go-systemd 7b2428fec40033549c68f54e26e89e7ca9a9ce31
+github.com/dgrijalva/jwt-go 01aeca54ebda6e0fbfafd0a524d234159c05ec20
+github.com/dustin/go-humanize 145fabdb1ab757076a70a886d092a3af27f66f4c
+github.com/godbus/dbus 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15
+github.com/golang/protobuf 4bd1920723d7b7c925de087aa32e2187708897f7
+github.com/google/go-querystring 9235644dd9e52eeae6fa48efd539fdc351a0af53
+github.com/gorilla/schema 08023a0215e7fc27a9aecd8b8c50913c40019478
+github.com/gorilla/websocket 804cb600d06b10672f2fbc0a336a7bee507a428e
+github.com/gosuri/uitable 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
+github.com/joyent/gocommon ade826b8b54e81a779ccb29d358a45ba24b7809c
+github.com/joyent/gosdc 2f11feadd2d9891e92296a1077c3e2e56939547d
+github.com/joyent/gosign 0da0d5f1342065321c97812b1f4ac0c2b0bab56c
+github.com/juju/ansiterm b99631de12cf04a906c1d4e4ec54fb86eae5863d
+github.com/juju/blobstore 06056004b3d7b54bbb7984d830c537bad00fec21
+github.com/juju/bundlechanges 7725027b95e0d54635e0fb11efc2debdcdf19f75
+github.com/juju/cmd 9425a576247f348b9b40afe3b60085de63470de5
+github.com/juju/description d3742c23561884cd7d759ef7142340af1d22cab0
+github.com/juju/errors 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3
+github.com/juju/gnuflag 4e76c56581859c14d9d87e1ddbe29e1c0f10195f
+github.com/juju/go4 40d72ab9641a2a8c36a9c46a51e28367115c8e59
+github.com/juju/gojsonpointer afe8b77aa08f272b49e01b82de78510c11f61500
+github.com/juju/gojsonreference f0d24ac5ee330baa21721cdff56d45e4ee42628e
+github.com/juju/gojsonschema e1ad140384f254c82f89450d9a7c8dd38a632838
+github.com/juju/gomaasapi cfbc096bd45f276c17a391efc4db710b60ae3ad7
+github.com/juju/httpprof 14bf14c307672fd2456bdbf35d19cf0ccd3cf565
+github.com/juju/httprequest 266fd1e9debf09c037a63f074d099a2da4559ece
+github.com/juju/idmclient 4dc25171f675da4206b71695d3fd80e519ad05c1
+github.com/juju/jsonschema a0ef8b74ebcffeeff9fc374854deb4af388f037e
+github.com/juju/loggo 21bc4c63e8b435779a080e39e592969b7b90b889
+github.com/juju/mempool 24974d6c264fe5a29716e7d56ea24c4bd904b7cc
+github.com/juju/mutex 59c26ee163447c5c57f63ff71610d433862013de
+github.com/juju/persistent-cookiejar 5243747bf8f2d0897f6c7a52799327dc97d585e8
+github.com/juju/pubsub 9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4
+github.com/juju/replicaset 6b5becf2232ce76656ea765d8d915d41755a1513
+github.com/juju/retry 62c62032529169c7ec02fa48f93349604c345e1f
+github.com/juju/rfc ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee
+github.com/juju/romulus 98d6700423d63971f10ca14afea9ecf2b9b99f0f
+github.com/juju/schema 075de04f9b7d7580d60a1e12a0b3f50bb18e6998
+github.com/juju/terms-client 9b925afd677234e4146dde3cb1a11e187cbed64e
+github.com/juju/testing fce9bc4ebf7a77310c262ac4884e03b778eae06a
+github.com/juju/txn 28898197906200d603394d8e4ce537436529f1c5
+github.com/juju/usso 68a59c96c178fbbad65926e7f93db50a2cd14f33
+github.com/juju/utils 9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26
+github.com/juju/version 1f41e27e54f21acccf9b2dddae063a782a8a7ceb
+github.com/juju/webbrowser 54b8c57083b4afb7dc75da7f13e2967b2606a507
+github.com/juju/xml eb759a627588d35166bc505fceb51b88500e291e
+github.com/juju/zip f6b1e93fa2e29a1d7d49b566b2b51efb060c982a
+github.com/julienschmidt/httprouter 77a895ad01ebc98a4dc95d8355bc825ce80a56f6
+github.com/lestrrat/go-jspointer f4881e611bdbe9fb413a7780721ef8400a1f2341
+github.com/lestrrat/go-jsref e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1
+github.com/lestrrat/go-jsschema b09d7650b822d2ea3dc83d5091a5e2acd8330051
+github.com/lestrrat/go-jsval b1258a10419fe0693f7b35ad65cd5074bc0ba1e5
+github.com/lestrrat/go-pdebug 2e6eaaa5717f81bda41d27070d3c966f40a1e75f
+github.com/lestrrat/go-structinfo f74c056fe41f860aa6264478c664a6fff8a64298
+github.com/lunixbochs/vtclean 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36
+github.com/lxc/lxd 23da0234979fa6299565b91b529a6dbeb42ee36d
+github.com/masterzen/azure-sdk-for-go ee4f0065d00cd12b542f18f5bc45799e88163b12
+github.com/masterzen/simplexml 4572e39b1ab9fe03ee513ce6fc7e289e98482190
+github.com/masterzen/winrm 7a535cd943fccaeed196718896beec3fb51aff41
+github.com/masterzen/xmlpath 13f4951698adc0fa9c1dda3e275d489a24201161
+github.com/mattn/go-colorable ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8
+github.com/mattn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
+github.com/mattn/go-runewidth d96d1bd051f2bd9e7e43d602782b37b93b1b5666
+github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c
+github.com/nu7hatch/gouuid 179d4d0c4d8d407a32af483c2354df1d2c91e6c3
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/prometheus/client_golang 575f371f7862609249a1be4c9145f429fe065e32
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common dd586c1c5abb0be59e60f942c22af711a2008cb4
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/rogpeppe/fastuuid 6724a57986aff9bff1a1770e9347036def7c89f6
+github.com/vmware/govmomi c0c7ce63df7edd78e713257b924c89d9a2dac119
+golang.org/x/crypto 8e06e8ddd9629eb88639aba897641bff8031f1d3
+golang.org/x/net ea47fc708ee3e20177f3ca3716217c4ab75942cb
+golang.org/x/oauth2 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a
+golang.org/x/sys 7a6e5648d140666db5d920909e082ca00a87ba2c
+golang.org/x/text 2910a502d2bf9e43193af9d68ca516529614eed3
+google.golang.org/api 0d3983fb069cb6651353fc44c5cb604e263f2a93
+google.golang.org/cloud f20d6dcccb44ed49de45ae3703312cb46e627db1
+gopkg.in/amz.v3 8c3190dff075bf5442c9eedbf8f8ed6144a099e7
+gopkg.in/check.v1 4f90aeace3a26ad7021961c297b22c42160c7b25
+gopkg.in/errgo.v1 442357a80af5c6bf9b6d51ae791a39c3421004f3
+gopkg.in/goose.v1 ac43167b647feacdd9a1e34ee81e574551bc748d
+gopkg.in/ini.v1 776aa739ce9373377cd16f526cdf06cb4c89b40f
+gopkg.in/juju/blobstore.v2 51fa6e26128d74e445c72d3a91af555151cc3654
+gopkg.in/juju/charm.v6-unstable 83771c4919d6810bce5b7e63f46bea5fbfed0b93
+gopkg.in/juju/charmrepo.v2-unstable e79aa298df89ea887c9bffec46063c24bfb730f7
+gopkg.in/juju/charmstore.v5-unstable fd1eef3002fc6b6daff5e97efab6f5056d22dcc7
+gopkg.in/juju/environschema.v1 7359fc7857abe2b11b5b3e23811a9c64cb6b01e0
+gopkg.in/juju/jujusvg.v2 d82160011935ef79fc7aca84aba2c6f74700fe75
+gopkg.in/juju/names.v2 0847c26d322a121e52614f969fb82eae2820c715
+gopkg.in/juju/worker.v1 6965b9d826717287bb002e02d1fd4d079978083e
+gopkg.in/macaroon-bakery.v1 469b44e6f1f9479e115c8ae879ef80695be624d5
+gopkg.in/macaroon.v1 ab3940c6c16510a850e1c2dd628b919f0f3f1464
+gopkg.in/mgo.v2 f2b6f6c918c452ad107eec89615f074e3bd80e33
+gopkg.in/natefinch/lumberjack.v2 514cbda263a734ae8caac038dadf05f8f3f9f738
+gopkg.in/natefinch/npipe.v2 c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6
+gopkg.in/retry.v1 c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db
+gopkg.in/tomb.v1 dd632973f1e7218eb1089048e0798ec9ae7dceb8
+gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a
--- /dev/null
+github.com/Azure/azure-sdk-for-go git 902d95d9f311ae585ee98cfd18f418b467d60d5a 2016-07-20T05:16:58Z
+github.com/Azure/go-autorest git 6f40a8acfe03270d792cb8155e2942c09d7cff95 2016-07-19T23:14:56Z
+github.com/ajstarks/svgo git 89e3ac64b5b3e403a5e7c35ea4f98d45db7b4518 2014-10-04T21:11:59Z
+github.com/altoros/gosigma git 31228935eec685587914528585da4eb9b073c76d 2015-04-08T14:52:32Z
+github.com/beorn7/perks git 3ac7bf7a47d159a033b107610db8a1b6575507a4 2016-02-29T21:34:45Z
+github.com/bmizerany/pat git c068ca2f0aacee5ac3681d68e4d0a003b7d1fd2c 2016-02-17T10:32:42Z
+github.com/coreos/go-systemd git 7b2428fec40033549c68f54e26e89e7ca9a9ce31 2016-02-02T21:14:25Z
+github.com/dgrijalva/jwt-go git 01aeca54ebda6e0fbfafd0a524d234159c05ec20 2016-07-05T20:30:06Z
+github.com/dustin/go-humanize git 145fabdb1ab757076a70a886d092a3af27f66f4c 2014-12-28T07:11:48Z
+github.com/godbus/dbus git 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15 2016-05-06T22:25:50Z
+github.com/golang/protobuf git 4bd1920723d7b7c925de087aa32e2187708897f7 2016-11-09T07:27:36Z
+github.com/google/go-querystring git 9235644dd9e52eeae6fa48efd539fdc351a0af53 2016-04-01T23:30:42Z
+github.com/gorilla/schema git 08023a0215e7fc27a9aecd8b8c50913c40019478 2016-04-26T23:15:12Z
+github.com/gorilla/websocket git 804cb600d06b10672f2fbc0a336a7bee507a428e 2017-02-14T17:41:18Z
+github.com/gosuri/uitable git 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 2016-04-04T20:39:58Z
+github.com/joyent/gocommon git ade826b8b54e81a779ccb29d358a45ba24b7809c 2016-03-20T19:31:33Z
+github.com/joyent/gosdc git 2f11feadd2d9891e92296a1077c3e2e56939547d 2014-05-24T00:08:15Z
+github.com/joyent/gosign git 0da0d5f1342065321c97812b1f4ac0c2b0bab56c 2014-05-24T00:07:34Z
+github.com/juju/ansiterm git b99631de12cf04a906c1d4e4ec54fb86eae5863d 2016-09-07T23:45:32Z
+github.com/juju/blobstore git 06056004b3d7b54bbb7984d830c537bad00fec21 2015-07-29T11:18:58Z
+github.com/juju/bundlechanges git 7725027b95e0d54635e0fb11efc2debdcdf19f75 2016-12-15T16:06:52Z
+github.com/juju/cmd git 9425a576247f348b9b40afe3b60085de63470de5 2017-03-20T01:37:09Z
+github.com/juju/description git d3742c23561884cd7d759ef7142340af1d22cab0 2017-03-20T07:46:40Z
+github.com/juju/errors git 1b5e39b83d1835fa480e0c2ddefb040ee82d58b3 2015-09-16T12:56:42Z
+github.com/juju/gnuflag git 4e76c56581859c14d9d87e1ddbe29e1c0f10195f 2016-08-09T16:52:14Z
+github.com/juju/go4 git 40d72ab9641a2a8c36a9c46a51e28367115c8e59 2016-02-22T16:32:58Z
+github.com/juju/gojsonpointer git afe8b77aa08f272b49e01b82de78510c11f61500 2015-02-04T19:46:29Z
+github.com/juju/gojsonreference git f0d24ac5ee330baa21721cdff56d45e4ee42628e 2015-02-04T19:46:33Z
+github.com/juju/gojsonschema git e1ad140384f254c82f89450d9a7c8dd38a632838 2015-03-12T17:00:16Z
+github.com/juju/gomaasapi git cfbc096bd45f276c17a391efc4db710b60ae3ad7 2017-02-27T07:51:07Z
+github.com/juju/httpprof git 14bf14c307672fd2456bdbf35d19cf0ccd3cf565 2014-12-17T16:00:36Z
+github.com/juju/httprequest git 266fd1e9debf09c037a63f074d099a2da4559ece 2016-10-06T15:09:09Z
+github.com/juju/idmclient git 4dc25171f675da4206b71695d3fd80e519ad05c1 2017-02-09T16:27:49Z
+github.com/juju/jsonschema git a0ef8b74ebcffeeff9fc374854deb4af388f037e 2016-11-02T18:19:19Z
+github.com/juju/loggo git 21bc4c63e8b435779a080e39e592969b7b90b889 2017-02-22T12:20:47Z
+github.com/juju/mempool git 24974d6c264fe5a29716e7d56ea24c4bd904b7cc 2016-02-05T10:49:27Z
+github.com/juju/mutex git 59c26ee163447c5c57f63ff71610d433862013de 2016-06-17T01:09:07Z
+github.com/juju/persistent-cookiejar git 5243747bf8f2d0897f6c7a52799327dc97d585e8 2016-11-15T13:33:28Z
+github.com/juju/pubsub git 9dcaca7eb4340dbf685aa7b3ad4cc4f8691a33d4 2016-07-28T03:00:34Z
+github.com/juju/replicaset git 6b5becf2232ce76656ea765d8d915d41755a1513 2016-11-25T16:08:49Z
+github.com/juju/retry git 62c62032529169c7ec02fa48f93349604c345e1f 2015-10-29T02:48:21Z
+github.com/juju/rfc git ebdbbdb950cd039a531d15cdc2ac2cbd94f068ee 2016-07-11T02:42:13Z
+github.com/juju/romulus git 98d6700423d63971f10ca14afea9ecf2b9b99f0f 2017-01-23T14:29:29Z
+github.com/juju/schema git 075de04f9b7d7580d60a1e12a0b3f50bb18e6998 2016-04-20T04:42:03Z
+github.com/juju/terms-client git 9b925afd677234e4146dde3cb1a11e187cbed64e 2016-08-09T13:19:00Z
+github.com/juju/testing git fce9bc4ebf7a77310c262ac4884e03b778eae06a 2017-02-22T09:01:19Z
+github.com/juju/txn git 28898197906200d603394d8e4ce537436529f1c5 2016-11-16T04:07:55Z
+github.com/juju/usso git 68a59c96c178fbbad65926e7f93db50a2cd14f33 2016-04-01T10:44:24Z
+github.com/juju/utils git 9f8aeb9b09e2d8c769be8317ccfa23f7eec62c26 2017-02-15T08:19:00Z
+github.com/juju/version git 1f41e27e54f21acccf9b2dddae063a782a8a7ceb 2016-10-31T05:19:06Z
+github.com/juju/webbrowser git 54b8c57083b4afb7dc75da7f13e2967b2606a507 2016-03-09T14:36:29Z
+github.com/juju/xml git eb759a627588d35166bc505fceb51b88500e291e 2015-04-13T13:11:21Z
+github.com/juju/zip git f6b1e93fa2e29a1d7d49b566b2b51efb060c982a 2016-02-05T10:52:21Z
+github.com/julienschmidt/httprouter git 77a895ad01ebc98a4dc95d8355bc825ce80a56f6 2015-10-13T22:55:20Z
+github.com/lestrrat/go-jspointer git f4881e611bdbe9fb413a7780721ef8400a1f2341 2016-02-29T02:13:54Z
+github.com/lestrrat/go-jsref git e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1 2016-06-01T01:32:40Z
+github.com/lestrrat/go-jsschema git b09d7650b822d2ea3dc83d5091a5e2acd8330051 2016-09-03T13:19:57Z
+github.com/lestrrat/go-jsval git b1258a10419fe0693f7b35ad65cd5074bc0ba1e5 2016-10-12T04:57:17Z
+github.com/lestrrat/go-pdebug git 2e6eaaa5717f81bda41d27070d3c966f40a1e75f 2016-08-17T06:33:33Z
+github.com/lestrrat/go-structinfo git f74c056fe41f860aa6264478c664a6fff8a64298 2016-03-08T13:11:05Z
+github.com/lunixbochs/vtclean git 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36 2016-01-25T03:51:06Z
+github.com/lxc/lxd git 23da0234979fa6299565b91b529a6dbeb42ee36d 2017-02-16T05:29:42Z
+github.com/masterzen/azure-sdk-for-go git ee4f0065d00cd12b542f18f5bc45799e88163b12 2016-10-14T13:56:28Z
+github.com/masterzen/simplexml git 4572e39b1ab9fe03ee513ce6fc7e289e98482190 2016-06-08T18:30:07Z
+github.com/masterzen/winrm git 7a535cd943fccaeed196718896beec3fb51aff41 2016-10-14T15:10:40Z
+github.com/masterzen/xmlpath git 13f4951698adc0fa9c1dda3e275d489a24201161 2014-02-18T18:59:01Z
+github.com/mattn/go-colorable git ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8 2016-07-31T23:54:17Z
+github.com/mattn/go-isatty git 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 2016-08-06T12:27:52Z
+github.com/mattn/go-runewidth git d96d1bd051f2bd9e7e43d602782b37b93b1b5666 2015-11-18T07:21:59Z
+github.com/matttproud/golang_protobuf_extensions git c12348ce28de40eed0136aa2b644d0ee0650e56c 2016-04-24T11:30:07Z
+github.com/nu7hatch/gouuid git 179d4d0c4d8d407a32af483c2354df1d2c91e6c3 2013-12-21T20:05:32Z
+github.com/pkg/errors git 839d9e913e063e28dfd0e6c7b7512793e0a48be9 2016-10-02T05:25:12Z
+github.com/prometheus/client_golang git 575f371f7862609249a1be4c9145f429fe065e32 2016-11-24T15:57:32Z
+github.com/prometheus/client_model git fa8ad6fec33561be4280a8f0514318c79d7f6cb6 2015-02-12T10:17:44Z
+github.com/prometheus/common git dd586c1c5abb0be59e60f942c22af711a2008cb4 2016-05-03T22:05:32Z
+github.com/prometheus/procfs git abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 2016-04-11T19:08:41Z
+github.com/rogpeppe/fastuuid git 6724a57986aff9bff1a1770e9347036def7c89f6 2015-01-06T09:32:20Z
+github.com/vmware/govmomi git c0c7ce63df7edd78e713257b924c89d9a2dac119 2016-06-30T15:37:42Z
+golang.org/x/crypto git 8e06e8ddd9629eb88639aba897641bff8031f1d3 2016-09-22T17:06:29Z
+golang.org/x/net git ea47fc708ee3e20177f3ca3716217c4ab75942cb 2015-08-29T23:03:18Z
+golang.org/x/oauth2 git 11c60b6f71a6ad48ed6f93c65fa4c6f9b1b5b46a 2015-03-25T02:00:22Z
+golang.org/x/sys git 7a6e5648d140666db5d920909e082ca00a87ba2c 2017-02-01T05:12:45Z
+golang.org/x/text git 2910a502d2bf9e43193af9d68ca516529614eed3 2016-07-26T16:48:57Z
+google.golang.org/api git 0d3983fb069cb6651353fc44c5cb604e263f2a93 2014-12-10T23:51:26Z
+google.golang.org/cloud git f20d6dcccb44ed49de45ae3703312cb46e627db1 2015-03-19T22:36:35Z
+gopkg.in/amz.v3 git 8c3190dff075bf5442c9eedbf8f8ed6144a099e7 2016-12-15T13:08:49Z
+gopkg.in/check.v1 git 4f90aeace3a26ad7021961c297b22c42160c7b25 2016-01-05T16:49:36Z
+gopkg.in/errgo.v1 git 442357a80af5c6bf9b6d51ae791a39c3421004f3 2016-12-22T12:58:16Z
+gopkg.in/goose.v1 git ac43167b647feacdd9a1e34ee81e574551bc748d 2017-02-15T01:56:23Z
+gopkg.in/ini.v1 git 776aa739ce9373377cd16f526cdf06cb4c89b40f 2016-02-22T23:24:41Z
+gopkg.in/juju/blobstore.v2 git 51fa6e26128d74e445c72d3a91af555151cc3654 2016-01-25T02:37:03Z
+gopkg.in/juju/charm.v6-unstable git 83771c4919d6810bce5b7e63f46bea5fbfed0b93 2016-10-03T20:31:18Z
+gopkg.in/juju/charmrepo.v2-unstable git e79aa298df89ea887c9bffec46063c24bfb730f7 2016-11-17T15:25:28Z
+gopkg.in/juju/charmstore.v5-unstable git fd1eef3002fc6b6daff5e97efab6f5056d22dcc7 2016-09-16T10:09:07Z
+gopkg.in/juju/environschema.v1 git 7359fc7857abe2b11b5b3e23811a9c64cb6b01e0 2015-11-04T11:58:10Z
+gopkg.in/juju/jujusvg.v2 git d82160011935ef79fc7aca84aba2c6f74700fe75 2016-06-09T10:52:15Z
+gopkg.in/juju/names.v2 git 0847c26d322a121e52614f969fb82eae2820c715 2016-11-02T13:43:03Z
+gopkg.in/juju/worker.v1 git 6965b9d826717287bb002e02d1fd4d079978083e 2017-03-08T00:24:58Z
+gopkg.in/macaroon-bakery.v1 git 469b44e6f1f9479e115c8ae879ef80695be624d5 2016-06-22T12:14:21Z
+gopkg.in/macaroon.v1 git ab3940c6c16510a850e1c2dd628b919f0f3f1464 2015-01-21T11:42:31Z
+gopkg.in/mgo.v2 git f2b6f6c918c452ad107eec89615f074e3bd80e33 2016-08-18T01:52:18Z
+gopkg.in/natefinch/lumberjack.v2 git 514cbda263a734ae8caac038dadf05f8f3f9f738 2016-01-25T11:17:49Z
+gopkg.in/natefinch/npipe.v2 git c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6 2016-06-21T03:49:01Z
+gopkg.in/retry.v1 git c09f6b86ba4d5d2cf5bdf0665364aec9fd4815db 2016-10-25T18:14:30Z
+gopkg.in/tomb.v1 git dd632973f1e7218eb1089048e0798ec9ae7dceb8 2014-10-24T13:56:13Z
+gopkg.in/yaml.v2 git a3f3340b5840cee44f372bddb5880fcbc419b46a 2017-02-08T14:18:51Z
--- /dev/null
+github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
+github.com/Microsoft/hcsshim v0.6.5
+github.com/Microsoft/go-winio v0.4.5
+github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
+github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
+github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609
+github.com/gorilla/context v1.1
+github.com/gorilla/mux v1.1
+github.com/Microsoft/opengcs v0.3.4
+github.com/kr/pty 5cf931ef8f
+github.com/mattn/go-shellwords v1.0.3
+github.com/sirupsen/logrus v1.0.3
+github.com/tchap/go-patricia v2.2.6
+github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
+golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
+golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
+github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
+github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
+golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
+github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
+github.com/pmezard/go-difflib v1.0.0
+github.com/gotestyourself/gotestyourself v1.1.0
+github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
+github.com/imdario/mergo 0.2.1
+golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
+github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
+github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8
+github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
+github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce
+github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
+github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
+github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
+github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
+github.com/hashicorp/memberlist v0.1.0
+github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
+github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
+github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
+github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
+github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
+github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
+github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
+github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
+github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
+github.com/coreos/etcd v3.2.1
+github.com/coreos/go-semver v0.2.0
+github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
+github.com/hashicorp/consul v0.5.2
+github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
+github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
+github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
+github.com/vbatts/tar-split v0.10.1
+github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
+github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa
+github.com/pborman/uuid v1.0
+google.golang.org/grpc v1.3.0
+github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
+github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
+github.com/opencontainers/runtime-spec v1.0.0
+github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
+github.com/coreos/go-systemd v4
+github.com/godbus/dbus v4.0.0
+github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
+github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
+github.com/Graylog2/go-gelf v2
+github.com/fluent/fluent-logger-golang v1.2.1
+github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
+github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c
+github.com/fsnotify/fsnotify v1.4.2
+github.com/aws/aws-sdk-go v1.4.22
+github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0
+github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
+github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf
+golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0
+google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823
+cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525
+github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
+google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
+github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0
+github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
+github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0
+github.com/gogo/protobuf v0.4
+github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
+github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
+golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
+golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
+github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad
+github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990
+github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
+github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0
+github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e
+github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/matttproud/golang_protobuf_extensions v1.0.0
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
+github.com/spf13/cobra v1.5.1
+github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c
+github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
+github.com/opencontainers/selinux v1.0.0-rc1
--- /dev/null
+# the following lines are in sorted order, FYI
+github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
+github.com/Microsoft/hcsshim v0.6.5
+github.com/Microsoft/go-winio v0.4.5
+github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
+github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
+github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
+github.com/gorilla/context v1.1
+github.com/gorilla/mux v1.1
+github.com/Microsoft/opengcs v0.3.4
+github.com/kr/pty 5cf931ef8f
+github.com/mattn/go-shellwords v1.0.3
+github.com/sirupsen/logrus v1.0.3
+github.com/tchap/go-patricia v2.2.6
+github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
+golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
+golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
+github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
+github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
+golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
+github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
+github.com/pmezard/go-difflib v1.0.0
+github.com/gotestyourself/gotestyourself v1.1.0
+
+github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
+github.com/imdario/mergo 0.2.1
+golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
+
+github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
+github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8
+github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
+
+#get libnetwork packages
+github.com/docker/libnetwork 68f1039f172434709a4550fe92e3e058406c74ce
+github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
+github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
+github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
+github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
+github.com/hashicorp/memberlist v0.1.0
+github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
+github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
+github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
+github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
+github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
+github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
+github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
+github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
+github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
+github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
+github.com/coreos/etcd v3.2.1
+github.com/coreos/go-semver v0.2.0
+github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
+github.com/hashicorp/consul v0.5.2
+github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
+github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
+
+# get graph and distribution packages
+github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
+github.com/vbatts/tar-split v0.10.1
+github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
+
+# get go-zfs packages
+github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa
+github.com/pborman/uuid v1.0
+
+google.golang.org/grpc v1.3.0
+
+# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
+github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
+github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
+github.com/opencontainers/runtime-spec v1.0.0
+
+github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
+
+# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
+github.com/coreos/go-systemd v4
+github.com/godbus/dbus v4.0.0
+github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
+github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
+
+# gelf logging driver deps
+github.com/Graylog2/go-gelf v2
+
+github.com/fluent/fluent-logger-golang v1.2.1
+# fluent-logger-golang deps
+github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972
+github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c
+
+# fsnotify
+github.com/fsnotify/fsnotify v1.4.2
+
+# awslogs deps
+github.com/aws/aws-sdk-go v1.4.22
+github.com/go-ini/ini 060d7da055ba6ec5ea7a31f116332fe5efa04ce0
+github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
+
+# logentries
+github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf
+
+# gcplogs deps
+golang.org/x/oauth2 96382aa079b72d8c014eb0c50f6c223d1e6a2de0
+google.golang.org/api 3cc2e591b550923a2c5f0ab5a803feda924d5823
+cloud.google.com/go 9d965e63e8cceb1b5d7977a202f0fcb8866d6525
+github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7
+google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
+
+# containerd
+github.com/containerd/containerd 06b9cb35161009dcb7123345749fef02f7cea8e0
+github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
+
+# cluster
+github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0
+github.com/gogo/protobuf v0.4
+github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
+github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
+golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
+golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
+github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad
+github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990
+github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
+github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
+github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0
+github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e
+github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
+github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/matttproud/golang_protobuf_extensions v1.0.0
+github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
+github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
+
+# cli
+github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git
+github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
+github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
+
+# metrics
+github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
+
+github.com/opencontainers/selinux v1.0.0-rc1
+
+# archive/tar
+# mkdir -p ./vendor/archive
+# git clone git://github.com/tonistiigi/go-1.git ./go
+# git --git-dir ./go/.git --work-tree ./go checkout revert-prefix-ignore
+# cp -a go/src/archive/tar ./vendor/archive/tar
+# rm -rf ./go
+# vndr
--- /dev/null
+github.com/kr/pretty 737b74a46c4bf788349f72cb256fed10aea4d0ac
+github.com/kr/text 7cafcd837844e784b526369c9bce262804aebc60
+github.com/maruel/ut a9c9f15ccfa6f8b90182a53df32f4745586fbae3
+github.com/mattn/go-colorable 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
+github.com/mattn/go-isatty 56b76bdf51f7708750eac80fa38b952bb9f32639
+github.com/mgutz/ansi c286dcecd19ff979eeb73ea444e479b903f2cfcb
+github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
+golang.org/x/sys a646d33e2ee3172a661fc09bca23bb4889a41bc8
--- /dev/null
+vendors:
+- path: github.com/kr/pretty
+ rev: 737b74a46c4bf788349f72cb256fed10aea4d0ac
+- path: github.com/kr/text
+ rev: 7cafcd837844e784b526369c9bce262804aebc60
+- path: github.com/maruel/ut
+ rev: a9c9f15ccfa6f8b90182a53df32f4745586fbae3
+- path: github.com/mattn/go-colorable
+ rev: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
+- path: github.com/mattn/go-isatty
+ rev: 56b76bdf51f7708750eac80fa38b952bb9f32639
+- path: github.com/mgutz/ansi
+ rev: c286dcecd19ff979eeb73ea444e479b903f2cfcb
+- path: github.com/pmezard/go-difflib
+ rev: 792786c7400a136282c1664665ae0a8db921c6c2
+- path: golang.org/x/sys
+ rev: a646d33e2ee3172a661fc09bca23bb4889a41bc8
--- /dev/null
+cloud.google.com/go/compute/metadata c589d0c9f0d81640c518354c7bcae77d99820aa3
+cloud.google.com/go/internal c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/Azure/azure-sdk-for-go/arm/compute bd73d950fa4440dae889bd9917bff7cef539f86e
+github.com/Azure/azure-sdk-for-go/arm/network bd73d950fa4440dae889bd9917bff7cef539f86e
+github.com/Azure/go-autorest/autorest 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/azure 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/date 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/to 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/Azure/go-autorest/autorest/validation 8a25372bbfec739b8719a9e3987400d15ef9e179
+github.com/PuerkitoBio/purell c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/PuerkitoBio/urlesc c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/asaskevich/govalidator 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877
+github.com/aws/aws-sdk-go/aws 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/awserr 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/awsutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/client 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/client/metadata 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/corehandlers 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/endpointcreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/credentials/stscreds 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/defaults 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/ec2metadata 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/request 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/session 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/aws/signer/v4 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/endpoints 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/ec2query 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/query 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/query/queryutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/rest 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/private/waiter 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/service/ec2 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/aws/aws-sdk-go/service/sts 707203bc55114ed114446bf57949c5c211d8b7c0
+github.com/beorn7/perks/quantile 3ac7bf7a47d159a033b107610db8a1b6575507a4
+github.com/blang/semver c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/http c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/jose c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/key c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/oauth2 c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/go-oidc/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/health c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/httputil c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/coreos/pkg/timeutil c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/davecgh/go-spew/spew c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/dgrijalva/jwt-go 9ed569b5d1ac936e6494082958d63a6aa4fff99a
+github.com/docker/distribution/digest c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/docker/distribution/reference c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful/log c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/emicklei/go-restful/swagger c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/ghodss/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-ini/ini 6e4869b434bd001f6983749881c7ead3545887d8
+github.com/go-openapi/jsonpointer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/jsonreference c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/spec c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/go-openapi/swag c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/gogo/protobuf/proto c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/gogo/protobuf/sortkeys c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/golang/glog c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/golang/protobuf/proto 98fa357170587e470c5f27d3c3ea0947b71eb455
+github.com/golang/snappy d9eb7a3d35ec988b8585d4a0068e462c27d28380
+github.com/google/gofuzz c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/hashicorp/consul/api daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36
+github.com/hashicorp/go-cleanhttp ad28ea4487f05916463e2423a55166280e8254b5
+github.com/hashicorp/serf/coordinate 1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5
+github.com/influxdb/influxdb/client 291aaeb9485b43b16875c238482b2f7d0a22a13b
+github.com/influxdb/influxdb/tsdb 291aaeb9485b43b16875c238482b2f7d0a22a13b
+github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
+github.com/jonboulle/clockwork c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/juju/ratelimit c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/julienschmidt/httprouter 109e267447e95ad1bb48b758e40dd7453eb7b039
+github.com/mailru/easyjson/buffer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/mailru/easyjson/jlexer c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/mailru/easyjson/jwriter c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/matttproud/golang_protobuf_extensions/pbutil fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
+github.com/miekg/dns 58f52c57ce9df13460ac68200cef30a008b9c468
+github.com/pborman/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/pmezard/go-difflib/difflib d77da356e56a7428ad25149ca77381849a6a5232
+github.com/prometheus/client_golang/prometheus c5b7fccd204277076155f10851dad72b76a49317
+github.com/prometheus/client_model/go fa8ad6fec33561be4280a8f0514318c79d7f6cb6
+github.com/prometheus/common/expfmt 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/log 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/model 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/route 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/common/version 85637ea67b04b5c3bb25e671dacded2977f8f9f6
+github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
+github.com/samuel/go-zookeeper/zk 177002e16a0061912f02377e2dd8951a8b3551bc
+github.com/spf13/pflag c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/stretchr/testify/assert d77da356e56a7428ad25149ca77381849a6a5232
+github.com/stretchr/testify/require d77da356e56a7428ad25149ca77381849a6a5232
+github.com/syndtr/goleveldb/leveldb 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/cache 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/comparer 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/errors 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/filter 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/iterator 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/journal 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/memdb 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/opt 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/storage 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/table 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/syndtr/goleveldb/leveldb/util 6b4daa5362b502898ddf367c5c11deb9e7a5c727
+github.com/ugorji/go/codec c589d0c9f0d81640c518354c7bcae77d99820aa3
+github.com/vaughan0/go-ini a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
+golang.org/x/net/context b336a971b799939dd16ae9b1df8334cb8b977c4d
+golang.org/x/net/context/ctxhttp b336a971b799939dd16ae9b1df8334cb8b977c4d
+golang.org/x/net/http2 c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/http2/hpack c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/idna c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/internal/timeseries 6250b412798208e6c90b03b7c4f226de5aa299e2
+golang.org/x/net/lex/httplex c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/net/netutil bc3663df0ac92f928d419e31e0d2af22e683a5a2
+golang.org/x/oauth2 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/google 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/internal 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/jws 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/oauth2/jwt 65a8d08c6292395d47053be10b3c5e91960def76
+golang.org/x/sys/unix c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows/registry c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/sys/windows/svc/eventlog c200b10b5d5e122be351b67af224adc6128af5bf
+golang.org/x/text/cases c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/internal/tag c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/language c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/runes c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/secure/bidirule c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/secure/precis c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/transform c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/unicode/bidi c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/unicode/norm c589d0c9f0d81640c518354c7bcae77d99820aa3
+golang.org/x/text/width c589d0c9f0d81640c518354c7bcae77d99820aa3
+google.golang.org/api/compute/v1 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/gensupport 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/googleapi 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/api/googleapi/internal/uritemplates 63ade871fd3aec1225809d496e81ec91ab76ea29
+google.golang.org/appengine 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/app_identity 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/base 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/datastore 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/log 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/modules 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/internal/remote_api 4f7eeb5305a4ba1966344836ba4af9996b7b4e05
+google.golang.org/appengine/internal/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/appengine/urlfetch 267c27e7492265b84fc6719503b14a1e17975d79
+google.golang.org/cloud/compute/metadata 0a83eba2cadb60eb22123673c8fb6fca02b03c94
+google.golang.org/cloud/internal 0a83eba2cadb60eb22123673c8fb6fca02b03c94
+gopkg.in/fsnotify.v1 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
+gopkg.in/inf.v0 c589d0c9f0d81640c518354c7bcae77d99820aa3
+gopkg.in/yaml.v2 7ad95dd0798a40da1ccdff6dff35fd177b5edf40
+k8s.io/client-go/1.5/discovery c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/core/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/errors c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/meta c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/meta/metatypes c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/resource c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/unversioned c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/api/validation/path c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery/announced c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apimachinery/registered c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/autoscaling/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/v1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage/install c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/apis/storage/v1beta1 c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/auth/user c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/conversion c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/conversion/queryparams c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/fields c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/labels c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/json c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/streaming c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/runtime/serializer/versioning c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/selection c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/types c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/cert c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/clock c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/errors c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/flowcontrol c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/framer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/integer c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/intstr c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/json c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/labels c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/net c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/parsers c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/rand c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/runtime c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/sets c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/uuid c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/validation c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/validation/field c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/wait c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/util/yaml c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/version c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/watch c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/pkg/watch/versioned c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/rest c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/cache c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/clientcmd/api c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/tools/metrics c589d0c9f0d81640c518354c7bcae77d99820aa3
+k8s.io/client-go/1.5/transport c589d0c9f0d81640c518354c7bcae77d99820aa3
--- /dev/null
+{
+ "comment": "",
+ "ignore": "test appengine",
+ "package": [
+ {
+ "checksumSHA1": "Cslv4/ITyQmgjSUhNXFu8q5bqOU=",
+ "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/compute/metadata",
+ "path": "cloud.google.com/go/compute/metadata",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "hiJXjkFEGy+sDFf6O58Ocdy9Rnk=",
+ "origin": "k8s.io/client-go/1.5/vendor/cloud.google.com/go/internal",
+ "path": "cloud.google.com/go/internal",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "oIt4tXgFYnZJBsCac1BQLnTWALM=",
+ "path": "github.com/Azure/azure-sdk-for-go/arm/compute",
+ "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
+ "revisionTime": "2016-10-28T18:31:11Z"
+ },
+ {
+ "checksumSHA1": "QKi6LiSyD5GnRK8ExpMgZl4XiMI=",
+ "path": "github.com/Azure/azure-sdk-for-go/arm/network",
+ "revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
+ "revisionTime": "2016-10-28T18:31:11Z"
+ },
+ {
+ "checksumSHA1": "eVSHe6GIHj9/ziFrQLZ1SC7Nn6k=",
+ "path": "github.com/Azure/go-autorest/autorest",
+ "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+ "revisionTime": "2016-10-25T18:07:34Z"
+ },
+ {
+ "checksumSHA1": "0sYi0JprevG/PZjtMbOh8h0pt0g=",
+ "path": "github.com/Azure/go-autorest/autorest/azure",
+ "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+ "revisionTime": "2016-10-25T18:07:34Z"
+ },
+ {
+ "checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=",
+ "path": "github.com/Azure/go-autorest/autorest/date",
+ "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+ "revisionTime": "2016-10-25T18:07:34Z"
+ },
+ {
+ "checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=",
+ "path": "github.com/Azure/go-autorest/autorest/to",
+ "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+ "revisionTime": "2016-10-25T18:07:34Z"
+ },
+ {
+ "checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=",
+ "path": "github.com/Azure/go-autorest/autorest/validation",
+ "revision": "8a25372bbfec739b8719a9e3987400d15ef9e179",
+ "revisionTime": "2016-10-25T18:07:34Z"
+ },
+ {
+ "checksumSHA1": "IatnluZB5jTVUncMN134e4VOV34=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/purell",
+ "path": "github.com/PuerkitoBio/purell",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "E/Tz8z0B/gaR551g+XqPKAhcteM=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/PuerkitoBio/urlesc",
+ "path": "github.com/PuerkitoBio/urlesc",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "BdLdZP/C2uOO3lqk9X3NCKFpXa4=",
+ "path": "github.com/asaskevich/govalidator",
+ "revision": "7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877",
+ "revisionTime": "2016-10-01T16:31:30Z"
+ },
+ {
+ "checksumSHA1": "WNfR3yhLjRC5/uccgju/bwrdsxQ=",
+ "path": "github.com/aws/aws-sdk-go/aws",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
+ "path": "github.com/aws/aws-sdk-go/aws/awserr",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
+ "path": "github.com/aws/aws-sdk-go/aws/awsutil",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
+ "path": "github.com/aws/aws-sdk-go/aws/client",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
+ "path": "github.com/aws/aws-sdk-go/aws/client/metadata",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
+ "path": "github.com/aws/aws-sdk-go/aws/corehandlers",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
+ "path": "github.com/aws/aws-sdk-go/aws/credentials",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=",
+ "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
+ "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
+ "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
+ "path": "github.com/aws/aws-sdk-go/aws/defaults",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=",
+ "path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
+ "path": "github.com/aws/aws-sdk-go/aws/request",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
+ "path": "github.com/aws/aws-sdk-go/aws/session",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=",
+ "path": "github.com/aws/aws-sdk-go/aws/signer/v4",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
+ "path": "github.com/aws/aws-sdk-go/private/endpoints",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol/query",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol/rest",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
+ "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
+ "path": "github.com/aws/aws-sdk-go/private/waiter",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
+ "path": "github.com/aws/aws-sdk-go/service/ec2",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
+ "path": "github.com/aws/aws-sdk-go/service/sts",
+ "revision": "707203bc55114ed114446bf57949c5c211d8b7c0",
+ "revisionTime": "2016-11-02T21:59:28Z"
+ },
+ {
+ "checksumSHA1": "4QnLdmB1kG3N+KlDd1N+G9TWAGQ=",
+ "path": "github.com/beorn7/perks/quantile",
+ "revision": "3ac7bf7a47d159a033b107610db8a1b6575507a4",
+ "revisionTime": "2016-02-29T21:34:45Z"
+ },
+ {
+ "checksumSHA1": "n+s4YwtzpMWW5Rt0dEaQa7NHDGQ=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/blang/semver",
+ "path": "github.com/blang/semver",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Z2AOGSmDKKvI6nuxa+UPjQWpIeM=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/http",
+ "path": "github.com/coreos/go-oidc/http",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "8yvt1xKCgNwuuavJdxRnvaIjrIc=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/jose",
+ "path": "github.com/coreos/go-oidc/jose",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "zhXKrWBSSJLqZxVE/Xsw0M9ynFQ=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/key",
+ "path": "github.com/coreos/go-oidc/key",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "bkW0mnXvmHQwHprW/6wrbpP7lAk=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oauth2",
+ "path": "github.com/coreos/go-oidc/oauth2",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "E1x2k5FdhJ+dzFrh3kCmC6aJfVw=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/go-oidc/oidc",
+ "path": "github.com/coreos/go-oidc/oidc",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "O0UMBRCOD9ItMayDqLQ2MJEjkVE=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/health",
+ "path": "github.com/coreos/pkg/health",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "74vyZz/d49FZXMbFaHOfCGvSLj0=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/httputil",
+ "path": "github.com/coreos/pkg/httputil",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "etBdQ0LN6ojGunfvUt6B5C3FNrQ=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/coreos/pkg/timeutil",
+ "path": "github.com/coreos/pkg/timeutil",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "SdSd7pyjONWWTHc5XE3AhglLo34=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/davecgh/go-spew/spew",
+ "path": "github.com/davecgh/go-spew/spew",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=",
+ "path": "github.com/dgrijalva/jwt-go",
+ "revision": "9ed569b5d1ac936e6494082958d63a6aa4fff99a",
+ "revisionTime": "2016-11-01T19:39:35Z"
+ },
+ {
+ "checksumSHA1": "f1wARLDzsF/JoyN01yoxXEwFIp8=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/digest",
+ "path": "github.com/docker/distribution/digest",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "PzXRTLmmqWXxmDqdIXLcRYBma18=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/docker/distribution/reference",
+ "path": "github.com/docker/distribution/reference",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "1vQR+ZyudsjKio6RNKmWhwzGTb0=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful",
+ "path": "github.com/emicklei/go-restful",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "3xWz4fZ9xW+CfADpYoPFcZCYJ4E=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/log",
+ "path": "github.com/emicklei/go-restful/log",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "J7CtF9gIs2yH9A7lPQDDrhYxiRk=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/emicklei/go-restful/swagger",
+ "path": "github.com/emicklei/go-restful/swagger",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ww7LVo7jNJ1o6sfRcromEHKyY+o=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/ghodss/yaml",
+ "path": "github.com/ghodss/yaml",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "cVyhKIRI2gQrgpn5qrBeAqErmWM=",
+ "path": "github.com/go-ini/ini",
+ "revision": "6e4869b434bd001f6983749881c7ead3545887d8",
+ "revisionTime": "2016-08-27T06:11:18Z"
+ },
+ {
+ "checksumSHA1": "NaZnW0tKj/b0k5WzcMD0twrLbrE=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonpointer",
+ "path": "github.com/go-openapi/jsonpointer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "3LJXjMDxPY+veIqzQtiAvK3hXnY=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/jsonreference",
+ "path": "github.com/go-openapi/jsonreference",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "faeB3fny260hQ/gEfEXa1ZQTGtk=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/spec",
+ "path": "github.com/go-openapi/spec",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "wGpZwJ5HZtReou8A3WEV1Gdxs6k=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/go-openapi/swag",
+ "path": "github.com/go-openapi/swag",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "BIyZQL97iG7mzZ2UMR3XpiXbZdc=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/proto",
+ "path": "github.com/gogo/protobuf/proto",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "e6cMbpJj41MpihS5eP4SIliRBK4=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/gogo/protobuf/sortkeys",
+ "path": "github.com/gogo/protobuf/sortkeys",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "URsJa4y/sUUw/STmbeYx9EKqaYE=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/golang/glog",
+ "path": "github.com/golang/glog",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "yDh5kmmr0zEF1r+rvYqbZcR7iLs=",
+ "path": "github.com/golang/protobuf/proto",
+ "revision": "98fa357170587e470c5f27d3c3ea0947b71eb455",
+ "revisionTime": "2016-10-12T20:53:35Z"
+ },
+ {
+ "checksumSHA1": "2a/SsTUBMKtcM6VtpbdPGO+c6c8=",
+ "path": "github.com/golang/snappy",
+ "revision": "d9eb7a3d35ec988b8585d4a0068e462c27d28380",
+ "revisionTime": "2016-05-29T05:00:41Z"
+ },
+ {
+ "checksumSHA1": "/yFfUp3tGt6cK22UVzbq8SjPDCU=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/google/gofuzz",
+ "path": "github.com/google/gofuzz",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=",
+ "path": "github.com/hashicorp/consul/api",
+ "revision": "daacc4be8bee214e3fc4b32a6dd385f5ef1b4c36",
+ "revisionTime": "2016-10-28T04:06:46Z"
+ },
+ {
+ "checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=",
+ "path": "github.com/hashicorp/go-cleanhttp",
+ "revision": "ad28ea4487f05916463e2423a55166280e8254b5",
+ "revisionTime": "2016-04-07T17:41:26Z"
+ },
+ {
+ "checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
+ "path": "github.com/hashicorp/serf/coordinate",
+ "revision": "1d4fa605f6ff3ed628d7ae5eda7c0e56803e72a5",
+ "revisionTime": "2016-10-07T00:41:22Z"
+ },
+ {
+ "path": "github.com/influxdb/influxdb/client",
+ "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b",
+ "revisionTime": "2015-09-16T14:41:53+02:00"
+ },
+ {
+ "path": "github.com/influxdb/influxdb/tsdb",
+ "revision": "291aaeb9485b43b16875c238482b2f7d0a22a13b",
+ "revisionTime": "2015-09-16T14:41:53+02:00"
+ },
+ {
+ "checksumSHA1": "0ZrwvB6KoGPj2PoDNSEJwxQ6Mog=",
+ "path": "github.com/jmespath/go-jmespath",
+ "revision": "bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d",
+ "revisionTime": "2016-08-03T19:07:31Z"
+ },
+ {
+ "checksumSHA1": "9ZVOEbIXnTuYpVqce4en8rwlkPE=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/jonboulle/clockwork",
+ "path": "github.com/jonboulle/clockwork",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "gA95N2LM2hEJLoqrTPaFsSWDJ2Y=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/juju/ratelimit",
+ "path": "github.com/juju/ratelimit",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Farach1xcmsQYrhiUfkwF2rbIaE=",
+ "path": "github.com/julienschmidt/httprouter",
+ "revision": "109e267447e95ad1bb48b758e40dd7453eb7b039",
+ "revisionTime": "2015-09-05T19:25:33+02:00"
+ },
+ {
+ "checksumSHA1": "urY45++NYCue4nh4k8OjUFnIGfU=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/buffer",
+ "path": "github.com/mailru/easyjson/buffer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "yTDKAM4KBgOvXRsZC50zg0OChvM=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jlexer",
+ "path": "github.com/mailru/easyjson/jlexer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "4+d+6rhM1pei6lBguhqSEW7LaXs=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/mailru/easyjson/jwriter",
+ "path": "github.com/mailru/easyjson/jwriter",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Q2vw4HZBbnU8BLFt8VrzStwqSJg=",
+ "path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
+ "revision": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a",
+ "revisionTime": "2015-04-06T19:39:34+02:00"
+ },
+ {
+ "checksumSHA1": "Wahi4g/9XiHhSLAJ+8jskg71PCU=",
+ "path": "github.com/miekg/dns",
+ "revision": "58f52c57ce9df13460ac68200cef30a008b9c468",
+ "revisionTime": "2016-10-18T06:08:08Z"
+ },
+ {
+ "checksumSHA1": "3YJklSuzSE1Rt8A+2dhiWSmf/fw=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/pborman/uuid",
+ "path": "github.com/pborman/uuid",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "zKKp5SZ3d3ycKe4EKMNT0BqAWBw=",
+ "origin": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
+ "path": "github.com/pmezard/go-difflib/difflib",
+ "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+ "revisionTime": "2016-06-15T09:26:46Z"
+ },
+ {
+ "checksumSHA1": "KkB+77Ziom7N6RzSbyUwYGrmDeU=",
+ "path": "github.com/prometheus/client_golang/prometheus",
+ "revision": "c5b7fccd204277076155f10851dad72b76a49317",
+ "revisionTime": "2016-08-17T15:48:24Z"
+ },
+ {
+ "checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=",
+ "path": "github.com/prometheus/client_model/go",
+ "revision": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6",
+ "revisionTime": "2015-02-12T10:17:44Z"
+ },
+ {
+ "checksumSHA1": "mHyjbJ3BWOfUV6q9f5PBt0gaY1k=",
+ "path": "github.com/prometheus/common/expfmt",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=",
+ "path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "UU6hIfhVjnAYDADQEfE/3T7Ddm8=",
+ "path": "github.com/prometheus/common/log",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "nFie+rxcX5WdIv1diZ+fu3aj6lE=",
+ "path": "github.com/prometheus/common/model",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "QQKJYoGcY10nIHxhBEHwjwUZQzk=",
+ "path": "github.com/prometheus/common/route",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "91KYK0SpvkaMJJA2+BcxbVnyRO0=",
+ "path": "github.com/prometheus/common/version",
+ "revision": "85637ea67b04b5c3bb25e671dacded2977f8f9f6",
+ "revisionTime": "2016-10-02T21:02:34Z"
+ },
+ {
+ "checksumSHA1": "W218eJZPXJG783fUr/z6IaAZyes=",
+ "path": "github.com/prometheus/procfs",
+ "revision": "abf152e5f3e97f2fafac028d2cc06c1feb87ffa5",
+ "revisionTime": "2016-04-11T19:08:41Z"
+ },
+ {
+ "checksumSHA1": "+49Vr4Me28p3cR+gxX5SUQHbbas=",
+ "path": "github.com/samuel/go-zookeeper/zk",
+ "revision": "177002e16a0061912f02377e2dd8951a8b3551bc",
+ "revisionTime": "2015-08-17T10:50:50-07:00"
+ },
+ {
+ "checksumSHA1": "YuPBOVkkE3uuBh4RcRUTF0n+frs=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/spf13/pflag",
+ "path": "github.com/spf13/pflag",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=",
+ "path": "github.com/stretchr/testify/assert",
+ "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+ "revisionTime": "2016-06-15T09:26:46Z"
+ },
+ {
+ "checksumSHA1": "P9FJpir2c4G5PA46qEkaWy3l60U=",
+ "path": "github.com/stretchr/testify/require",
+ "revision": "d77da356e56a7428ad25149ca77381849a6a5232",
+ "revisionTime": "2016-06-15T09:26:46Z"
+ },
+ {
+ "checksumSHA1": "VhcnDY37sYAnL8WjfYQN9YYl+W4=",
+ "path": "github.com/syndtr/goleveldb/leveldb",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
+ "path": "github.com/syndtr/goleveldb/leveldb/cache",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=",
+ "path": "github.com/syndtr/goleveldb/leveldb/comparer",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=",
+ "path": "github.com/syndtr/goleveldb/leveldb/errors",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=",
+ "path": "github.com/syndtr/goleveldb/leveldb/filter",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "8dXuAVIsbtaMiGGuHjzGR6Ny/5c=",
+ "path": "github.com/syndtr/goleveldb/leveldb/iterator",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=",
+ "path": "github.com/syndtr/goleveldb/leveldb/journal",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "j+uaQ6DwJ50dkIdfMQu1TXdlQcY=",
+ "path": "github.com/syndtr/goleveldb/leveldb/memdb",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=",
+ "path": "github.com/syndtr/goleveldb/leveldb/opt",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "/Wvv9HeJTN9UUjdjwUlz7X4ioIo=",
+ "path": "github.com/syndtr/goleveldb/leveldb/storage",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "JTJA+u8zk7EXy1UUmpFPNGvtO2A=",
+ "path": "github.com/syndtr/goleveldb/leveldb/table",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "4zil8Gwg8VPkDn1YzlgCvtukJFU=",
+ "path": "github.com/syndtr/goleveldb/leveldb/util",
+ "revision": "6b4daa5362b502898ddf367c5c11deb9e7a5c727",
+ "revisionTime": "2016-10-11T05:00:08Z"
+ },
+ {
+ "checksumSHA1": "f6Aew+ZA+HBAXCw6/xTST3mB0Lw=",
+ "origin": "k8s.io/client-go/1.5/vendor/github.com/ugorji/go/codec",
+ "path": "github.com/ugorji/go/codec",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "sFD8LpJPQtWLwGda3edjf5mNUbs=",
+ "path": "github.com/vaughan0/go-ini",
+ "revision": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1",
+ "revisionTime": "2013-09-23T16:52:12+02:00"
+ },
+ {
+ "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
+ "path": "golang.org/x/net/context",
+ "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d",
+ "revisionTime": "2016-10-27T19:58:04Z"
+ },
+ {
+ "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=",
+ "path": "golang.org/x/net/context/ctxhttp",
+ "revision": "b336a971b799939dd16ae9b1df8334cb8b977c4d",
+ "revisionTime": "2016-10-27T19:58:04Z"
+ },
+ {
+ "checksumSHA1": "SPYGC6DQrH9jICccUsOfbvvhB4g=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2",
+ "path": "golang.org/x/net/http2",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "EYNaHp7XdLWRydUCE0amEkKAtgk=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/http2/hpack",
+ "path": "golang.org/x/net/http2/hpack",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "gXiSniT8fevWOVPVKopYgrdzi60=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/idna",
+ "path": "golang.org/x/net/idna",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
+ "path": "golang.org/x/net/internal/timeseries",
+ "revision": "6250b412798208e6c90b03b7c4f226de5aa299e2",
+ "revisionTime": "2016-08-24T22:20:41Z"
+ },
+ {
+ "checksumSHA1": "yhndhWXMs/VSEDLks4dNyFMQStA=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/net/lex/httplex",
+ "path": "golang.org/x/net/lex/httplex",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "7WASrg0PEueWDDRHkFhEEN6Qrms=",
+ "path": "golang.org/x/net/netutil",
+ "revision": "bc3663df0ac92f928d419e31e0d2af22e683a5a2",
+ "revisionTime": "2016-06-21T20:48:10Z"
+ },
+ {
+ "checksumSHA1": "mktBVED98G2vv+OKcSgtnFVZC1Y=",
+ "path": "golang.org/x/oauth2",
+ "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+ "revisionTime": "2016-06-07T03:33:14Z"
+ },
+ {
+ "checksumSHA1": "2rk6lthfQa5Rfydj8j7+dilKGbo=",
+ "path": "golang.org/x/oauth2/google",
+ "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+ "revisionTime": "2016-06-07T03:33:14Z"
+ },
+ {
+ "checksumSHA1": "W/GiDqzsagBnR7/yEvxatMhUDBs=",
+ "path": "golang.org/x/oauth2/internal",
+ "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+ "revisionTime": "2016-06-07T03:33:14Z"
+ },
+ {
+ "checksumSHA1": "CPTYHWrVL4jA0B1IuC0hvgcE2AQ=",
+ "path": "golang.org/x/oauth2/jws",
+ "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+ "revisionTime": "2016-06-07T03:33:14Z"
+ },
+ {
+ "checksumSHA1": "xifBSq0Pn6pIoPA/o3tyzq8X4Ds=",
+ "path": "golang.org/x/oauth2/jwt",
+ "revision": "65a8d08c6292395d47053be10b3c5e91960def76",
+ "revisionTime": "2016-06-07T03:33:14Z"
+ },
+ {
+ "checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=",
+ "path": "golang.org/x/sys/unix",
+ "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+ "revisionTime": "2016-10-22T18:22:21Z"
+ },
+ {
+ "checksumSHA1": "fpW2dhGFC6SrVzipJx7fjg2DIH8=",
+ "path": "golang.org/x/sys/windows",
+ "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+ "revisionTime": "2016-10-22T18:22:21Z"
+ },
+ {
+ "checksumSHA1": "PjYlbMS0ttyZYlaevvjA/gV3g1c=",
+ "path": "golang.org/x/sys/windows/registry",
+ "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+ "revisionTime": "2016-10-22T18:22:21Z"
+ },
+ {
+ "checksumSHA1": "uVlUSSKplihZG7N+QJ6fzDZ4Kh8=",
+ "path": "golang.org/x/sys/windows/svc/eventlog",
+ "revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
+ "revisionTime": "2016-10-22T18:22:21Z"
+ },
+ {
+ "checksumSHA1": "QQpKbWuqvhmxVr/hfEYdWzzcXRM=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/cases",
+ "path": "golang.org/x/text/cases",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "iAsGo/kxvnwILbJVUCd0ZcqZO/Q=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/internal/tag",
+ "path": "golang.org/x/text/internal/tag",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "mQ6PCGHY7K0oPjKbYD8wsTjm/P8=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/language",
+ "path": "golang.org/x/text/language",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "WpeH2TweiuiZAQVTJNO5vyZAQQA=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/runes",
+ "path": "golang.org/x/text/runes",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "1VjEPyjdi0xOiIN/Alkqiad/B/c=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/bidirule",
+ "path": "golang.org/x/text/secure/bidirule",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "FcK7VslktIAWj5jnWVnU2SesBq0=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/secure/precis",
+ "path": "golang.org/x/text/secure/precis",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "nwlu7UTwYbCj9l5f3a7t2ROwNzM=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/transform",
+ "path": "golang.org/x/text/transform",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "nWJ9R1+Xw41f/mM3b7BYtv77CfI=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/bidi",
+ "path": "golang.org/x/text/unicode/bidi",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "BAZ96wCGUj6HdY9sG60Yw09KWA4=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/unicode/norm",
+ "path": "golang.org/x/text/unicode/norm",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "AZMILKWqLP99UilLgbGZ+uzIVrM=",
+ "origin": "k8s.io/client-go/1.5/vendor/golang.org/x/text/width",
+ "path": "golang.org/x/text/width",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "AjdmRXf0fiy6Bec9mNlsGsmZi1k=",
+ "path": "google.golang.org/api/compute/v1",
+ "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+ "revisionTime": "2016-05-31T06:42:46Z"
+ },
+ {
+ "checksumSHA1": "OtsMVXY89Hc/bBXdDp84atFQawM=",
+ "path": "google.golang.org/api/gensupport",
+ "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+ "revisionTime": "2016-05-31T06:42:46Z"
+ },
+ {
+ "checksumSHA1": "yQREK/OWrz9PLljbr127+xFk6J0=",
+ "path": "google.golang.org/api/googleapi",
+ "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+ "revisionTime": "2016-05-31T06:42:46Z"
+ },
+ {
+ "checksumSHA1": "ii4ET3JHk3vkMUEcg+9t/1RZSUU=",
+ "path": "google.golang.org/api/googleapi/internal/uritemplates",
+ "revision": "63ade871fd3aec1225809d496e81ec91ab76ea29",
+ "revisionTime": "2016-05-31T06:42:46Z"
+ },
+ {
+ "checksumSHA1": "N3KZEuQ9O1QwJXcCJbe7Czwroo4=",
+ "path": "google.golang.org/appengine",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "G9Xp1ScdsfcKsw+PcWunivRRP3o=",
+ "path": "google.golang.org/appengine/internal",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "x6Thdfyasqd68dWZWqzWWeIfAfI=",
+ "path": "google.golang.org/appengine/internal/app_identity",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
+ "path": "google.golang.org/appengine/internal/base",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
+ "path": "google.golang.org/appengine/internal/datastore",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
+ "path": "google.golang.org/appengine/internal/log",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "eLZVX1EHLclFtQnjDIszsdyWRHo=",
+ "path": "google.golang.org/appengine/internal/modules",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
+ "path": "google.golang.org/appengine/internal/remote_api",
+ "revision": "4f7eeb5305a4ba1966344836ba4af9996b7b4e05",
+ "revisionTime": "2016-08-19T23:33:10Z"
+ },
+ {
+ "checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
+ "path": "google.golang.org/appengine/internal/urlfetch",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
+ "path": "google.golang.org/appengine/urlfetch",
+ "revision": "267c27e7492265b84fc6719503b14a1e17975d79",
+ "revisionTime": "2016-06-21T05:59:22Z"
+ },
+ {
+ "checksumSHA1": "Wp8g9MHRmK8SwcyGVCoGtPx+5Lo=",
+ "path": "google.golang.org/cloud/compute/metadata",
+ "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94",
+ "revisionTime": "2016-06-21T15:59:29Z"
+ },
+ {
+ "checksumSHA1": "U7dGDNwEHORvJFMoNSXErKE7ITg=",
+ "path": "google.golang.org/cloud/internal",
+ "revision": "0a83eba2cadb60eb22123673c8fb6fca02b03c94",
+ "revisionTime": "2016-06-21T15:59:29Z"
+ },
+ {
+ "checksumSHA1": "JfVmsMwyeeepbdw4q4wpN07BuFg=",
+ "path": "gopkg.in/fsnotify.v1",
+ "revision": "30411dbcefb7a1da7e84f75530ad3abe4011b4f8",
+ "revisionTime": "2016-04-12T13:37:56Z"
+ },
+ {
+ "checksumSHA1": "pfQwQtWlFezJq0Viroa/L+v+yDM=",
+ "origin": "k8s.io/client-go/1.5/vendor/gopkg.in/inf.v0",
+ "path": "gopkg.in/inf.v0",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "KgT+peLCcuh0/m2mpoOZXuxXmwc=",
+ "path": "gopkg.in/yaml.v2",
+ "revision": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40",
+ "revisionTime": "2015-06-24T11:29:02+01:00"
+ },
+ {
+ "checksumSHA1": "st0Nbu4zwLcP3mz03lDOJVZtn8Y=",
+ "path": "k8s.io/client-go/1.5/discovery",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "S+OzpkipMb46LGZoWuveqSLAcoM=",
+ "path": "k8s.io/client-go/1.5/kubernetes",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "yCBn8ig1TUMrk+ljtK0nDr7E5Vo=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/apps/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ZRnUz5NrpvJsXAjtnRdEv5UYhSI=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/authentication/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "TY55Np20olmPMzXgfVlIUIyqv04=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/authorization/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "FRByJsFff/6lPH20FtJPaK1NPWI=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/autoscaling/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "3Cy2as7HnQ2FDcvpNbatpFWx0P4=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/batch/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "RUKywApIbSLLsfkYxXzifh7HIvs=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/certificates/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "4+Lsxu+sYgzsS2JOHP7CdrZLSKc=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/core/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "H8jzevN03YUfmf2krJt0qj2P9sU=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/extensions/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "hrpA6xxtwj3oMcQbFxI2cDhO2ZA=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/policy/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "B2+F12NeMwrOHvHK2ALyEcr3UGA=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/rbac/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "h2eSNUym87RWPlez7UKujShwrUQ=",
+ "path": "k8s.io/client-go/1.5/kubernetes/typed/storage/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "+oIykJ3A0wYjAWbbrGo0jNnMLXw=",
+ "path": "k8s.io/client-go/1.5/pkg/api",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "UsUsIdhuy5Ej2vI0hbmSsrimoaQ=",
+ "path": "k8s.io/client-go/1.5/pkg/api/errors",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Eo6LLHFqG6YznIAKr2mVjuqUj6k=",
+ "path": "k8s.io/client-go/1.5/pkg/api/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "dYznkLcCEai21z1dX8kZY7uDsck=",
+ "path": "k8s.io/client-go/1.5/pkg/api/meta",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "b06esG4xMj/YNFD85Lqq00cx+Yo=",
+ "path": "k8s.io/client-go/1.5/pkg/api/meta/metatypes",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "L9svak1yut0Mx8r9VLDOwpqZzBk=",
+ "path": "k8s.io/client-go/1.5/pkg/api/resource",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "m7jGshKDLH9kdokfa6MwAqzxRQk=",
+ "path": "k8s.io/client-go/1.5/pkg/api/unversioned",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "iI6s5WAexr1PEfqrbvuscB+oVik=",
+ "path": "k8s.io/client-go/1.5/pkg/api/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ikac34qI/IkTWHnfi8pPl9irPyo=",
+ "path": "k8s.io/client-go/1.5/pkg/api/validation/path",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "MJyygSPp8N6z+7SPtcROz4PEwas=",
+ "path": "k8s.io/client-go/1.5/pkg/apimachinery",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "EGb4IcSTQ1VXCmX0xcyG5GpWId8=",
+ "path": "k8s.io/client-go/1.5/pkg/apimachinery/announced",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "vhSyuINHQhCsDKTyBmvJT1HzDHI=",
+ "path": "k8s.io/client-go/1.5/pkg/apimachinery/registered",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "rXeBnwLg8ZFe6m5/Ki7tELVBYDk=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/apps",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "KzHaG858KV1tBh5cuLInNcm+G5s=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/apps/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "fynWdchlRbPaxuST2oGDKiKLTqE=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/apps/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "hreIYssoH4Ef/+Aglpitn3GNLR4=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authentication",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "EgUqJH4CqB9vXVg6T8II2OEt5LE=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authentication/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Z3DKgomzRPGcBv/8hlL6pfnIpXI=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authentication/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "GpuScB2Z+NOT4WIQg1mVvVSDUts=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authorization",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "+u3UD+HY9lBH+PFi/2B4W564JEw=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authorization/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "zIFzgWjmlWNLHGHMpCpDCvoLtKY=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/authorization/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "tdpzQFQyVkt5kCLTvtKTVqT+maE=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "nb6LbYGS5tv8H8Ovptg6M7XuDZ4=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "DNb1/nl/5RDdckRrJoXBRagzJXs=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/autoscaling/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "4bLhH2vNl5l4Qp6MjLhWyWVAPE0=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/batch",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "RpAAEynmxlvOlLLZK1KEUQRnYzk=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/batch/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "uWJ2BHmjL/Gq4FFlNkqiN6vvPyM=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/batch/v1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "mHWt/p724dKeP1vqLtWQCye7zaE=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/batch/v2alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "6dJ1dGfXkB3A42TOtMaY/rvv4N8=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/certificates",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Bkrhm6HbFYANwtzUE8eza9SWBk0=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/certificates/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "nRRPIBQ5O3Ad24kscNtK+gPC+fk=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/certificates/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "KUMhoaOg9GXHN/aAVvSLO18SgqU=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/extensions",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "eSo2VhNAYtesvmpEPqn05goW4LY=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/extensions/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "DunWIPrCC5iGMWzkaaugMOxD+hg=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "rVGYi2ko0E7vL5OZSMYX+NAGPYw=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/policy",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "llJHd2H0LzABGB6BcletzIHnexo=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/policy/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "j44bqyY13ldnuCtysYE8nRkMD7o=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/policy/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "vT7rFxowcKMTYc55mddePqUFRgE=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/rbac",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "r1MzUXsG+Zyn30aU8I5R5dgrJPA=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/rbac/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "aNfO8xn8VDO3fM9CpVCe6EIB+GA=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/rbac/v1alpha1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "rQCxrbisCXmj2wymlYG63kcTL9I=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/storage",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "wZyxh5nt5Eh6kF7YNAIYukKWWy0=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/storage/install",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "P8ANOt/I4Cs3QtjVXWmDA/gpQdg=",
+ "path": "k8s.io/client-go/1.5/pkg/apis/storage/v1beta1",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "qnVPwzvNLz2mmr3BXdU9qIhQXXU=",
+ "path": "k8s.io/client-go/1.5/pkg/auth/user",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "KrIchxhapSs242yAy8yrTS1XlZo=",
+ "path": "k8s.io/client-go/1.5/pkg/conversion",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "weZqKFcOhcnF47eDDHXzluCKSF0=",
+ "path": "k8s.io/client-go/1.5/pkg/conversion/queryparams",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "T3EMfyXZX5939/OOQ1JU+Nmbk4k=",
+ "path": "k8s.io/client-go/1.5/pkg/fields",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "2v11s3EBH8UBl2qfImT29tQN2kM=",
+ "path": "k8s.io/client-go/1.5/pkg/genericapiserver/openapi/common",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "GvBlph6PywK3zguou/T9kKNNdoQ=",
+ "path": "k8s.io/client-go/1.5/pkg/labels",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Vtrgy827r0rWzIAgvIWY4flu740=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "SEcZqRATexhgHvDn+eHvMc07UJs=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "qzYKG9YZSj8l/W1QVTOrGAry/BM=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/json",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "F7h+8zZ0JPLYkac4KgSVljguBE4=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/protobuf",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "CvySOL8C85e3y7EWQ+Au4cwUZJM=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/recognizer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "eCitoKeIun+lJzYFhAfdSIIicSM=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/streaming",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "kVWvZuLGltJ4YqQsiaCLRRLDDK0=",
+ "path": "k8s.io/client-go/1.5/pkg/runtime/serializer/versioning",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "m51+LAeQ9RK1KHX+l2iGcwbVCKs=",
+ "path": "k8s.io/client-go/1.5/pkg/selection",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "dp4IWcC3U6a0HeOdVCDQWODWCbw=",
+ "path": "k8s.io/client-go/1.5/pkg/third_party/forked/golang/reflect",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ER898XJD1ox4d71gKZD8TLtTSpM=",
+ "path": "k8s.io/client-go/1.5/pkg/types",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "BVdXtnLDlmBQksRPfHOIG+qdeVg=",
+ "path": "k8s.io/client-go/1.5/pkg/util",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "nnh8Sa4dCupxRI4bbKaozGp1d/A=",
+ "path": "k8s.io/client-go/1.5/pkg/util/cert",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "S32d5uduNlwouM8+mIz+ALpliUQ=",
+ "path": "k8s.io/client-go/1.5/pkg/util/clock",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Y6rWC0TUw2/uUeUjJ7kazyEUzBQ=",
+ "path": "k8s.io/client-go/1.5/pkg/util/errors",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "C7IfEAdCOePw3/IraaZCNXuYXLw=",
+ "path": "k8s.io/client-go/1.5/pkg/util/flowcontrol",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "EuslQHnhBSRXaWimYqLEqhMPV48=",
+ "path": "k8s.io/client-go/1.5/pkg/util/framer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ByO18NbZwiifFr8qtLyfJAHXguA=",
+ "path": "k8s.io/client-go/1.5/pkg/util/integer",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ww+RfsoIlUBDwThg2oqC5QVz33Y=",
+ "path": "k8s.io/client-go/1.5/pkg/util/intstr",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "7E8f8dLlXW7u6r9sggMjvB4HEiw=",
+ "path": "k8s.io/client-go/1.5/pkg/util/json",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "d0pFZxMJG9j95acNmaIM1l+X+QU=",
+ "path": "k8s.io/client-go/1.5/pkg/util/labels",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "wCN7u1lE+25neM9jXeI7aE8EAfk=",
+ "path": "k8s.io/client-go/1.5/pkg/util/net",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "g+kBkxcb+tYmFtRRly+VE+JAIfw=",
+ "path": "k8s.io/client-go/1.5/pkg/util/parsers",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "S4wUnE5VkaWWrkLbgPL/1oNLJ4g=",
+ "path": "k8s.io/client-go/1.5/pkg/util/rand",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "8j9c2PqTKybtnymXbStNYRexRj8=",
+ "path": "k8s.io/client-go/1.5/pkg/util/runtime",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "aAz4e8hLGs0+ZAz1TdA5tY/9e1A=",
+ "path": "k8s.io/client-go/1.5/pkg/util/sets",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "P/fwh6QZ5tsjVyHTaASDWL3WaGs=",
+ "path": "k8s.io/client-go/1.5/pkg/util/uuid",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "P9Bq/1qbF4SvnN9HyCTRpbUz7sQ=",
+ "path": "k8s.io/client-go/1.5/pkg/util/validation",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "D0JIEjlP69cuPOZEdsSKeFgsnI8=",
+ "path": "k8s.io/client-go/1.5/pkg/util/validation/field",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "T7ba8t8i+BtgClMgL+aMZM94fcI=",
+ "path": "k8s.io/client-go/1.5/pkg/util/wait",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "6RCTv/KDiw7as4KeyrgU3XrUSQI=",
+ "path": "k8s.io/client-go/1.5/pkg/util/yaml",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "OwKlsSeKtz1FBVC9cQ5gWRL5pKc=",
+ "path": "k8s.io/client-go/1.5/pkg/version",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Oil9WGw/dODbpBopn6LWQGS3DYg=",
+ "path": "k8s.io/client-go/1.5/pkg/watch",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "r5alnRCbLaPsbTeJjjTVn/bt6uw=",
+ "path": "k8s.io/client-go/1.5/pkg/watch/versioned",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "X1+ltyfHui/XCwDupXIf39+9gWQ=",
+ "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "KYy+js37AS0ZT08g5uBr1ZoMPmE=",
+ "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/gcp",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "wQ9G5++lbQpejqCzGHo037N3YcY=",
+ "path": "k8s.io/client-go/1.5/plugin/pkg/client/auth/oidc",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "ABe8YfZVEDoRpAUqp2BKP8o1VIA=",
+ "path": "k8s.io/client-go/1.5/rest",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "Gbe0Vs9hkI7X5hhbXUuWdRFffSI=",
+ "path": "k8s.io/client-go/1.5/tools/cache",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "K/oOznXABjqSS1c2Fs407c5F8KA=",
+ "path": "k8s.io/client-go/1.5/tools/clientcmd/api",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "c1PQ4WJRfpA9BYcFHW2+46hu5IE=",
+ "path": "k8s.io/client-go/1.5/tools/metrics",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ },
+ {
+ "checksumSHA1": "e4W2q+6wvjejv3V0UCI1mewTTro=",
+ "path": "k8s.io/client-go/1.5/transport",
+ "revision": "c589d0c9f0d81640c518354c7bcae77d99820aa3",
+ "revisionTime": "2016-09-30T00:14:02Z"
+ }
+ ],
+ "rootPath": "github.com/prometheus/prometheus"
+}
--- /dev/null
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ branch = "master"
+ name = "bazil.org/fuse"
+ packages = [".","fs","fuseutil"]
+ revision = "371fbbdaa8987b715bdd21d6adc4c9b20155f748"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/NYTimes/gziphandler"
+ packages = ["."]
+ revision = "97ae7fbaf81620fe97840685304a78a306a39c64"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/golang/protobuf"
+ packages = ["proto"]
+ revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/russross/blackfriday"
+ packages = ["."]
+ revision = "6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/crypto"
+ packages = ["acme","acme/autocert","hkdf"]
+ revision = "13931e22f9e72ea58bb73048bc752b48c6d4d4ac"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = ["context"]
+ revision = "4b14673ba32bee7f5ac0f990a48f033919fd418b"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/text"
+ packages = ["cases","internal","internal/gen","internal/tag","internal/triegen","internal/ucd","language","runes","secure/bidirule","secure/precis","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable","width"]
+ revision = "6eab0e8f74e86c598ec3b6fad4888e0c11482d48"
+
+[[projects]]
+ branch = "v2"
+ name = "gopkg.in/yaml.v2"
+ packages = ["."]
+ revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "2246e647ba1c78b0b9f948f9fb072fff1467284fb138709c063e99736f646b90"
+ solver-name = "gps-cdcl"
+ solver-version = 1
--- /dev/null
+bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748
+github.com/NYTimes/gziphandler 97ae7fbaf81620fe97840685304a78a306a39c64
+github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
+github.com/russross/blackfriday 6d1ef893fcb01b4f50cb6e57ed7df3e2e627b6b2
+golang.org/x/crypto 13931e22f9e72ea58bb73048bc752b48c6d4d4ac
+golang.org/x/net 4b14673ba32bee7f5ac0f990a48f033919fd418b
+golang.org/x/text 6eab0e8f74e86c598ec3b6fad4888e0c11482d48
+gopkg.in/yaml.v2 eb3733d160e74a9c7e442f435eb3bea458e1d19f
--- /dev/null
+// 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 modconv
+
+import (
+ "strings"
+
+ "cmd/go/internal/module"
+)
+
+func ParseDependenciesTSV(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ f := strings.Split(line, "\t")
+ if len(f) >= 3 {
+ list = append(list, module.Version{Path: f[0], Version: f[2]})
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "strings"
+
+ "cmd/go/internal/module"
+)
+
+func ParseVendorConf(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ if i := strings.Index(line, "#"); i >= 0 {
+ line = line[:i]
+ }
+ f := strings.Fields(line)
+ if len(f) >= 2 {
+ list = append(list, module.Version{Path: f[0], Version: f[1]})
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "encoding/json"
+
+ "cmd/go/internal/module"
+)
+
+func ParseVendorJSON(file string, data []byte) ([]module.Version, error) {
+ var cfg struct {
+ Package []struct {
+ Path string
+ Revision string
+ }
+ }
+ if err := json.Unmarshal(data, &cfg); err != nil {
+ return nil, err
+ }
+ var list []module.Version
+ for _, d := range cfg.Package {
+ list = append(list, module.Version{Path: d.Path, Version: d.Revision})
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "encoding/json"
+
+ "cmd/go/internal/module"
+)
+
+func ParseVendorManifest(file string, data []byte) ([]module.Version, error) {
+ var cfg struct {
+ Dependencies []struct {
+ ImportPath string
+ Revision string
+ }
+ }
+ if err := json.Unmarshal(data, &cfg); err != nil {
+ return nil, err
+ }
+ var list []module.Version
+ for _, d := range cfg.Dependencies {
+ list = append(list, module.Version{Path: d.ImportPath, Version: d.Revision})
+ }
+ return list, nil
+}
--- /dev/null
+// 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 modconv
+
+import (
+ "cmd/go/internal/module"
+ "strings"
+)
+
+func ParseVendorYML(file string, data []byte) ([]module.Version, error) {
+ var list []module.Version
+ vendors := false
+ path := ""
+ for lineno, line := range strings.Split(string(data), "\n") {
+ lineno++
+ if line == "" {
+ continue
+ }
+ if strings.HasPrefix(line, "vendors:") {
+ vendors = true
+ } else if line[0] != '-' && line[0] != ' ' && line[0] != '\t' {
+ vendors = false
+ }
+ if !vendors {
+ continue
+ }
+ if strings.HasPrefix(line, "- path:") {
+ path = strings.TrimSpace(line[len("- path:"):])
+ }
+ if strings.HasPrefix(line, " rev:") {
+ rev := strings.TrimSpace(line[len(" rev:"):])
+ if path != "" && rev != "" {
+ list = append(list, module.Version{Path: path, Version: rev})
+ }
+ }
+ }
+ return list, nil
+}
--- /dev/null
+// 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 bitbucket
+
+import (
+ "fmt"
+ "strings"
+
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/gitrepo"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+ f := strings.Split(path, "/")
+ if len(f) < 3 || f[0] != "bitbucket.org" {
+ return nil, fmt.Errorf("bitbucket repo must be bitbucket.org/org/project")
+ }
+ path = f[0] + "/" + f[1] + "/" + f[2]
+ return gitrepo.Repo("https://"+path, path)
+}
--- /dev/null
+// 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 codehost defines the interface implemented by a code hosting source,
+// along with support code for use by implementations.
+package codehost
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/str"
+)
+
+// Downloaded size limits.
+const (
+ MaxGoMod = 16 << 20 // maximum size of go.mod file
+ MaxLICENSE = 16 << 20 // maximum size of LICENSE file
+ MaxZipFile = 500 << 20 // maximum size of downloaded zip file
+)
+
+// A Repo represents a code hosting source.
+// Typical implementations include local version control repositories,
+// remote version control servers, and code hosting sites.
+type Repo interface {
+ // Root returns the import path of the root directory of the repository.
+ Root() string
+
+ // List lists all tags with the given prefix.
+ Tags(prefix string) (tags []string, err error)
+
+ // Stat returns information about the revision rev.
+ // A revision can be any identifier known to the underlying service:
+ // commit hash, branch, tag, and so on.
+ Stat(rev string) (*RevInfo, error)
+
+ // Latest returns the latest revision on the default branch,
+ // whatever that means in the underlying implementation.
+ Latest() (*RevInfo, error)
+
+ // ReadFile reads the given file in the file tree corresponding to revision rev.
+ // It should refuse to read more than maxSize bytes.
+ ReadFile(rev, file string, maxSize int64) (data []byte, err error)
+
+ // ReadZip downloads a zip file for the subdir subdirectory
+ // of the given revision to a new file in a given temporary directory.
+ // It should refuse to read more than maxSize bytes.
+ // It returns a ReadCloser for a streamed copy of the zip file,
+ // along with the actual subdirectory (possibly shorter than subdir)
+ // contained in the zip file. All files in the zip file are expected to be
+ // nested in a single top-level directory, whose name is not specified.
+ ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
+}
+
+// A Rev describes a single revision in a source code repository.
+type RevInfo struct {
+ Name string // complete ID in underlying repository
+ Short string // shortened ID, for use in pseudo-version
+ Version string // TODO what is this?
+ Time time.Time // commit time
+}
+
+// AllHex reports whether the revision rev is entirely lower-case hexadecimal digits.
+func AllHex(rev string) bool {
+ for i := 0; i < len(rev); i++ {
+ c := rev[i]
+ if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+// ShortenSHA1 shortens a SHA1 hash (40 hex digits) to the canonical length
+// used in pseudo-versions (12 hex digits).
+func ShortenSHA1(rev string) string {
+ if AllHex(rev) && len(rev) == 40 {
+ return rev[:12]
+ }
+ return rev
+}
+
+// WorkRoot is the root of the cached work directory.
+// It is set by cmd/go/internal/vgo.InitMod.
+var WorkRoot string
+
+// WorkDir returns the name of the cached work directory to use for the
+// given repository type and name.
+func WorkDir(typ, name string) (string, error) {
+ if WorkRoot == "" {
+ return "", fmt.Errorf("codehost.WorkRoot not set")
+ }
+
+ // We name the work directory for the SHA256 hash of the type and name.
+ // We intentionally avoid the actual name both because of possible
+ // conflicts with valid file system paths and because we want to ensure
+ // that one checkout is never nested inside another. That nesting has
+ // led to security problems in the past.
+ if strings.Contains(typ, ":") {
+ return "", fmt.Errorf("codehost.WorkDir: type cannot contain colon")
+ }
+ key := typ + ":" + name
+ dir := filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
+ data, err := ioutil.ReadFile(dir + ".info")
+ if err == nil {
+ have := strings.TrimSuffix(string(data), "\n")
+ if have != key {
+ return "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
+ }
+ _, err := os.Stat(dir)
+ if err != nil {
+ return "", fmt.Errorf("%s exists but %s does not", dir+".info", dir)
+ }
+ if cfg.BuildX {
+ fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name)
+ }
+ return dir, nil
+ }
+
+ if cfg.BuildX {
+ fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", dir, typ, name)
+ }
+ os.RemoveAll(dir)
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ return "", err
+ }
+ if err := ioutil.WriteFile(dir+".info", []byte(key), 0666); err != nil {
+ os.RemoveAll(dir)
+ return "", err
+ }
+ return dir, nil
+}
+
+type RunError struct {
+ Cmd string
+ Err error
+ Stderr []byte
+}
+
+func (e *RunError) Error() string {
+ text := e.Cmd + ": " + e.Err.Error()
+ stderr := bytes.TrimRight(e.Stderr, "\n")
+ if len(stderr) > 0 {
+ text += ":\n\t" + strings.Replace(string(stderr), "\n", "\n\t", -1)
+ }
+ return text
+}
+
+// Run runs the command line in the given directory
+// (an empty dir means the current directory).
+// It returns the standard output and, for a non-zero exit,
+// a *RunError indicating the command, exit status, and standard error.
+// Standard error is unavailable for commands that exit successfully.
+func Run(dir string, cmdline ...interface{}) ([]byte, error) {
+ cmd := str.StringList(cmdline...)
+ if cfg.BuildX {
+ var cd string
+ if dir != "" {
+ cd = "cd " + dir + "; "
+ }
+ fmt.Fprintf(os.Stderr, "%s%s\n", cd, strings.Join(cmd, " "))
+ }
+ // TODO: Impose limits on command output size.
+ // TODO: Set environment to get English error messages.
+ var stderr bytes.Buffer
+ var stdout bytes.Buffer
+ c := exec.Command(cmd[0], cmd[1:]...)
+ c.Dir = dir
+ c.Stderr = &stderr
+ c.Stdout = &stdout
+ err := c.Run()
+ if err != nil {
+ err = &RunError{Cmd: strings.Join(cmd, " ") + " in " + dir, Stderr: stderr.Bytes(), Err: err}
+ }
+ return stdout.Bytes(), err
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "archive/zip"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "cmd/go/internal/modconv"
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfile"
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+)
+
+// A codeRepo implements modfetch.Repo using an underlying codehost.Repo.
+type codeRepo struct {
+ modPath string
+ code codehost.Repo
+ codeRoot string
+ codeDir string
+
+ path string
+ pathPrefix string
+ pathMajor string
+ pseudoMajor string
+}
+
+func newCodeRepo(code codehost.Repo, path string) (Repo, error) {
+ codeRoot := code.Root()
+ if !hasPathPrefix(path, codeRoot) {
+ return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
+ }
+ pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
+ if !ok {
+ return nil, fmt.Errorf("invalid module path %q", path)
+ }
+ pseudoMajor := "v0"
+ if pathMajor != "" {
+ pseudoMajor = pathMajor[1:]
+ }
+
+ // At this point we might have:
+ // codeRoot = github.com/rsc/foo
+ // path = github.com/rsc/foo/bar/v2
+ // pathPrefix = github.com/rsc/foo/bar
+ // pathMajor = /v2
+ // pseudoMajor = v2
+ //
+ // Compute codeDir = bar, the subdirectory within the repo
+ // corresponding to the module root.
+ codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, codeRoot), "/")
+ if strings.HasPrefix(path, "gopkg.in/") {
+ // But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot.
+ // For example we might have:
+ // codeRoot = gopkg.in/yaml.v2
+ // pathPrefix = gopkg.in/yaml
+ // pathMajor = .v2
+ // pseudoMajor = v2
+ // codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix)
+ // Clear codeDir - the module root is the repo root for gopkg.in repos.
+ codeDir = ""
+ }
+
+ r := &codeRepo{
+ modPath: path,
+ code: code,
+ codeRoot: codeRoot,
+ codeDir: codeDir,
+ pathPrefix: pathPrefix,
+ pathMajor: pathMajor,
+ pseudoMajor: pseudoMajor,
+ }
+
+ return r, nil
+}
+
+func (r *codeRepo) ModulePath() string {
+ return r.modPath
+}
+
+func (r *codeRepo) Versions(prefix string) ([]string, error) {
+ p := prefix
+ if r.codeDir != "" {
+ p = r.codeDir + "/" + p
+ }
+ tags, err := r.code.Tags(p)
+ if err != nil {
+ return nil, err
+ }
+ list := []string{}
+ for _, tag := range tags {
+ if !strings.HasPrefix(tag, p) {
+ continue
+ }
+ v := tag
+ if r.codeDir != "" {
+ v = v[len(r.codeDir)+1:]
+ }
+ if !semver.IsValid(v) || v != semver.Canonical(v) || isPseudoVersion(v) || !module.MatchPathMajor(v, r.pathMajor) {
+ continue
+ }
+ list = append(list, v)
+ }
+ SortVersions(list)
+ return list, nil
+}
+
+func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
+ if rev == "latest" {
+ return r.Latest()
+ }
+ codeRev := r.revToRev(rev)
+ if semver.IsValid(codeRev) && r.codeDir != "" {
+ codeRev = r.codeDir + "/" + codeRev
+ }
+ info, err := r.code.Stat(codeRev)
+ if err != nil {
+ return nil, err
+ }
+ return r.convert(info)
+}
+
+func (r *codeRepo) Latest() (*RevInfo, error) {
+ info, err := r.code.Latest()
+ if err != nil {
+ return nil, err
+ }
+ return r.convert(info)
+}
+
+func (r *codeRepo) convert(info *codehost.RevInfo) (*RevInfo, error) {
+ versionOK := func(v string) bool {
+ return semver.IsValid(v) && v == semver.Canonical(v) && !isPseudoVersion(v) && module.MatchPathMajor(v, r.pathMajor)
+ }
+ v := info.Version
+ if r.codeDir == "" {
+ if !versionOK(v) {
+ v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+ }
+ } else {
+ p := r.codeDir + "/"
+ if strings.HasPrefix(v, p) && versionOK(v[len(p):]) {
+ v = v[len(p):]
+ } else {
+ v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+ }
+ }
+
+ info2 := &RevInfo{
+ Name: info.Name,
+ Short: info.Short,
+ Time: info.Time,
+ Version: v,
+ }
+ return info2, nil
+}
+
+func (r *codeRepo) revToRev(rev string) string {
+ if semver.IsValid(rev) {
+ if isPseudoVersion(rev) {
+ i := strings.Index(rev, "-")
+ j := strings.Index(rev[i+1:], "-")
+ return rev[i+1+j+1:]
+ }
+ if r.codeDir == "" {
+ return rev
+ }
+ return r.codeDir + "/" + rev
+ }
+ return rev
+}
+
+func (r *codeRepo) versionToRev(version string) (rev string, err error) {
+ if !semver.IsValid(version) {
+ return "", fmt.Errorf("malformed semantic version %q", version)
+ }
+ return r.revToRev(version), nil
+}
+
+func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err error) {
+ rev, err = r.versionToRev(version)
+ if err != nil {
+ return "", "", nil, err
+ }
+ if r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".") {
+ if r.codeDir == "" {
+ return rev, "", nil, nil
+ }
+ file1 := path.Join(r.codeDir, "go.mod")
+ gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
+ if err1 != nil {
+ return "", "", nil, fmt.Errorf("missing go.mod")
+ }
+ return rev, r.codeDir, gomod1, nil
+ }
+
+ // Suppose pathMajor is "/v2".
+ // Either go.mod should claim v2 and v2/go.mod should not exist,
+ // or v2/go.mod should exist and claim v2. Not both.
+ // Note that we don't check the full path, just the major suffix,
+ // because of replacement modules. This might be a fork of
+ // the real module, found at a different path, usable only in
+ // a replace directive.
+ file1 := path.Join(r.codeDir, "go.mod")
+ file2 := path.Join(r.codeDir, r.pathMajor[1:], "go.mod")
+ gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
+ gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
+ found1 := err1 == nil && isMajor(gomod1, r.pathMajor)
+ found2 := err2 == nil && isMajor(gomod2, r.pathMajor)
+
+ if err2 == nil && !found2 {
+ return "", "", nil, fmt.Errorf("%s has non-...%s module path", file2, r.pathMajor)
+ }
+ if found1 && found2 {
+ return "", "", nil, fmt.Errorf("both %s and %s claim ...%s module", file1, file2, r.pathMajor)
+ }
+ if found2 {
+ return rev, filepath.Join(r.codeDir, r.pathMajor), gomod2, nil
+ }
+ if found1 {
+ return rev, r.codeDir, gomod1, nil
+ }
+ return "", "", nil, fmt.Errorf("missing or invalid go.mod")
+}
+
+func isMajor(gomod []byte, pathMajor string) bool {
+ return strings.HasSuffix(modPath(gomod), pathMajor)
+}
+
+var moduleStr = []byte("module")
+
+func modPath(mod []byte) string {
+ for len(mod) > 0 {
+ line := mod
+ mod = nil
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, mod = line[:i], line[i+1:]
+ }
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, moduleStr) {
+ continue
+ }
+ line = line[len(moduleStr):]
+ n := len(line)
+ line = bytes.TrimSpace(line)
+ if len(line) == n || len(line) == 0 {
+ continue
+ }
+
+ if line[0] == '"' || line[0] == '`' {
+ p, err := strconv.Unquote(string(line))
+ if err != nil {
+ return "" // malformed quoted string or multiline module path
+ }
+ return p
+ }
+
+ return string(line)
+ }
+ return "" // missing module path
+}
+
+func (r *codeRepo) GoMod(version string) (data []byte, err error) {
+ rev, dir, gomod, err := r.findDir(version)
+ if err != nil {
+ return nil, err
+ }
+ if gomod != nil {
+ return gomod, nil
+ }
+ data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
+ if err != nil {
+ if e := strings.ToLower(err.Error()); strings.Contains(e, "not found") || strings.Contains(e, "404") { // TODO
+ return r.legacyGoMod(rev, dir), nil
+ }
+ return nil, err
+ }
+ return data, nil
+}
+
+var altConfigs = []string{
+ "Gopkg.lock",
+
+ "GLOCKFILE",
+ "Godeps/Godeps.json",
+ "dependencies.tsv",
+ "glide.lock",
+ "vendor.conf",
+ "vendor.yml",
+ "vendor/manifest",
+ "vendor/vendor.json",
+}
+
+func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
+ mf := new(modfile.File)
+ mf.AddModuleStmt(r.modPath)
+ for _, file := range altConfigs {
+ data, err := r.code.ReadFile(rev, path.Join(dir, file), codehost.MaxGoMod)
+ if err != nil {
+ continue
+ }
+ convert := modconv.Converters[file]
+ if convert == nil {
+ continue
+ }
+ if err := ConvertLegacyConfig(mf, file, data); err != nil {
+ continue
+ }
+ break
+ }
+ data, err := mf.Format()
+ if err != nil {
+ return []byte(fmt.Sprintf("%s\nmodule %q\n", modconv.Prefix, r.modPath))
+ }
+ return append([]byte(modconv.Prefix+"\n"), data...)
+}
+
+func (r *codeRepo) modPrefix(rev string) string {
+ return r.modPath + "@" + rev
+}
+
+func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
+ rev, dir, _, err := r.findDir(version)
+ if err != nil {
+ return "", err
+ }
+ dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile)
+ if err != nil {
+ return "", err
+ }
+ if actualDir != "" && !hasPathPrefix(dir, actualDir) {
+ return "", fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
+ }
+ subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
+
+ // Spool to local file.
+ f, err := ioutil.TempFile(tmpdir, "vgo-codehost-")
+ if err != nil {
+ dl.Close()
+ return "", err
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+ maxSize := int64(codehost.MaxZipFile)
+ lr := &io.LimitedReader{R: dl, N: maxSize + 1}
+ if _, err := io.Copy(f, lr); err != nil {
+ dl.Close()
+ return "", err
+ }
+ dl.Close()
+ if lr.N <= 0 {
+ return "", fmt.Errorf("downloaded zip file too large")
+ }
+ size := (maxSize + 1) - lr.N
+ if _, err := f.Seek(0, 0); err != nil {
+ return "", err
+ }
+
+ // Translate from zip file we have to zip file we want.
+ zr, err := zip.NewReader(f, size)
+ if err != nil {
+ return "", err
+ }
+ f2, err := ioutil.TempFile(tmpdir, "vgo-")
+ if err != nil {
+ return "", err
+ }
+
+ zw := zip.NewWriter(f2)
+ newName := f2.Name()
+ defer func() {
+ f2.Close()
+ if err != nil {
+ os.Remove(newName)
+ }
+ }()
+ if subdir != "" {
+ subdir += "/"
+ }
+ haveLICENSE := false
+ topPrefix := ""
+ haveGoMod := make(map[string]bool)
+ for _, zf := range zr.File {
+ if topPrefix == "" {
+ i := strings.Index(zf.Name, "/")
+ if i < 0 {
+ return "", fmt.Errorf("missing top-level directory prefix")
+ }
+ topPrefix = zf.Name[:i+1]
+ }
+ if !strings.HasPrefix(zf.Name, topPrefix) {
+ return "", fmt.Errorf("zip file contains more than one top-level directory")
+ }
+ dir, file := path.Split(zf.Name)
+ if file == "go.mod" {
+ haveGoMod[dir] = true
+ }
+ }
+ root := topPrefix + subdir
+ inSubmodule := func(name string) bool {
+ for {
+ dir, _ := path.Split(name)
+ if len(dir) <= len(root) {
+ return false
+ }
+ if haveGoMod[dir] {
+ return true
+ }
+ name = dir[:len(dir)-1]
+ }
+ }
+ for _, zf := range zr.File {
+ if topPrefix == "" {
+ i := strings.Index(zf.Name, "/")
+ if i < 0 {
+ return "", fmt.Errorf("missing top-level directory prefix")
+ }
+ topPrefix = zf.Name[:i+1]
+ }
+ if strings.HasSuffix(zf.Name, "/") { // drop directory dummy entries
+ continue
+ }
+ if !strings.HasPrefix(zf.Name, topPrefix) {
+ return "", fmt.Errorf("zip file contains more than one top-level directory")
+ }
+ name := strings.TrimPrefix(zf.Name, topPrefix)
+ if !strings.HasPrefix(name, subdir) {
+ continue
+ }
+ name = strings.TrimPrefix(name, subdir)
+ if isVendoredPackage(name) {
+ continue
+ }
+ if inSubmodule(zf.Name) {
+ continue
+ }
+ base := path.Base(name)
+ if strings.ToLower(base) == "go.mod" && base != "go.mod" {
+ return "", fmt.Errorf("zip file contains %s, want all lower-case go.mod", zf.Name)
+ }
+ if name == "LICENSE" {
+ haveLICENSE = true
+ }
+ size := int64(zf.UncompressedSize)
+ if size < 0 || maxSize < size {
+ return "", fmt.Errorf("module source tree too big")
+ }
+ maxSize -= size
+
+ rc, err := zf.Open()
+ if err != nil {
+ return "", err
+ }
+ w, err := zw.Create(r.modPrefix(version) + "/" + name)
+ lr := &io.LimitedReader{R: rc, N: size + 1}
+ if _, err := io.Copy(w, lr); err != nil {
+ return "", err
+ }
+ if lr.N <= 0 {
+ return "", fmt.Errorf("individual file too large")
+ }
+ }
+
+ if !haveLICENSE && subdir != "" {
+ if data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE); err == nil {
+ w, err := zw.Create(r.modPrefix(version) + "/LICENSE")
+ if err != nil {
+ return "", err
+ }
+ if _, err := w.Write(data); err != nil {
+ return "", err
+ }
+ }
+ }
+ if err := zw.Close(); err != nil {
+ return "", err
+ }
+ if err := f2.Close(); err != nil {
+ return "", err
+ }
+
+ return f2.Name(), nil
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
+
+func isVendoredPackage(name string) bool {
+ var i int
+ if strings.HasPrefix(name, "vendor/") {
+ i += len("vendor/")
+ } else if j := strings.Index(name, "/vendor/"); j >= 0 {
+ i += len("/vendor/")
+ } else {
+ return false
+ }
+ return strings.Contains(name[i:], "/")
+}
+
+func PseudoVersion(major string, t time.Time, rev string) string {
+ if major == "" {
+ major = "v0"
+ }
+ return fmt.Sprintf("%s.0.0-%s-%s", major, t.UTC().Format("20060102150405"), rev)
+}
+
+var ErrNotPseudoVersion = errors.New("not a pseudo-version")
+
+/*
+func ParsePseudoVersion(repo Repo, version string) (rev string, err error) {
+ major := semver.Major(version)
+ if major == "" {
+ return "", ErrNotPseudoVersion
+ }
+ majorPrefix := major + ".0.0-"
+ if !strings.HasPrefix(version, majorPrefix) || !strings.Contains(version[len(majorPrefix):], "-") {
+ return "", ErrNotPseudoVersion
+ }
+ versionSuffix := version[len(majorPrefix):]
+ for i := 0; versionSuffix[i] != '-'; i++ {
+ c := versionSuffix[i]
+ if c < '0' || '9' < c {
+ return "", ErrNotPseudoVersion
+ }
+ }
+ rev = versionSuffix[strings.Index(versionSuffix, "-")+1:]
+ if rev == "" {
+ return "", ErrNotPseudoVersion
+ }
+ if proxyURL != "" {
+ return version, nil
+ }
+ fullRev, t, err := repo.CommitInfo(rev)
+ if err != nil {
+ return "", fmt.Errorf("unknown pseudo-version %s: loading %v: %v", version, rev, err)
+ }
+ v := PseudoVersion(major, t, repo.ShortRev(fullRev))
+ if v != version {
+ return "", fmt.Errorf("unknown pseudo-version %s: %v is %v", version, rev, v)
+ }
+ return fullRev, nil
+}
+*/
+
+var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
+
+func isPseudoVersion(v string) bool {
+ return pseudoVersionRE.MatchString(v)
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "archive/zip"
+ "internal/testenv"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "cmd/go/internal/modfetch/codehost"
+)
+
+func init() {
+ isTest = true
+}
+
+func TestMain(m *testing.M) {
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ dir, err := ioutil.TempDir("", "gitrepo-test-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ codehost.WorkRoot = dir
+ return m.Run()
+}
+
+var codeRepoTests = []struct {
+ path string
+ lookerr string
+ mpath string
+ rev string
+ err string
+ version string
+ name string
+ short string
+ time time.Time
+ gomod string
+ gomoderr string
+ zip []string
+ ziperr string
+}{
+ {
+ path: "github.com/rsc/vgotest1",
+ rev: "v0.0.0",
+ version: "v0.0.0",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ zip: []string{
+ "LICENSE",
+ "README.md",
+ "pkg/p.go",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ rev: "v1.0.0",
+ version: "v1.0.0",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ zip: []string{
+ "LICENSE",
+ "README.md",
+ "pkg/p.go",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "v2.0.0",
+ version: "v2.0.0",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ ziperr: "missing go.mod",
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ rev: "80d85",
+ version: "v0.0.0-20180219231006-80d85c5d4d17",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ zip: []string{
+ "LICENSE",
+ "README.md",
+ "pkg/p.go",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ rev: "mytag",
+ version: "v0.0.0-20180219231006-80d85c5d4d17",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ zip: []string{
+ "LICENSE",
+ "README.md",
+ "pkg/p.go",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "80d85",
+ version: "v2.0.0-20180219231006-80d85c5d4d17",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ gomoderr: "missing go.mod",
+ ziperr: "missing go.mod",
+ },
+ {
+ path: "github.com/rsc/vgotest1/v54321",
+ rev: "80d85",
+ version: "v54321.0.0-20180219231006-80d85c5d4d17",
+ name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
+ short: "80d85c5d4d17",
+ time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
+ ziperr: "missing go.mod",
+ },
+ {
+ path: "github.com/rsc/vgotest1/submod",
+ rev: "v1.0.0",
+ err: "unknown revision \"submod/v1.0.0\"",
+ },
+ {
+ path: "github.com/rsc/vgotest1/submod",
+ rev: "v1.0.3",
+ err: "unknown revision \"submod/v1.0.3\"",
+ },
+ {
+ path: "github.com/rsc/vgotest1/submod",
+ rev: "v1.0.4",
+ version: "v1.0.4",
+ name: "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6",
+ short: "8afe2b2efed9",
+ time: time.Date(2018, 2, 19, 23, 12, 7, 0, time.UTC),
+ gomod: "module \"github.com/vgotest1/submod\" // submod/go.mod\n",
+ zip: []string{
+ "go.mod",
+ "pkg/p.go",
+ "LICENSE",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ rev: "v1.1.0",
+ version: "v1.1.0",
+ name: "b769f2de407a4db81af9c5de0a06016d60d2ea09",
+ short: "b769f2de407a",
+ time: time.Date(2018, 2, 19, 23, 13, 36, 0, time.UTC),
+ gomod: "module \"github.com/rsc/vgotest1\" // root go.mod\nrequire \"github.com/rsc/vgotest1/submod\" v1.0.5\n",
+ zip: []string{
+ "LICENSE",
+ "README.md",
+ "go.mod",
+ "pkg/p.go",
+ },
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "v2.0.1",
+ version: "v2.0.1",
+ name: "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9",
+ short: "ea65f87c8f52",
+ time: time.Date(2018, 2, 19, 23, 14, 23, 0, time.UTC),
+ gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n",
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "v2.0.3",
+ version: "v2.0.3",
+ name: "f18795870fb14388a21ef3ebc1d75911c8694f31",
+ short: "f18795870fb1",
+ time: time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
+ gomoderr: "v2/go.mod has non-.../v2 module path",
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "v2.0.4",
+ version: "v2.0.4",
+ name: "1f863feb76bc7029b78b21c5375644838962f88d",
+ short: "1f863feb76bc",
+ time: time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
+ gomoderr: "both go.mod and v2/go.mod claim .../v2 module",
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ rev: "v2.0.5",
+ version: "v2.0.5",
+ name: "2f615117ce481c8efef46e0cc0b4b4dccfac8fea",
+ short: "2f615117ce48",
+ time: time.Date(2018, 2, 20, 0, 3, 59, 0, time.UTC),
+ gomod: "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n",
+ },
+ {
+ path: "go.googlesource.com/scratch",
+ rev: "0f302529858",
+ version: "v0.0.0-20180220024720-0f3025298580",
+ name: "0f30252985809011f026b5a2d5cf456e021623da",
+ short: "0f3025298580",
+ time: time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule go.googlesource.com/scratch\n",
+ },
+ {
+ path: "go.googlesource.com/scratch/rsc",
+ rev: "0f302529858",
+ version: "v0.0.0-20180220024720-0f3025298580",
+ name: "0f30252985809011f026b5a2d5cf456e021623da",
+ short: "0f3025298580",
+ time: time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+ gomod: "",
+ },
+ {
+ path: "go.googlesource.com/scratch/cbro",
+ rev: "0f302529858",
+ version: "v0.0.0-20180220024720-0f3025298580",
+ name: "0f30252985809011f026b5a2d5cf456e021623da",
+ short: "0f3025298580",
+ time: time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
+ gomoderr: "missing go.mod",
+ },
+ {
+ // redirect to github
+ path: "rsc.io/quote",
+ rev: "v1.0.0",
+ version: "v1.0.0",
+ name: "f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6",
+ short: "f488df80bcdb",
+ time: time.Date(2018, 2, 14, 0, 45, 20, 0, time.UTC),
+ gomod: "module \"rsc.io/quote\"\n",
+ },
+ {
+ // redirect to static hosting proxy
+ path: "swtch.com/testmod",
+ rev: "v1.0.0",
+ version: "v1.0.0",
+ name: "v1.0.0",
+ short: "v1.0.0",
+ time: time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC),
+ gomod: "module \"swtch.com/testmod\"\n",
+ },
+ {
+ // redirect to googlesource
+ path: "golang.org/x/text",
+ rev: "4e4a3210bb",
+ version: "v0.0.0-20180208041248-4e4a3210bb54",
+ name: "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1",
+ short: "4e4a3210bb54",
+ time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC),
+ },
+ {
+ path: "github.com/pkg/errors",
+ rev: "v0.8.0",
+ version: "v0.8.0",
+ name: "645ef00459ed84a119197bfb8d8205042c6df63d",
+ short: "645ef00459ed",
+ time: time.Date(2016, 9, 29, 1, 48, 1, 0, time.UTC),
+ },
+ {
+ // package in subdirectory - custom domain
+ path: "golang.org/x/net/context",
+ lookerr: "module root is \"golang.org/x/net\"",
+ },
+ {
+ // package in subdirectory - github
+ path: "github.com/rsc/quote/buggy",
+ rev: "c4d4236f",
+ version: "v0.0.0-20180214154420-c4d4236f9242",
+ name: "c4d4236f92427c64bfbcf1cc3f8142ab18f30b22",
+ short: "c4d4236f9242",
+ time: time.Date(2018, 2, 14, 15, 44, 20, 0, time.UTC),
+ gomoderr: "missing go.mod",
+ },
+ {
+ path: "gopkg.in/yaml.v2",
+ rev: "d670f940",
+ version: "v2.0.0-20180109114331-d670f9405373",
+ name: "d670f9405373e636a5a2765eea47fac0c9bc91a4",
+ short: "d670f9405373",
+ time: time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule gopkg.in/yaml.v2\n",
+ },
+ {
+ path: "gopkg.in/check.v1",
+ rev: "20d25e280405",
+ version: "v1.0.0-20161208181325-20d25e280405",
+ name: "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
+ short: "20d25e280405",
+ time: time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule gopkg.in/check.v1\n",
+ },
+ {
+ path: "gopkg.in/yaml.v2",
+ rev: "v2",
+ version: "v2.0.0-20180328195020-5420a8b6744d",
+ name: "5420a8b6744d3b0345ab293f6fcba19c978f1183",
+ short: "5420a8b6744d",
+ time: time.Date(2018, 3, 28, 19, 50, 20, 0, time.UTC),
+ gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
+ },
+ {
+ path: "vcs-test.golang.org/go/mod/gitrepo1",
+ rev: "master",
+ version: "v0.0.0-20180417194322-ede458df7cd0",
+ name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ short: "ede458df7cd0",
+ time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule vcs-test.golang.org/go/mod/gitrepo1\n",
+ },
+ {
+ path: "gopkg.in/natefinch/lumberjack.v2",
+ rev: "latest",
+ version: "v2.0.0-20170531160350-a96e63847dc3",
+ name: "a96e63847dc3c67d17befa69c303767e2f84e54f",
+ short: "a96e63847dc3",
+ time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+ },
+ {
+ path: "gopkg.in/natefinch/lumberjack.v2",
+ // This repo has a v2.1 tag.
+ // We only allow semver references to tags that are fully qualified, as in v2.1.0.
+ // Because we can't record v2.1.0 (the actual tag is v2.1), we record a pseudo-version
+ // instead, same as if the tag were any other non-version-looking string.
+ // We use a v2 pseudo-version here because of the .v2 in the path, not because
+ // of the v2 in the rev.
+ rev: "v2.1", // non-canonical semantic version turns into pseudo-version
+ version: "v2.0.0-20170531160350-a96e63847dc3",
+ name: "a96e63847dc3c67d17befa69c303767e2f84e54f",
+ short: "a96e63847dc3",
+ time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
+ gomod: "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+ },
+}
+
+func TestCodeRepo(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+ for _, tt := range codeRepoTests {
+ t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, func(t *testing.T) {
+ repo, err := Lookup(tt.path)
+ if err != nil {
+ if tt.lookerr != "" {
+ if err.Error() == tt.lookerr {
+ return
+ }
+ t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
+ }
+ t.Fatalf("Lookup(%q): %v", tt.path, err)
+ }
+ if tt.mpath == "" {
+ tt.mpath = tt.path
+ }
+ if mpath := repo.ModulePath(); mpath != tt.mpath {
+ t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
+ }
+ info, err := repo.Stat(tt.rev)
+ if err != nil {
+ if tt.err != "" {
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
+ }
+ return
+ }
+ t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
+ }
+ if tt.err != "" {
+ t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
+ }
+ if info.Version != tt.version {
+ t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
+ }
+ if info.Name != tt.name {
+ t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
+ }
+ if info.Short != tt.short {
+ t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
+ }
+ if !info.Time.Equal(tt.time) {
+ t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
+ }
+ if tt.gomod != "" || tt.gomoderr != "" {
+ data, err := repo.GoMod(tt.version)
+ if err != nil && tt.gomoderr == "" {
+ t.Errorf("repo.GoMod(%q): %v", tt.version, err)
+ } else if err != nil && tt.gomoderr != "" {
+ if err.Error() != tt.gomoderr {
+ t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomoderr)
+ }
+ } else if tt.gomoderr != "" {
+ t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomoderr)
+ } else if string(data) != tt.gomod {
+ t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
+ }
+ }
+ if tt.zip != nil || tt.ziperr != "" {
+ zipfile, err := repo.Zip(tt.version, tmpdir)
+ if err != nil {
+ if tt.ziperr != "" {
+ if err.Error() == tt.ziperr {
+ return
+ }
+ t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.ziperr)
+ }
+ t.Fatalf("repo.Zip(%q): %v", tt.version, err)
+ }
+ if tt.ziperr != "" {
+ t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.ziperr)
+ }
+ prefix := tt.path + "@" + tt.version + "/"
+ z, err := zip.OpenReader(zipfile)
+ if err != nil {
+ t.Fatalf("open zip %s: %v", zipfile, err)
+ }
+ var names []string
+ for _, file := range z.File {
+ if !strings.HasPrefix(file.Name, prefix) {
+ t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
+ continue
+ }
+ names = append(names, file.Name[len(prefix):])
+ }
+ z.Close()
+ if !reflect.DeepEqual(names, tt.zip) {
+ t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
+ }
+ }
+ })
+ }
+}
+
+var importTests = []struct {
+ path string
+ mpath string
+ err string
+}{
+ {
+ path: "golang.org/x/net/context",
+ mpath: "golang.org/x/net",
+ },
+ {
+ path: "github.com/rsc/quote/buggy",
+ mpath: "github.com/rsc/quote",
+ },
+ {
+ path: "golang.org/x/net",
+ mpath: "golang.org/x/net",
+ },
+ {
+ path: "github.com/rsc/quote",
+ mpath: "github.com/rsc/quote",
+ },
+ {
+ path: "golang.org/x/foo/bar",
+ err: "unknown module golang.org/x/foo/bar: no go-import tags",
+ },
+}
+
+func TestImport(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ for _, tt := range importTests {
+ t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
+ repo, info, err := Import(tt.path, nil)
+ if err != nil {
+ if tt.err != "" {
+ if err.Error() == tt.err {
+ return
+ }
+ t.Errorf("Import(%q): %v, want error %q", tt.path, err, tt.err)
+ }
+ t.Fatalf("Lookup(%q): %v", tt.path, err)
+ }
+ if mpath := repo.ModulePath(); mpath != tt.mpath {
+ t.Errorf("repo.ModulePath() = %q (%v), want %q", mpath, info.Version, tt.mpath)
+ }
+ })
+ }
+}
+
+var codeRepoVersionsTests = []struct {
+ path string
+ prefix string
+ versions []string
+}{
+ // TODO: Why do we allow a prefix here at all?
+ {
+ path: "github.com/rsc/vgotest1",
+ versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0"},
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ prefix: "v1.0",
+ versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"},
+ },
+ {
+ path: "github.com/rsc/vgotest1/v2",
+ versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"},
+ },
+ {
+ path: "swtch.com/testmod",
+ versions: []string{"v1.0.0", "v1.1.1"},
+ },
+ {
+ path: "gopkg.in/russross/blackfriday.v2",
+ versions: []string{"v2.0.0"},
+ },
+ {
+ path: "gopkg.in/natefinch/lumberjack.v2",
+ versions: []string{},
+ },
+}
+
+func TestCodeRepoVersions(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+ for _, tt := range codeRepoVersionsTests {
+ t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
+ repo, err := Lookup(tt.path)
+ if err != nil {
+ t.Fatalf("Lookup(%q): %v", tt.path, err)
+ }
+ list, err := repo.Versions(tt.prefix)
+ if err != nil {
+ t.Fatalf("Versions(%q): %v", tt.prefix, err)
+ }
+ if !reflect.DeepEqual(list, tt.versions) {
+ t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions)
+ }
+ })
+ }
+}
+
+var latestTests = []struct {
+ path string
+ version string
+ err string
+}{
+ {
+ path: "github.com/rsc/empty",
+ err: "no commits",
+ },
+ {
+ path: "github.com/rsc/vgotest1",
+ version: "v0.0.0-20180219223237-a08abb797a67",
+ },
+ {
+ path: "swtch.com/testmod",
+ version: "v1.1.1",
+ },
+}
+
+func TestLatest(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ tmpdir, err := ioutil.TempDir("", "vgo-modfetch-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+ for _, tt := range latestTests {
+ name := strings.Replace(tt.path, "/", "_", -1)
+ t.Run(name, func(t *testing.T) {
+ repo, err := Lookup(tt.path)
+ if err != nil {
+ t.Fatalf("Lookup(%q): %v", tt.path, err)
+ }
+ info, err := repo.Latest()
+ if err != nil {
+ if tt.err != "" {
+ if err.Error() == tt.err {
+ return
+ }
+ t.Fatalf("Latest(): %v, want %q", err, tt.err)
+ }
+ t.Fatalf("Latest(): %v", err)
+ }
+ if info.Version != tt.version {
+ t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
+ }
+ })
+ }
+}
+
+// fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags
+type fixedTagsRepo struct {
+ root string
+ tags []string
+}
+
+func (ch *fixedTagsRepo) Tags(string) ([]string, error) { return ch.tags, nil }
+func (ch *fixedTagsRepo) Root() string { return ch.root }
+func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error) { panic("not impl") }
+func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
+func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
+ panic("not impl")
+}
+func (ch *fixedTagsRepo) Stat(string) (*codehost.RevInfo, error) { panic("not impl") }
+
+func TestNonCanonicalSemver(t *testing.T) {
+ root := "golang.org/x/issue24476"
+ ch := &fixedTagsRepo{
+ root: root,
+ tags: []string{
+ "", "huh?", "1.0.1",
+ // what about "version 1 dot dogcow"?
+ "v1.🐕.🐄",
+ "v1", "v0.1",
+ // and one normal one that should pass through
+ "v1.0.1",
+ },
+ }
+
+ cr, err := newCodeRepo(ch, root)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ v, err := cr.Versions("")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(v) != 1 || v[0] != "v1.0.1" {
+ t.Fatal("unexpected versions returned:", v)
+ }
+}
+
+var modPathTests = []struct {
+ input []byte
+ expected string
+}{
+ {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+ {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+ {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+ {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+ {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"},
+ {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"},
+ {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"},
+ {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"},
+ {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"},
+ {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""},
+ {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"},
+ {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
+ {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
+ {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
+}
+
+func TestModPath(t *testing.T) {
+ for _, test := range modPathTests {
+ t.Run(string(test.input), func(t *testing.T) {
+ result := modPath(test.input)
+ if result != test.expected {
+ t.Fatalf("modPath(%s): %s, want %s", string(test.input), result, test.expected)
+ }
+ })
+ }
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "fmt"
+ "os"
+ "sort"
+ "strings"
+
+ "cmd/go/internal/modconv"
+ "cmd/go/internal/modfile"
+ "cmd/go/internal/semver"
+)
+
+// ConvertLegacyConfig converts legacy config to modfile.
+// The file argument is slash-delimited.
+func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
+ i := strings.LastIndex(file, "/")
+ j := -2
+ if i >= 0 {
+ j = strings.LastIndex(file[:i], "/")
+ }
+ convert := modconv.Converters[file[i+1:]]
+ if convert == nil && j != -2 {
+ convert = modconv.Converters[file[j+1:]]
+ }
+ if convert == nil {
+ return fmt.Errorf("unknown legacy config file %s", file)
+ }
+ require, err := convert(file, data)
+ if err != nil {
+ return fmt.Errorf("parsing %s: %v", file, err)
+ }
+
+ // Convert requirements block, which may use raw SHA1 hashes as versions,
+ // to valid semver requirement list, respecting major versions.
+ need := make(map[string]string)
+ for _, r := range require {
+ if r.Path == "" {
+ continue
+ }
+
+ // TODO: Something better here.
+ if strings.HasPrefix(r.Path, "github.com/") || strings.HasPrefix(r.Path, "golang.org/x/") {
+ f := strings.Split(r.Path, "/")
+ if len(f) > 3 {
+ r.Path = strings.Join(f[:3], "/")
+ }
+ }
+
+ repo, err := Lookup(r.Path)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "vgo: lookup %s: %v\n", r.Path, err)
+ continue
+ }
+ info, err := repo.Stat(r.Version)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "vgo: stat %s@%s: %v\n", r.Path, r.Version, err)
+ continue
+ }
+ path := repo.ModulePath()
+ need[path] = semver.Max(need[path], info.Version)
+ }
+
+ var paths []string
+ for path := range need {
+ paths = append(paths, path)
+ }
+ sort.Strings(paths)
+ for _, path := range paths {
+ f.AddRequire(path, need[path])
+ }
+
+ return nil
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "bytes"
+ "internal/testenv"
+ "strings"
+ "testing"
+
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/modconv"
+ "cmd/go/internal/modfile"
+)
+
+func TestConvertLegacyConfig(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+
+ if testing.Verbose() {
+ old := cfg.BuildX
+ defer func() {
+ cfg.BuildX = old
+ }()
+ cfg.BuildX = true
+ }
+
+ var tests = []struct {
+ path string
+ vers string
+ gomod string
+ }{
+ {
+ // Gopkg.lock parsing.
+ "github.com/golang/dep", "v0.4.0",
+ `module github.com/golang/dep
+
+ require (
+ github.com/Masterminds/semver v0.0.0-20170726230514-a93e51b5a57e
+ github.com/Masterminds/vcs v1.11.1
+ github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
+ github.com/boltdb/bolt v1.3.1
+ github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
+ github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
+ github.com/jmank88/nuts v0.3.0
+ github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
+ github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
+ github.com/pkg/errors v0.8.0
+ github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
+ golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
+ golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
+ golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
+ )`,
+ },
+
+ // TODO: https://github.com/docker/distribution uses vendor.conf
+
+ {
+ // Godeps.json parsing.
+ // TODO: Should v2.0.0 work here too?
+ "github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
+ `module github.com/docker/distribution
+
+ require (
+ github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
+ github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
+ github.com/Sirupsen/logrus v0.0.0-20150409230825-55eb11d21d2a
+ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
+ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
+ github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
+ github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
+ github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
+ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
+ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
+ github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
+ github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
+ github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
+ github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
+ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
+ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
+ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
+ golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
+ gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
+ gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
+ )`,
+ },
+
+ {
+ // golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
+ "github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
+ `module github.com/fishy/gcsbucket
+
+ require (
+ cloud.google.com/go v0.18.0
+ github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
+ github.com/golang/protobuf v1.0.0
+ github.com/googleapis/gax-go v0.0.0-20170915024731-317e0006254c
+ golang.org/x/net v0.0.0-20180216171745-136a25c244d3
+ golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
+ golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
+ google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
+ google.golang.org/appengine v1.0.0
+ google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
+ google.golang.org/grpc v1.10.0
+ )`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
+ f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want, err := f.Format()
+ if err != nil {
+ t.Fatal(err)
+ }
+ repo, err := Lookup(tt.path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := repo.GoMod(tt.vers)
+ if err != nil {
+ t.Fatal(err)
+ }
+ prefix := modconv.Prefix + "\n"
+ if !bytes.HasPrefix(out, []byte(prefix)) {
+ t.Fatalf("go.mod missing prefix %q:\n%s", prefix, out)
+ }
+ out = out[len(prefix):]
+ if !bytes.Equal(out, want) {
+ t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
+ }
+ })
+ }
+}
--- /dev/null
+// 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.
+
+// Support for custom domains.
+
+package modfetch
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io"
+ "net/url"
+ "os"
+ "strings"
+
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/gitrepo"
+)
+
+// metaImport represents the parsed <meta name="go-import"
+// content="prefix vcs reporoot" /> tags from HTML files.
+type metaImport struct {
+ Prefix, VCS, RepoRoot string
+}
+
+func lookupCustomDomain(path string) (Repo, error) {
+ dom := path
+ if i := strings.Index(dom, "/"); i >= 0 {
+ dom = dom[:i]
+ }
+ if !strings.Contains(dom, ".") {
+ return nil, fmt.Errorf("unknown module %s: not a domain name", path)
+ }
+ var body io.ReadCloser
+ err := webGetGoGet("https://"+path+"?go-get=1", &body)
+ if body != nil {
+ defer body.Close()
+ }
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "FindRepo: %v\n", err)
+ return nil, err
+ }
+ // Note: accepting a non-200 OK here, so people can serve a
+ // meta import in their http 404 page.
+ imports, err := parseMetaGoImports(body)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "findRepo: %v\n", err)
+ return nil, err
+ }
+ if len(imports) == 0 {
+ return nil, fmt.Errorf("unknown module %s: no go-import tags", path)
+ }
+
+ // First look for new module definition.
+ for _, imp := range imports {
+ if path == imp.Prefix || strings.HasPrefix(path, imp.Prefix+"/") {
+ if imp.VCS == "mod" {
+ u, err := url.Parse(imp.RepoRoot)
+ if err != nil {
+ return nil, fmt.Errorf("invalid module URL %q", imp.RepoRoot)
+ } else if u.Scheme != "https" {
+ // TODO: Allow -insecure flag as a build flag?
+ return nil, fmt.Errorf("invalid module URL %q: must be HTTPS", imp.RepoRoot)
+ }
+ return newProxyRepo(imp.RepoRoot, imp.Prefix), nil
+ }
+ }
+ }
+
+ // Fall back to redirections to known version control systems.
+ for _, imp := range imports {
+ if path == imp.Prefix {
+ if !strings.HasPrefix(imp.RepoRoot, "https://") {
+ // TODO: Allow -insecure flag as a build flag?
+ return nil, fmt.Errorf("invalid server URL %q: must be HTTPS", imp.RepoRoot)
+ }
+ if imp.VCS == "git" {
+ code, err := gitrepo.Repo(imp.RepoRoot, imp.Prefix)
+ if err != nil {
+ return nil, err
+ }
+ return newCodeRepo(code, path)
+ }
+ return nil, fmt.Errorf("unknown VCS, Repo: %s, %s", imp.VCS, imp.RepoRoot)
+ }
+ }
+
+ // Check for redirect to repo root.
+ for _, imp := range imports {
+ if strings.HasPrefix(path, imp.Prefix+"/") {
+ return nil, &ModuleSubdirError{imp.Prefix}
+ }
+ }
+
+ return nil, fmt.Errorf("unknown module %s: no matching go-import tags", path)
+}
+
+type ModuleSubdirError struct {
+ ModulePath string
+}
+
+func (e *ModuleSubdirError) Error() string {
+ return fmt.Sprintf("module root is %q", e.ModulePath)
+}
+
+type customPrefix struct {
+ codehost.Repo
+ root string
+}
+
+func (c *customPrefix) Root() string {
+ return c.root
+}
+
+// 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) (imports []metaImport, err error) {
+ d := xml.NewDecoder(r)
+ d.CharsetReader = charsetReader
+ d.Strict = false
+ var t xml.Token
+ for {
+ t, err = d.RawToken()
+ if err != nil {
+ if err == io.EOF || len(imports) > 0 {
+ err = nil
+ }
+ return
+ }
+ if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
+ return
+ }
+ if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
+ return
+ }
+ 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],
+ })
+ }
+ }
+}
+
+// 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 ""
+}
+
+// charsetReader returns a reader for the given charset. 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 "ascii":
+ return input, nil
+ default:
+ return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
+ }
+}
--- /dev/null
+// 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"
+ "strings"
+
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/gitrepo"
+)
+
+// Lookup returns the code repository enclosing the given module path,
+// which must begin with github.com/.
+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")
+ }
+ path = f[0] + "/" + f[1] + "/" + f[2]
+ return gitrepo.Repo("https://"+path, path)
+}
--- /dev/null
+// 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 gitrepo provides a Git-based implementation of codehost.Repo.
+package gitrepo
+
+import (
+ "archive/zip"
+ "bytes"
+ "cmd/go/internal/modfetch/codehost"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// Repo returns the code repository at the given Git remote reference.
+// The returned repo reports the given root as its module root.
+func Repo(remote, root string) (codehost.Repo, error) {
+ return newRepo(remote, root, false)
+}
+
+// LocalRepo is like Repo but accepts both Git remote references
+// and paths to repositories on the local file system.
+// The returned repo reports the given root as its module root.
+func LocalRepo(remote, root string) (codehost.Repo, error) {
+ return newRepo(remote, root, true)
+}
+
+const workDirType = "git2"
+
+func newRepo(remote, root string, localOK bool) (codehost.Repo, error) {
+ r := &repo{remote: remote, root: root, canArchive: true}
+ if strings.Contains(remote, "://") {
+ // This is a remote path.
+ dir, err := codehost.WorkDir(workDirType, r.remote)
+ if err != nil {
+ return nil, err
+ }
+ r.dir = dir
+ if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil {
+ if _, err := codehost.Run(dir, "git", "init", "--bare"); err != nil {
+ os.RemoveAll(dir)
+ return nil, err
+ }
+ // We could just say git fetch https://whatever later,
+ // but this lets us say git fetch origin instead, which
+ // is a little nicer. More importantly, using a named remote
+ // avoids a problem with Git LFS. See golang.org/issue/25605.
+ if _, err := codehost.Run(dir, "git", "remote", "add", "origin", r.remote); err != nil {
+ os.RemoveAll(dir)
+ return nil, err
+ }
+ r.remote = "origin"
+ }
+ } else {
+ // Local path.
+ // Disallow colon (not in ://) because sometimes
+ // that's rcp-style host:path syntax and sometimes it's not (c:\work).
+ // The go command has always insisted on URL syntax for ssh.
+ if strings.Contains(remote, ":") {
+ return nil, fmt.Errorf("git remote cannot use host:path syntax")
+ }
+ if !localOK {
+ return nil, fmt.Errorf("git remote must not be local directory")
+ }
+ r.local = true
+ info, err := os.Stat(remote)
+ if err != nil {
+ return nil, err
+ }
+ if !info.IsDir() {
+ return nil, fmt.Errorf("%s exists but is not a directory", remote)
+ }
+ r.dir = remote
+ }
+ return r, nil
+}
+
+type repo struct {
+ remote string
+ local bool
+ root string
+ dir string
+ canArchive bool
+
+ refsOnce sync.Once
+ refs map[string]string
+ refsErr error
+}
+
+func (r *repo) Root() string {
+ return r.root
+}
+
+// loadRefs loads heads and tags references from the remote into the map r.refs.
+// Should only be called as r.refsOnce.Do(r.loadRefs).
+func (r *repo) loadRefs() {
+ // The git protocol sends all known refs and ls-remote filters them on the client side,
+ // so we might as well record both heads and tags in one shot.
+ // Most of the time we only care about tags but sometimes we care about heads too.
+ out, err := codehost.Run(r.dir, "git", "ls-remote", "-q", r.remote)
+ if err != nil {
+ r.refsErr = err
+ return
+ }
+
+ r.refs = make(map[string]string)
+ for _, line := range strings.Split(string(out), "\n") {
+ f := strings.Fields(line)
+ if len(f) != 2 {
+ continue
+ }
+ if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
+ r.refs[f[1]] = f[0]
+ }
+ }
+ for ref, hash := range r.refs {
+ if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
+ r.refs[strings.TrimSuffix(ref, "^{}")] = hash
+ delete(r.refs, ref)
+ }
+ }
+}
+
+func (r *repo) Tags(prefix string) ([]string, error) {
+ r.refsOnce.Do(r.loadRefs)
+ if r.refsErr != nil {
+ return nil, r.refsErr
+ }
+
+ tags := []string{}
+ for ref := range r.refs {
+ if !strings.HasPrefix(ref, "refs/tags/") {
+ continue
+ }
+ tag := ref[len("refs/tags/"):]
+ if !strings.HasPrefix(tag, prefix) {
+ continue
+ }
+ tags = append(tags, tag)
+ }
+ sort.Strings(tags)
+ return tags, nil
+}
+
+func (r *repo) Latest() (*codehost.RevInfo, error) {
+ r.refsOnce.Do(r.loadRefs)
+ if r.refsErr != nil {
+ return nil, r.refsErr
+ }
+ if r.refs["HEAD"] == "" {
+ return nil, fmt.Errorf("no commits")
+ }
+ return r.Stat(r.refs["HEAD"])
+}
+
+// findRef finds some ref name for the given hash,
+// for use when the server requires giving a ref instead of a hash.
+// There may be multiple ref names for a given hash,
+// in which case this returns some name - it doesn't matter which.
+func (r *repo) findRef(hash string) (ref string, ok bool) {
+ r.refsOnce.Do(r.loadRefs)
+ for ref, h := range r.refs {
+ if h == hash {
+ return ref, true
+ }
+ }
+ return "", false
+}
+
+func unshallow(gitDir string) []string {
+ if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil {
+ return []string{"--unshallow"}
+ }
+ return []string{}
+}
+
+// statOrArchive tries to stat the given rev in the local repository,
+// or else it tries to obtain an archive at the rev with the given arguments,
+// or else it falls back to aggressive fetching and then a local stat.
+// The archive step is an optimization for servers that support it
+// (most do not, but maybe that will change), to let us minimize
+// the amount of code downloaded.
+func (r *repo) statOrArchive(rev string, archiveArgs ...string) (info *codehost.RevInfo, archive []byte, err error) {
+ // Do we have this rev?
+ r.refsOnce.Do(r.loadRefs)
+ var hash string
+ if k := "refs/tags/" + rev; r.refs[k] != "" {
+ hash = r.refs[k]
+ } else if k := "refs/heads/" + rev; r.refs[k] != "" {
+ hash = r.refs[k]
+ rev = hash
+ } else if rev == "HEAD" && r.refs["HEAD"] != "" {
+ hash = r.refs["HEAD"]
+ rev = hash
+ } else if len(rev) >= 5 && len(rev) <= 40 && codehost.AllHex(rev) {
+ hash = rev
+ } else {
+ return nil, nil, fmt.Errorf("unknown revision %q", rev)
+ }
+
+ out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash)
+ if err == nil {
+ hash = strings.TrimSpace(string(out))
+ goto Found
+ }
+
+ // We don't have the rev. Can we fetch it?
+ if r.local {
+ return nil, nil, fmt.Errorf("unknown revision %q", rev)
+ }
+
+ if r.canArchive {
+ // git archive with --remote requires a ref, not a hash.
+ // Proceed only if we know a ref for this hash.
+ if ref, ok := r.findRef(hash); ok {
+ out, err := codehost.Run(r.dir, "git", "archive", "--format=zip", "--remote="+r.remote, "--prefix=prefix/", ref, archiveArgs)
+ if err == nil {
+ return &codehost.RevInfo{Version: rev}, out, nil
+ }
+ if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
+ return nil, nil, fmt.Errorf("file not found")
+ }
+ if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("Operation not supported by protocol")) {
+ r.canArchive = false
+ }
+ }
+ }
+
+ // Maybe it's a prefix of a ref we know.
+ // Iterating through all the refs is faster than doing unnecessary fetches.
+ // This is not strictly correct, in that the short ref might be ambiguous
+ // in the git repo as a whole, but not ambiguous in the list of named refs,
+ // so that we will resolve it where the git server would not.
+ // But this check avoids great expense, and preferring a known ref does
+ // not seem like such a bad failure mode.
+ if len(hash) >= 5 && len(hash) < 40 {
+ var full string
+ for _, h := range r.refs {
+ if strings.HasPrefix(h, hash) {
+ if full != "" {
+ // Prefix is ambiguous even in the ref list!
+ full = ""
+ break
+ }
+ full = h
+ }
+ }
+ if full != "" {
+ hash = full
+ }
+ }
+
+ // Fetch it.
+ if len(hash) == 40 {
+ name := hash
+ if ref, ok := r.findRef(hash); ok {
+ name = ref
+ }
+ if _, err = codehost.Run(r.dir, "git", "fetch", "--depth=1", r.remote, name); err == nil {
+ goto Found
+ }
+ if !strings.Contains(err.Error(), "unadvertised object") && !strings.Contains(err.Error(), "no such remote ref") && !strings.Contains(err.Error(), "does not support shallow") {
+ return nil, nil, err
+ }
+ }
+
+ // It's a prefix, and we don't have a way to make the server resolve the prefix for us,
+ // or it's a full hash but also an unadvertised object.
+ // Download progressively more of the repo to look for it.
+
+ // Fetch the main branch (non-shallow).
+ if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), r.remote); err != nil {
+ return nil, nil, err
+ }
+ if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+ hash = strings.TrimSpace(string(out))
+ goto Found
+ }
+
+ // Fetch all tags (non-shallow).
+ if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", "--tags", r.remote); err != nil {
+ return nil, nil, err
+ }
+ if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+ hash = strings.TrimSpace(string(out))
+ goto Found
+ }
+
+ // Fetch all branches (non-shallow).
+ if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/heads/*:refs/heads/*"); err != nil {
+ return nil, nil, err
+ }
+ if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+ hash = strings.TrimSpace(string(out))
+ goto Found
+ }
+
+ // Fetch all refs (non-shallow).
+ if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/*:refs/*"); err != nil {
+ return nil, nil, err
+ }
+ if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
+ hash = strings.TrimSpace(string(out))
+ goto Found
+ }
+ return nil, nil, fmt.Errorf("cannot find hash %s", hash)
+Found:
+
+ if strings.HasPrefix(hash, rev) {
+ rev = hash
+ }
+
+ out, err = codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%ct", hash)
+ if err != nil {
+ return nil, nil, err
+ }
+ t, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)
+ if err != nil {
+ return nil, nil, fmt.Errorf("invalid time from git log: %q", out)
+ }
+
+ info = &codehost.RevInfo{
+ Name: hash,
+ Short: codehost.ShortenSHA1(hash),
+ Time: time.Unix(t, 0).UTC(),
+ Version: rev,
+ }
+ return info, nil, nil
+}
+
+func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
+ // If the server will give us a git archive, we can pull the
+ // commit ID and the commit time out of the archive.
+ // We want an archive as small as possible (for speed),
+ // but we have to specify a pattern that matches at least one file name.
+ // The pattern here matches README, .gitignore, .gitattributes,
+ // and go.mod (and some other incidental file names);
+ // hopefully most repos will have at least one of these.
+ info, archive, err := r.statOrArchive(rev, "[Rg.][Ego][A.i][Dmt][Miao][Edgt]*")
+ if err != nil {
+ return nil, err
+ }
+ if archive != nil {
+ return zip2info(archive, info.Version)
+ }
+ return info, nil
+}
+
+func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+ info, archive, err := r.statOrArchive(rev, file)
+ if err != nil {
+ return nil, err
+ }
+ if archive != nil {
+ return zip2file(archive, file, maxSize)
+ }
+ out, err := codehost.Run(r.dir, "git", "cat-file", "blob", info.Name+":"+file)
+ if err != nil {
+ return nil, fmt.Errorf("file not found")
+ }
+ return out, nil
+}
+
+func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+ // TODO: Use maxSize or drop it.
+ args := []string{}
+ if subdir != "" {
+ args = append(args, "--", subdir)
+ }
+ info, archive, err := r.statOrArchive(rev, args...)
+ if err != nil {
+ return nil, "", err
+ }
+ if archive == nil {
+ archive, err = codehost.Run(r.dir, "git", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
+ if err != nil {
+ if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
+ return nil, "", fmt.Errorf("file not found")
+ }
+ return nil, "", err
+ }
+ }
+
+ return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
+}
+
+func zip2info(archive []byte, rev string) (*codehost.RevInfo, error) {
+ r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+ if err != nil {
+ return nil, err
+ }
+ if r.Comment == "" {
+ return nil, fmt.Errorf("missing commit ID in git zip comment")
+ }
+ hash := r.Comment
+ if len(hash) != 40 || !codehost.AllHex(hash) {
+ return nil, fmt.Errorf("invalid commit ID in git zip comment")
+ }
+ if len(r.File) == 0 {
+ return nil, fmt.Errorf("git zip has no files")
+ }
+ info := &codehost.RevInfo{
+ Name: hash,
+ Short: codehost.ShortenSHA1(hash),
+ Time: r.File[0].Modified.UTC(),
+ Version: rev,
+ }
+ return info, nil
+}
+
+func zip2file(archive []byte, file string, maxSize int64) ([]byte, error) {
+ r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+ if err != nil {
+ return nil, err
+ }
+ for _, f := range r.File {
+ if f.Name != "prefix/"+file {
+ continue
+ }
+ rc, err := f.Open()
+ if err != nil {
+ return nil, err
+ }
+ defer rc.Close()
+ l := &io.LimitedReader{R: rc, N: maxSize + 1}
+ data, err := ioutil.ReadAll(l)
+ if err != nil {
+ return nil, err
+ }
+ if l.N <= 0 {
+ return nil, fmt.Errorf("file %s too large", file)
+ }
+ return data, nil
+ }
+ return nil, fmt.Errorf("incomplete git zip archive: cannot find %s", file)
+}
--- /dev/null
+// 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 gitrepo
+
+import (
+ "archive/zip"
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "cmd/go/internal/modfetch/codehost"
+)
+
+func TestMain(m *testing.M) {
+ os.Exit(testMain(m))
+}
+
+const gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1"
+
+// localGitRepo is like gitrepo1 but allows archive access.
+var localGitRepo string
+
+func testMain(m *testing.M) int {
+ if _, err := exec.LookPath("git"); err != nil {
+ fmt.Fprintln(os.Stderr, "skipping because git binary not found")
+ fmt.Println("PASS")
+ return 0
+ }
+
+ dir, err := ioutil.TempDir("", "gitrepo-test-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ codehost.WorkRoot = dir
+
+ if testenv.HasExternalNetwork() && testenv.HasExec() {
+ // Clone gitrepo1 into a local directory.
+ // If we use a file:// URL to access the local directory,
+ // then git starts up all the usual protocol machinery,
+ // which will let us test remote git archive invocations.
+ localGitRepo = filepath.Join(dir, "gitrepo2")
+ if _, err := codehost.Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
+ log.Fatal(err)
+ }
+ if _, err := codehost.Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ return m.Run()
+}
+
+func testRepo(remote string) (codehost.Repo, error) {
+ if remote == "localGitRepo" {
+ remote = "file://" + filepath.ToSlash(localGitRepo)
+ }
+ // Re ?root: nothing should care about the second argument,
+ // so use a string that will be distinctive if it does show up.
+ return LocalRepo(remote, "?root")
+}
+
+var tagsTests = []struct {
+ repo string
+ prefix string
+ tags []string
+}{
+ {gitrepo1, "xxx", []string{}},
+ {gitrepo1, "", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}},
+ {gitrepo1, "v", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}},
+ {gitrepo1, "v1", []string{"v1.2.3", "v1.2.4-annotated"}},
+ {gitrepo1, "2", []string{}},
+}
+
+func TestTags(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+ testenv.MustHaveExec(t)
+
+ for _, tt := range tagsTests {
+ f := func(t *testing.T) {
+ r, err := testRepo(tt.repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tags, err := r.Tags(tt.prefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(tags, tt.tags) {
+ t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags)
+ }
+ }
+ t.Run(tt.repo+"/"+tt.prefix, f)
+ if tt.repo == gitrepo1 {
+ tt.repo = "localGitRepo"
+ t.Run(tt.repo+"/"+tt.prefix, f)
+ }
+ }
+}
+
+var latestTests = []struct {
+ repo string
+ info *codehost.RevInfo
+}{
+ {
+ gitrepo1,
+ &codehost.RevInfo{
+ Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Short: "ede458df7cd0",
+ Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ },
+ },
+}
+
+func TestLatest(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+ testenv.MustHaveExec(t)
+
+ for _, tt := range latestTests {
+ f := func(t *testing.T) {
+ r, err := testRepo(tt.repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ info, err := r.Latest()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if *info != *tt.info {
+ t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
+ }
+ }
+ t.Run(tt.repo, f)
+ if tt.repo == gitrepo1 {
+ tt.repo = "localGitRepo"
+ t.Run(tt.repo, f)
+ }
+ }
+}
+
+var readFileTests = []struct {
+ repo string
+ rev string
+ file string
+ err string
+ data string
+}{
+ {
+ repo: gitrepo1,
+ rev: "HEAD",
+ file: "README",
+ data: "",
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2",
+ file: "another.txt",
+ data: "another\n",
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2.3.4",
+ file: "another.txt",
+ err: "file not found",
+ },
+}
+
+func TestReadFile(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+ testenv.MustHaveExec(t)
+
+ for _, tt := range readFileTests {
+ f := func(t *testing.T) {
+ r, err := testRepo(tt.repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ data, err := r.ReadFile(tt.rev, tt.file, 100)
+ if err != nil {
+ if tt.err == "" {
+ t.Fatalf("ReadFile: unexpected error %v", err)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("ReadFile: wrong error %q, want %q", err, tt.err)
+ }
+ if len(data) != 0 {
+ t.Errorf("ReadFile: non-empty data %q with error %v", data, err)
+ }
+ return
+ }
+ if tt.err != "" {
+ t.Fatalf("ReadFile: no error, wanted %v", tt.err)
+ }
+ if string(data) != tt.data {
+ t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data)
+ }
+ }
+ t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+ if tt.repo == gitrepo1 {
+ tt.repo = "localGitRepo"
+ t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+ }
+ }
+}
+
+var readZipTests = []struct {
+ repo string
+ rev string
+ subdir string
+ actualSubdir string
+ err string
+ files map[string]uint64
+}{
+ {
+ repo: gitrepo1,
+ rev: "v2.3.4",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/README": 0,
+ "prefix/v2": 3,
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/README": 0,
+ "prefix/v2": 3,
+ "prefix/another.txt": 8,
+ "prefix/foo.txt": 13,
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v3",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ "prefix/README": 0,
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v3",
+ subdir: "v3/sub/dir",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v3",
+ subdir: "v3/sub",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "aaaaaaaaab",
+ subdir: "",
+ err: "cannot find hash",
+ },
+ {
+ repo: "https://github.com/rsc/vgotest1",
+ rev: "submod/v1.0.4",
+ subdir: "submod",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/submod/": 0,
+ "prefix/submod/go.mod": 53,
+ "prefix/submod/pkg/": 0,
+ "prefix/submod/pkg/p.go": 31,
+ },
+ },
+}
+
+type zipFile struct {
+ name string
+ size int64
+}
+
+func TestReadZip(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+ testenv.MustHaveExec(t)
+
+ for _, tt := range readZipTests {
+ f := func(t *testing.T) {
+ r, err := testRepo(tt.repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rc, actualSubdir, err := r.ReadZip(tt.rev, tt.subdir, 100000)
+ if err != nil {
+ if tt.err == "" {
+ t.Fatalf("ReadZip: unexpected error %v", err)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("ReadZip: wrong error %q, want %q", err, tt.err)
+ }
+ if rc != nil {
+ t.Errorf("ReadZip: non-nil io.ReadCloser with error %v", err)
+ }
+ return
+ }
+ defer rc.Close()
+ if tt.err != "" {
+ t.Fatalf("ReadZip: no error, wanted %v", tt.err)
+ }
+ if actualSubdir != tt.actualSubdir {
+ t.Fatalf("ReadZip: actualSubdir = %q, want %q", actualSubdir, tt.actualSubdir)
+ }
+ zipdata, err := ioutil.ReadAll(rc)
+ if err != nil {
+ t.Fatal(err)
+ }
+ z, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata)))
+ if err != nil {
+ t.Fatalf("ReadZip: cannot read zip file: %v", err)
+ }
+ have := make(map[string]bool)
+ for _, f := range z.File {
+ size, ok := tt.files[f.Name]
+ if !ok {
+ t.Errorf("ReadZip: unexpected file %s", f.Name)
+ continue
+ }
+ have[f.Name] = true
+ if f.UncompressedSize64 != size {
+ t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size)
+ }
+ }
+ for name := range tt.files {
+ if !have[name] {
+ t.Errorf("ReadZip: missing file %s", name)
+ }
+ }
+ }
+ t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+ if tt.repo == gitrepo1 {
+ tt.repo = "localGitRepo"
+ t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+ }
+ }
+}
+
+var statTests = []struct {
+ repo string
+ rev string
+ err string
+ info *codehost.RevInfo
+}{
+ {
+ repo: gitrepo1,
+ rev: "HEAD",
+ info: &codehost.RevInfo{
+ Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Short: "ede458df7cd0",
+ Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2", // branch
+ info: &codehost.RevInfo{
+ Name: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
+ Short: "9d02800338b8",
+ Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
+ Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2.3.4", // badly-named branch (semver should be a tag)
+ info: &codehost.RevInfo{
+ Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+ Short: "76a00fb249b7",
+ Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+ Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0)
+ info: &codehost.RevInfo{
+ Name: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
+ Short: "76a00fb249b7",
+ Version: "v2.3",
+ Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v1.2.3", // tag
+ info: &codehost.RevInfo{
+ Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Short: "ede458df7cd0",
+ Version: "v1.2.3",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "ede458df", // hash prefix in refs
+ info: &codehost.RevInfo{
+ Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Short: "ede458df7cd0",
+ Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "97f6aa59", // hash prefix not in refs
+ info: &codehost.RevInfo{
+ Name: "97f6aa59c81c623494825b43d39e445566e429a4",
+ Short: "97f6aa59c81c",
+ Version: "97f6aa59c81c623494825b43d39e445566e429a4",
+ Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
+ info: &codehost.RevInfo{
+ Name: "ede458df7cd0fdca520df19a33158086a8a68e81",
+ Short: "ede458df7cd0",
+ Version: "v1.2.4-annotated",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitrepo1,
+ rev: "aaaaaaaaab",
+ err: "cannot find hash",
+ },
+}
+
+func TestStat(t *testing.T) {
+ testenv.MustHaveExternalNetwork(t)
+ testenv.MustHaveExec(t)
+
+ for _, tt := range statTests {
+ f := func(t *testing.T) {
+ r, err := testRepo(tt.repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ info, err := r.Stat(tt.rev)
+ if err != nil {
+ if tt.err == "" {
+ t.Fatalf("Stat: unexpected error %v", err)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("Stat: wrong error %q, want %q", err, tt.err)
+ }
+ if info != nil {
+ t.Errorf("Stat: non-nil info with error %q", err)
+ }
+ return
+ }
+ if *info != *tt.info {
+ t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
+ }
+ }
+ t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+ if tt.repo == gitrepo1 {
+ tt.repo = "localGitRepo"
+ t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+ }
+ }
+}
--- /dev/null
+// 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 googlesource
+
+import (
+ "fmt"
+ "strings"
+
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/gitrepo"
+)
+
+func Lookup(path string) (codehost.Repo, error) {
+ i := strings.Index(path, "/")
+ if i+1 == len(path) || !strings.HasSuffix(path[:i+1], ".googlesource.com/") {
+ return nil, fmt.Errorf("not *.googlesource.com/*")
+ }
+ j := strings.Index(path[i+1:], "/")
+ if j >= 0 {
+ path = path[:i+1+j]
+ }
+ return gitrepo.Repo("https://"+path, path)
+}
--- /dev/null
+// 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.
+
+// TODO: Figure out what gopkg.in should do.
+
+package modfetch
+
+import (
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/gitrepo"
+ "cmd/go/internal/modfile"
+ "fmt"
+)
+
+func gopkginLookup(path string) (codehost.Repo, error) {
+ root, _, _, _, ok := modfile.ParseGopkgIn(path)
+ if !ok {
+ return nil, fmt.Errorf("invalid gopkg.in/ path: %q", path)
+ }
+ return gitrepo.Repo("https://"+root, root)
+}
--- /dev/null
+// 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.
+
+// +build cmd_go_bootstrap
+
+package modfetch
+
+import (
+ "fmt"
+ "io"
+)
+
+func webGetGoGet(url string, body *io.ReadCloser) error {
+ return fmt.Errorf("no network in go_bootstrap")
+}
+
+func webGetBytes(url string, body *[]byte) error {
+ return fmt.Errorf("no network in go_bootstrap")
+}
+
+func webGetBody(url string, body *io.ReadCloser) error {
+ return fmt.Errorf("no network in go_bootstrap")
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "strings"
+ "time"
+
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/semver"
+)
+
+var proxyURL = os.Getenv("GOPROXY")
+
+func lookupProxy(path string) (Repo, error) {
+ u, err := url.Parse(proxyURL)
+ if err != nil || u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "file" {
+ // Don't echo $GOPROXY back in case it has user:password in it (sigh).
+ return nil, fmt.Errorf("invalid $GOPROXY setting")
+ }
+ return newProxyRepo(u.String(), path), nil
+}
+
+type proxyRepo struct {
+ url string
+ path string
+}
+
+func newProxyRepo(baseURL, path string) Repo {
+ return &proxyRepo{strings.TrimSuffix(baseURL, "/") + "/" + pathEscape(path), path}
+}
+
+func (p *proxyRepo) ModulePath() string {
+ return p.path
+}
+
+func (p *proxyRepo) Versions(prefix string) ([]string, error) {
+ var data []byte
+ err := webGetBytes(p.url+"/@v/list", &data)
+ if err != nil {
+ return nil, err
+ }
+ var list []string
+ for _, line := range strings.Split(string(data), "\n") {
+ f := strings.Fields(line)
+ if len(f) >= 1 && semver.IsValid(f[0]) && strings.HasPrefix(f[0], prefix) {
+ list = append(list, f[0])
+ }
+ }
+ return list, nil
+}
+
+func (p *proxyRepo) latest() (*RevInfo, error) {
+ var data []byte
+ err := webGetBytes(p.url+"/@v/list", &data)
+ if err != nil {
+ return nil, err
+ }
+ var best time.Time
+ var bestVersion string
+ for _, line := range strings.Split(string(data), "\n") {
+ f := strings.Fields(line)
+ if len(f) >= 2 && semver.IsValid(f[0]) {
+ ft, err := time.Parse(time.RFC3339, f[1])
+ if err == nil && best.Before(ft) {
+ best = ft
+ bestVersion = f[0]
+ }
+ }
+ }
+ if bestVersion == "" {
+ return nil, fmt.Errorf("no commits")
+ }
+ info := &RevInfo{
+ Version: bestVersion,
+ Name: bestVersion,
+ Short: bestVersion,
+ Time: best,
+ }
+ return info, nil
+}
+
+func (p *proxyRepo) Stat(rev string) (*RevInfo, error) {
+ var data []byte
+ err := webGetBytes(p.url+"/@v/"+pathEscape(rev)+".info", &data)
+ if err != nil {
+ return nil, err
+ }
+ info := new(RevInfo)
+ if err := json.Unmarshal(data, info); err != nil {
+ return nil, err
+ }
+ return info, nil
+}
+
+func (p *proxyRepo) Latest() (*RevInfo, error) {
+ var data []byte
+ u := p.url + "/@latest"
+ err := webGetBytes(u, &data)
+ if err != nil {
+ // TODO return err if not 404
+ return p.latest()
+ }
+ info := new(RevInfo)
+ if err := json.Unmarshal(data, info); err != nil {
+ return nil, err
+ }
+ return info, nil
+}
+
+func (p *proxyRepo) GoMod(version string) ([]byte, error) {
+ var data []byte
+ err := webGetBytes(p.url+"/@v/"+pathEscape(version)+".mod", &data)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+}
+
+func (p *proxyRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
+ var body io.ReadCloser
+ err = webGetBody(p.url+"/@v/"+pathEscape(version)+".zip", &body)
+ if err != nil {
+ return "", err
+ }
+ defer body.Close()
+
+ // Spool to local file.
+ f, err := ioutil.TempFile(tmpdir, "vgo-proxy-download-")
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+ maxSize := int64(codehost.MaxZipFile)
+ lr := &io.LimitedReader{R: body, N: maxSize + 1}
+ if _, err := io.Copy(f, lr); err != nil {
+ os.Remove(f.Name())
+ return "", err
+ }
+ if lr.N <= 0 {
+ os.Remove(f.Name())
+ return "", fmt.Errorf("downloaded zip file too large")
+ }
+ if err := f.Close(); err != nil {
+ os.Remove(f.Name())
+ return "", err
+ }
+ return f.Name(), nil
+}
+
+// pathEscape escapes s so it can be used in a path.
+// That is, it escapes things like ? and # (which really shouldn't appear anyway).
+// It does not escape / to %2F: our REST API is designed so that / can be left as is.
+func pathEscape(s string) string {
+ return strings.Replace(url.PathEscape(s), "%2F", "/", -1)
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+ "fmt"
+ "strings"
+)
+
+// Query looks up a revision of a given module given a version query string.
+// The module must be a complete module path.
+// The version must take one of the following forms:
+//
+// - the literal string "latest", denoting the latest available tagged version
+// - v1.2.3, a semantic version string
+// - v1 or v1.2, an abbreviated semantic version string completed by adding zeroes (v1.0.0 or v1.2.0);
+// - >v1.2.3, denoting the earliest available version after v1.2.3
+// - <v1.2.3, denoting the latest available version before v1.2.3
+// - an RFC 3339 time stamp, denoting the latest available version at that time
+// - a Unix time expressed as seconds since 1970, denoting the latest available version at that time
+// - a repository commit identifier, denoting that version
+//
+// The time stamps can be followed by an optional @branch suffix to limit the
+// result to revisions on a particular branch name.
+//
+func Query(path, vers string, allowed func(module.Version) bool) (*RevInfo, error) {
+ repo, err := Lookup(path)
+ if err != nil {
+ return nil, err
+ }
+
+ if strings.HasPrefix(vers, "v") && semver.IsValid(vers) {
+ // TODO: This turns query for "v2" into Stat "v2.0.0",
+ // but probably it should allow checking for a branch named "v2".
+ vers = semver.Canonical(vers)
+ if allowed != nil && !allowed(module.Version{Path: path, Version: vers}) {
+ return nil, fmt.Errorf("%s@%s excluded", path, vers)
+ }
+ return repo.Stat(vers)
+ }
+ if strings.HasPrefix(vers, ">") || strings.HasPrefix(vers, "<") || vers == "latest" {
+ var op string
+ if vers != "latest" {
+ if !semver.IsValid(vers[1:]) {
+ return nil, fmt.Errorf("invalid semantic version in range %s", vers)
+ }
+ op, vers = vers[:1], vers[1:]
+ }
+ versions, err := repo.Versions("")
+ if err != nil {
+ return nil, err
+ }
+ if len(versions) == 0 && vers == "latest" {
+ return repo.Latest()
+ }
+ if vers == "latest" {
+ for i := len(versions) - 1; i >= 0; i-- {
+ if allowed == nil || allowed(module.Version{Path: path, Version: versions[i]}) {
+ return repo.Stat(versions[i])
+ }
+ }
+ } else if op == "<" {
+ for i := len(versions) - 1; i >= 0; i-- {
+ if semver.Compare(versions[i], vers) < 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
+ return repo.Stat(versions[i])
+ }
+ }
+ } else {
+ for i := 0; i < len(versions); i++ {
+ if semver.Compare(versions[i], vers) > 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
+ return repo.Stat(versions[i])
+ }
+ }
+ }
+ return nil, fmt.Errorf("no matching versions for %s%s", op, vers)
+ }
+ // TODO: Time queries, maybe.
+
+ return repo.Stat(vers)
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "errors"
+ pathpkg "path"
+ "sort"
+ "strings"
+ "time"
+
+ "cmd/go/internal/modfetch/bitbucket"
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfetch/github"
+ "cmd/go/internal/modfetch/googlesource"
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+)
+
+// A Repo represents a repository storing all versions of a single module.
+type Repo interface {
+ // ModulePath returns the module path.
+ ModulePath() string
+
+ // Versions lists all known versions with the given prefix.
+ // Pseudo-versions are not included.
+ // Versions should be returned sorted in semver order
+ // (implementations can use SortVersions).
+ Versions(prefix string) (tags []string, err error)
+
+ // Stat returns information about the revision rev.
+ // A revision can be any identifier known to the underlying service:
+ // commit hash, branch, tag, and so on.
+ Stat(rev string) (*RevInfo, error)
+
+ // Latest returns the latest revision on the default branch,
+ // whatever that means in the underlying source code repository.
+ // It is only used when there are no tagged versions.
+ Latest() (*RevInfo, error)
+
+ // GoMod returns the go.mod file for the given version.
+ GoMod(version string) (data []byte, err error)
+
+ // Zip downloads a zip file for the given version
+ // to a new file in a given temporary directory.
+ // It returns the name of the new file.
+ // The caller should remove the file when finished with it.
+ Zip(version, tmpdir string) (tmpfile string, err error)
+}
+
+// A Rev describes a single revision in a module repository.
+type RevInfo struct {
+ Version string // version string
+ Name string // complete ID in underlying repository
+ Short string // shortened ID, for use in pseudo-version
+ Time time.Time // commit time
+}
+
+// Lookup returns the module with the given module path.
+func Lookup(path string) (Repo, error) {
+ if proxyURL != "" {
+ return lookupProxy(path)
+ }
+ if code, err := lookupCodeHost(path, false); err != errNotHosted {
+ if err != nil {
+ return nil, err
+ }
+ return newCodeRepo(code, path)
+ }
+ return lookupCustomDomain(path)
+}
+
+func Import(path string, allowed func(module.Version) bool) (Repo, *RevInfo, error) {
+ try := func(path string) (Repo, *RevInfo, error) {
+ r, err := Lookup(path)
+ if err != nil {
+ return nil, nil, err
+ }
+ info, err := Query(path, "latest", allowed)
+ if err != nil {
+ return nil, nil, err
+ }
+ _, err = r.GoMod(info.Version)
+ if err != nil {
+ return nil, nil, err
+ }
+ return r, info, nil
+ }
+
+ var firstErr error
+ for {
+ r, info, err := try(path)
+ if err == nil {
+ return r, info, nil
+ }
+ if firstErr == nil {
+ firstErr = err
+ }
+ p := pathpkg.Dir(path)
+ if p == "." {
+ break
+ }
+ path = p
+ }
+ return nil, nil, firstErr
+}
+
+var errNotHosted = errors.New("not hosted")
+
+var isTest bool
+
+func lookupCodeHost(path string, customDomain bool) (codehost.Repo, error) {
+ switch {
+ case strings.HasPrefix(path, "github.com/"):
+ return github.Lookup(path)
+ case strings.HasPrefix(path, "bitbucket.org/"):
+ return bitbucket.Lookup(path)
+ case customDomain && strings.HasSuffix(path[:strings.Index(path, "/")+1], ".googlesource.com/") ||
+ isTest && strings.HasPrefix(path, "go.googlesource.com/scratch"):
+ return googlesource.Lookup(path)
+ case strings.HasPrefix(path, "gopkg.in/"):
+ return gopkginLookup(path)
+ }
+ return nil, errNotHosted
+}
+
+func SortVersions(list []string) {
+ sort.Slice(list, func(i, j int) bool {
+ cmp := semver.Compare(list[i], list[j])
+ if cmp != 0 {
+ return cmp < 0
+ }
+ return list[i] < list[j]
+ })
+}
--- /dev/null
+// 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 modfetch
+
+import (
+ "archive/zip"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "cmd/go/internal/modfetch/codehost"
+)
+
+func Unzip(dir, zipfile, prefix string, maxSize int64) error {
+ if maxSize == 0 {
+ maxSize = codehost.MaxZipFile
+ }
+
+ // Directory can exist, but must be empty.
+ // except maybe
+ files, _ := ioutil.ReadDir(dir)
+ if len(files) > 0 {
+ return fmt.Errorf("target directory %v exists and is not empty", dir)
+ }
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ return err
+ }
+
+ f, err := os.Open(zipfile)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ info, err := f.Stat()
+ if err != nil {
+ return err
+ }
+
+ z, err := zip.NewReader(f, info.Size())
+ if err != nil {
+ return fmt.Errorf("unzip %v: %s", zipfile, err)
+ }
+
+ // Check total size.
+ var size int64
+ for _, zf := range z.File {
+ if !strings.HasPrefix(zf.Name, prefix) {
+ return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name)
+ }
+ if strings.HasSuffix(zf.Name, "/") {
+ continue
+ }
+ s := int64(zf.UncompressedSize64)
+ if s < 0 || maxSize-size < s {
+ return fmt.Errorf("unzip %v: content too large", zipfile)
+ }
+ size += s
+ }
+
+ // Unzip, enforcing sizes checked earlier.
+ for _, zf := range z.File {
+ if strings.HasSuffix(zf.Name, "/") {
+ continue
+ }
+ dst := filepath.Join(dir, zf.Name[len(prefix):])
+ if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
+ return err
+ }
+ w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444)
+ if err != nil {
+ return fmt.Errorf("unzip %v: %v", zipfile, err)
+ }
+ r, err := zf.Open()
+ if err != nil {
+ r.Close()
+ w.Close()
+ return fmt.Errorf("unzip %v: %v", zipfile, err)
+ }
+ lr := &io.LimitedReader{R: r, N: int64(zf.UncompressedSize64) + 1}
+ _, err = io.Copy(w, lr)
+ r.Close()
+ if err != nil {
+ w.Close()
+ return fmt.Errorf("unzip %v: %v", zipfile, err)
+ }
+ if err := w.Close(); err != nil {
+ return fmt.Errorf("unzip %v: %v", zipfile, err)
+ }
+ if lr.N <= 0 {
+ return fmt.Errorf("unzip %v: content too large", zipfile)
+ }
+ }
+
+ return nil
+}
--- /dev/null
+// 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.
+
+// +build !cmd_go_bootstrap
+
+package modfetch
+
+import (
+ "io"
+
+ web "cmd/go/internal/web2"
+)
+
+// webGetGoGet fetches a go-get=1 URL and returns the body in *body.
+// It allows non-200 responses, as usual for these URLs.
+func webGetGoGet(url string, body *io.ReadCloser) error {
+ return web.Get(url, web.Non200OK(), web.Body(body))
+}
+
+// webGetBytes returns the body returned by an HTTP GET, as a []byte.
+// It insists on a 200 response.
+func webGetBytes(url string, body *[]byte) error {
+ return web.Get(url, web.ReadAllBody(body))
+}
+
+// webGetBody returns the body returned by an HTTP GET, as a io.ReadCloser.
+// It insists on a 200 response.
+func webGetBody(url string, body *io.ReadCloser) error {
+ return web.Get(url, web.Body(body))
+}
--- /dev/null
+// 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.
+
+// TODO: Figure out what gopkg.in should do.
+
+package modfile
+
+import "strings"
+
+// ParseGopkgIn splits gopkg.in import paths into their constituent parts
+func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
+ if !strings.HasPrefix(path, "gopkg.in/") {
+ return
+ }
+ f := strings.Split(path, "/")
+ if len(f) >= 2 {
+ if elem, v, ok := dotV(f[1]); ok {
+ root = strings.Join(f[:2], "/")
+ repo = "github.com/go-" + elem + "/" + elem
+ major = v
+ subdir = strings.Join(f[2:], "/")
+ return root, repo, major, subdir, true
+ }
+ }
+ if len(f) >= 3 {
+ if elem, v, ok := dotV(f[2]); ok {
+ root = strings.Join(f[:3], "/")
+ repo = "github.com/" + f[1] + "/" + elem
+ major = v
+ subdir = strings.Join(f[3:], "/")
+ return root, repo, major, subdir, true
+ }
+ }
+ return
+}
+
+func dotV(name string) (elem, v string, ok bool) {
+ i := len(name) - 1
+ for i >= 0 && '0' <= name[i] && name[i] <= '9' {
+ i--
+ }
+ if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 {
+ return "", "", false
+ }
+ return name[:i-1], name[i:], true
+}
--- /dev/null
+// 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.
+
+// Module file printer.
+
+package modfile
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+func Format(f *FileSyntax) []byte {
+ pr := &printer{}
+ pr.file(f)
+ return pr.Bytes()
+}
+
+// A printer collects the state during printing of a file or expression.
+type printer struct {
+ bytes.Buffer // output buffer
+ comment []Comment // pending end-of-line comments
+ margin int // left margin (indent), a number of tabs
+}
+
+// printf prints to the buffer.
+func (p *printer) printf(format string, args ...interface{}) {
+ fmt.Fprintf(p, format, args...)
+}
+
+// indent returns the position on the current line, in bytes, 0-indexed.
+func (p *printer) indent() int {
+ b := p.Bytes()
+ n := 0
+ for n < len(b) && b[len(b)-1-n] != '\n' {
+ n++
+ }
+ return n
+}
+
+// newline ends the current line, flushing end-of-line comments.
+func (p *printer) newline() {
+ if len(p.comment) > 0 {
+ p.printf(" ")
+ for i, com := range p.comment {
+ if i > 0 {
+ p.trim()
+ p.printf("\n")
+ for i := 0; i < p.margin; i++ {
+ p.printf("\t")
+ }
+ }
+ p.printf("%s", strings.TrimSpace(com.Token))
+ }
+ p.comment = p.comment[:0]
+ }
+
+ p.trim()
+ p.printf("\n")
+ for i := 0; i < p.margin; i++ {
+ p.printf("\t")
+ }
+}
+
+// trim removes trailing spaces and tabs from the current line.
+func (p *printer) trim() {
+ // Remove trailing spaces and tabs from line we're about to end.
+ b := p.Bytes()
+ n := len(b)
+ for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') {
+ n--
+ }
+ p.Truncate(n)
+}
+
+// file formats the given file into the print buffer.
+func (p *printer) file(f *FileSyntax) {
+ for _, com := range f.Before {
+ p.printf("%s", strings.TrimSpace(com.Token))
+ p.newline()
+ }
+
+ for i, stmt := range f.Stmt {
+ switch x := stmt.(type) {
+ case *CommentBlock:
+ // comments already handled
+ p.expr(x)
+
+ default:
+ p.expr(x)
+ p.newline()
+ }
+
+ for _, com := range stmt.Comment().After {
+ p.printf("%s", strings.TrimSpace(com.Token))
+ p.newline()
+ }
+
+ if i+1 < len(f.Stmt) {
+ p.newline()
+ }
+ }
+}
+
+func (p *printer) expr(x Expr) {
+ // Emit line-comments preceding this expression.
+ if before := x.Comment().Before; len(before) > 0 {
+ // Want to print a line comment.
+ // Line comments must be at the current margin.
+ p.trim()
+ if p.indent() > 0 {
+ // There's other text on the line. Start a new line.
+ p.printf("\n")
+ }
+ // Re-indent to margin.
+ for i := 0; i < p.margin; i++ {
+ p.printf("\t")
+ }
+ for _, com := range before {
+ p.printf("%s", strings.TrimSpace(com.Token))
+ p.newline()
+ }
+ }
+
+ switch x := x.(type) {
+ default:
+ panic(fmt.Errorf("printer: unexpected type %T", x))
+
+ case *CommentBlock:
+ // done
+
+ case *LParen:
+ p.printf("(")
+ case *RParen:
+ p.printf(")")
+
+ case *Line:
+ sep := ""
+ for _, tok := range x.Token {
+ p.printf("%s%s", sep, tok)
+ sep = " "
+ }
+
+ case *LineBlock:
+ for _, tok := range x.Token {
+ p.printf("%s ", tok)
+ }
+ p.expr(&x.LParen)
+ p.margin++
+ for _, l := range x.Line {
+ p.newline()
+ p.expr(l)
+ }
+ p.margin--
+ p.newline()
+ p.expr(&x.RParen)
+ }
+
+ // Queue end-of-line comments for printing when we
+ // reach the end of the line.
+ p.comment = append(p.comment, x.Comment().Suffix...)
+}
--- /dev/null
+// 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.
+
+// Module file parser.
+// This is a simplified copy of Google's buildifier parser.
+
+package modfile
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A Position describes the position between two bytes of input.
+type Position struct {
+ Line int // line in input (starting at 1)
+ LineRune int // rune in line (starting at 1)
+ Byte int // byte in input (starting at 0)
+}
+
+// add returns the position at the end of s, assuming it starts at p.
+func (p Position) add(s string) Position {
+ p.Byte += len(s)
+ if n := strings.Count(s, "\n"); n > 0 {
+ p.Line += n
+ s = s[strings.LastIndex(s, "\n")+1:]
+ p.LineRune = 1
+ }
+ p.LineRune += utf8.RuneCountInString(s)
+ return p
+}
+
+// An Expr represents an input element.
+type Expr interface {
+ // Span returns the start and end position of the expression,
+ // excluding leading or trailing comments.
+ Span() (start, end Position)
+
+ // Comment returns the comments attached to the expression.
+ // This method would normally be named 'Comments' but that
+ // would interfere with embedding a type of the same name.
+ Comment() *Comments
+}
+
+// A Comment represents a single // comment.
+type Comment struct {
+ Start Position
+ Token string // without trailing newline
+ Suffix bool // an end of line (not whole line) comment
+}
+
+// Comments collects the comments associated with an expression.
+type Comments struct {
+ Before []Comment // whole-line comments before this expression
+ Suffix []Comment // end-of-line comments after this expression
+
+ // For top-level expressions only, After lists whole-line
+ // comments following the expression.
+ After []Comment
+}
+
+// Comment returns the receiver. This isn't useful by itself, but
+// a Comments struct is embedded into all the expression
+// implementation types, and this gives each of those a Comment
+// method to satisfy the Expr interface.
+func (c *Comments) Comment() *Comments {
+ return c
+}
+
+// A FileSyntax represents an entire go.mod file.
+type FileSyntax struct {
+ Name string // file path
+ Comments
+ Stmt []Expr
+}
+
+func (x *FileSyntax) Span() (start, end Position) {
+ if len(x.Stmt) == 0 {
+ return
+ }
+ start, _ = x.Stmt[0].Span()
+ _, end = x.Stmt[len(x.Stmt)-1].Span()
+ return start, end
+}
+
+// A CommentBlock represents a top-level block of comments separate
+// from any rule.
+type CommentBlock struct {
+ Comments
+ Start Position
+}
+
+func (x *CommentBlock) Span() (start, end Position) {
+ return x.Start, x.Start
+}
+
+// A Line is a single line of tokens.
+type Line struct {
+ Comments
+ Start Position
+ Token []string
+ End Position
+}
+
+func (x *Line) Span() (start, end Position) {
+ return x.Start, x.End
+}
+
+// A LineBlock is a factored block of lines, like
+//
+// require (
+// "x"
+// "y"
+// )
+//
+type LineBlock struct {
+ Comments
+ Start Position
+ LParen LParen
+ Token []string
+ Line []*Line
+ RParen RParen
+}
+
+func (x *LineBlock) Span() (start, end Position) {
+ return x.Start, x.RParen.Pos.add(")")
+}
+
+// An LParen represents the beginning of a parenthesized line block.
+// It is a place to store suffix comments.
+type LParen struct {
+ Comments
+ Pos Position
+}
+
+func (x *LParen) Span() (start, end Position) {
+ return x.Pos, x.Pos.add(")")
+}
+
+// An RParen represents the end of a parenthesized line block.
+// It is a place to store whole-line (before) comments.
+type RParen struct {
+ Comments
+ Pos Position
+}
+
+func (x *RParen) Span() (start, end Position) {
+ return x.Pos, x.Pos.add(")")
+}
+
+// An input represents a single input file being parsed.
+type input struct {
+ // Lexing state.
+ filename string // name of input file, for errors
+ complete []byte // entire input
+ remaining []byte // remaining input
+ token []byte // token being scanned
+ lastToken string // most recently returned token, for error messages
+ pos Position // current input position
+ comments []Comment // accumulated comments
+ endRule int // position of end of current rule
+
+ // Parser state.
+ file *FileSyntax // returned top-level syntax tree
+ parseError error // error encountered during parsing
+
+ // Comment assignment state.
+ pre []Expr // all expressions, in preorder traversal
+ post []Expr // all expressions, in postorder traversal
+}
+
+func newInput(filename string, data []byte) *input {
+ return &input{
+ filename: filename,
+ complete: data,
+ remaining: data,
+ pos: Position{Line: 1, LineRune: 1, Byte: 0},
+ }
+}
+
+// parse parses the input file.
+func parse(file string, data []byte) (f *FileSyntax, err error) {
+ in := newInput(file, data)
+ // The parser panics for both routine errors like syntax errors
+ // and for programmer bugs like array index errors.
+ // Turn both into error returns. Catching bug panics is
+ // especially important when processing many files.
+ defer func() {
+ if e := recover(); e != nil {
+ if e == in.parseError {
+ err = in.parseError
+ } else {
+ err = fmt.Errorf("%s:%d:%d: internal error: %v", in.filename, in.pos.Line, in.pos.LineRune, e)
+ }
+ }
+ }()
+
+ // Invoke the parser.
+ in.parseFile()
+ if in.parseError != nil {
+ return nil, in.parseError
+ }
+ in.file.Name = in.filename
+
+ // Assign comments to nearby syntax.
+ in.assignComments()
+
+ return in.file, nil
+}
+
+// Error is called to report an error.
+// The reason s is often "syntax error".
+// Error does not return: it panics.
+func (in *input) Error(s string) {
+ if s == "syntax error" && in.lastToken != "" {
+ s += " near " + in.lastToken
+ }
+ in.parseError = fmt.Errorf("%s:%d:%d: %v", in.filename, in.pos.Line, in.pos.LineRune, s)
+ panic(in.parseError)
+}
+
+// eof reports whether the input has reached end of file.
+func (in *input) eof() bool {
+ return len(in.remaining) == 0
+}
+
+// peekRune returns the next rune in the input without consuming it.
+func (in *input) peekRune() int {
+ if len(in.remaining) == 0 {
+ return 0
+ }
+ r, _ := utf8.DecodeRune(in.remaining)
+ return int(r)
+}
+
+// peekPrefix reports whether the remaining input begins with the given prefix.
+func (in *input) peekPrefix(prefix string) bool {
+ // This is like bytes.HasPrefix(in.remaining, []byte(prefix))
+ // but without the allocation of the []byte copy of prefix.
+ for i := 0; i < len(prefix); i++ {
+ if i >= len(in.remaining) || in.remaining[i] != prefix[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// readRune consumes and returns the next rune in the input.
+func (in *input) readRune() int {
+ if len(in.remaining) == 0 {
+ in.Error("internal lexer error: readRune at EOF")
+ }
+ r, size := utf8.DecodeRune(in.remaining)
+ in.remaining = in.remaining[size:]
+ if r == '\n' {
+ in.pos.Line++
+ in.pos.LineRune = 1
+ } else {
+ in.pos.LineRune++
+ }
+ in.pos.Byte += size
+ return int(r)
+}
+
+type symType struct {
+ pos Position
+ endPos Position
+ text string
+}
+
+// startToken marks the beginning of the next input token.
+// It must be followed by a call to endToken, once the token has
+// been consumed using readRune.
+func (in *input) startToken(sym *symType) {
+ in.token = in.remaining
+ sym.text = ""
+ sym.pos = in.pos
+}
+
+// endToken marks the end of an input token.
+// It records the actual token string in sym.text if the caller
+// has not done that already.
+func (in *input) endToken(sym *symType) {
+ if sym.text == "" {
+ tok := string(in.token[:len(in.token)-len(in.remaining)])
+ sym.text = tok
+ in.lastToken = sym.text
+ }
+ sym.endPos = in.pos
+}
+
+// lex is called from the parser to obtain the next input token.
+// It returns the token value (either a rune like '+' or a symbolic token _FOR)
+// and sets val to the data associated with the token.
+// For all our input tokens, the associated data is
+// val.Pos (the position where the token begins)
+// and val.Token (the input string corresponding to the token).
+func (in *input) lex(sym *symType) int {
+ // Skip past spaces, stopping at non-space or EOF.
+ countNL := 0 // number of newlines we've skipped past
+ for !in.eof() {
+ // Skip over spaces. Count newlines so we can give the parser
+ // information about where top-level blank lines are,
+ // for top-level comment assignment.
+ c := in.peekRune()
+ if c == ' ' || c == '\t' || c == '\r' {
+ in.readRune()
+ continue
+ }
+
+ // Comment runs to end of line.
+ if in.peekPrefix("//") {
+ in.startToken(sym)
+
+ // Is this comment the only thing on its line?
+ // Find the last \n before this // and see if it's all
+ // spaces from there to here.
+ i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n"))
+ suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0
+ in.readRune()
+ in.readRune()
+
+ // Consume comment.
+ for len(in.remaining) > 0 && in.readRune() != '\n' {
+ }
+ in.endToken(sym)
+
+ sym.text = strings.TrimRight(sym.text, "\n")
+ in.lastToken = "comment"
+
+ // If we are at top level (not in a statement), hand the comment to
+ // the parser as a _COMMENT token. The grammar is written
+ // to handle top-level comments itself.
+ if !suffix {
+ // Not in a statement. Tell parser about top-level comment.
+ return _COMMENT
+ }
+
+ // Otherwise, save comment for later attachment to syntax tree.
+ if countNL > 1 {
+ in.comments = append(in.comments, Comment{sym.pos, "", false})
+ }
+ in.comments = append(in.comments, Comment{sym.pos, sym.text, suffix})
+ countNL = 1
+ return _EOL
+ }
+
+ if in.peekPrefix("/*") {
+ in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
+ }
+
+ // Found non-space non-comment.
+ break
+ }
+
+ // Found the beginning of the next token.
+ in.startToken(sym)
+ defer in.endToken(sym)
+
+ // End of file.
+ if in.eof() {
+ in.lastToken = "EOF"
+ return _EOF
+ }
+
+ // Punctuation tokens.
+ switch c := in.peekRune(); c {
+ case '\n':
+ in.readRune()
+ return c
+
+ case '(':
+ in.readRune()
+ return c
+
+ case ')':
+ in.readRune()
+ return c
+
+ case '"', '`': // quoted string
+ quote := c
+ in.readRune()
+ for {
+ if in.eof() {
+ in.pos = sym.pos
+ in.Error("unexpected EOF in string")
+ }
+ if in.peekRune() == '\n' {
+ in.Error("unexpected newline in string")
+ }
+ c := in.readRune()
+ if c == quote {
+ break
+ }
+ if c == '\\' && quote != '`' {
+ if in.eof() {
+ in.pos = sym.pos
+ in.Error("unexpected EOF in string")
+ }
+ in.readRune()
+ }
+ }
+ in.endToken(sym)
+ return _STRING
+ }
+
+ // Checked all punctuation. Must be identifier token.
+ if c := in.peekRune(); !isIdent(c) {
+ in.Error(fmt.Sprintf("unexpected input character %#q", c))
+ }
+
+ // Scan over identifier.
+ for isIdent(in.peekRune()) {
+ if in.peekPrefix("//") {
+ break
+ }
+ if in.peekPrefix("/*") {
+ in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)"))
+ }
+ in.readRune()
+ }
+ return _IDENT
+}
+
+// isIdent reports whether c is an identifier rune.
+// We treat nearly all runes as identifier runes.
+func isIdent(c int) bool {
+ return c != 0 && !unicode.IsSpace(rune(c))
+}
+
+// Comment assignment.
+// We build two lists of all subexpressions, preorder and postorder.
+// The preorder list is ordered by start location, with outer expressions first.
+// The postorder list is ordered by end location, with outer expressions last.
+// We use the preorder list to assign each whole-line comment to the syntax
+// immediately following it, and we use the postorder list to assign each
+// end-of-line comment to the syntax immediately preceding it.
+
+// order walks the expression adding it and its subexpressions to the
+// preorder and postorder lists.
+func (in *input) order(x Expr) {
+ if x != nil {
+ in.pre = append(in.pre, x)
+ }
+ switch x := x.(type) {
+ default:
+ panic(fmt.Errorf("order: unexpected type %T", x))
+ case nil:
+ // nothing
+ case *LParen, *RParen:
+ // nothing
+ case *CommentBlock:
+ // nothing
+ case *Line:
+ // nothing
+ case *FileSyntax:
+ for _, stmt := range x.Stmt {
+ in.order(stmt)
+ }
+ case *LineBlock:
+ in.order(&x.LParen)
+ for _, l := range x.Line {
+ in.order(l)
+ }
+ in.order(&x.RParen)
+ }
+ if x != nil {
+ in.post = append(in.post, x)
+ }
+}
+
+// assignComments attaches comments to nearby syntax.
+func (in *input) assignComments() {
+ const debug = false
+
+ // Generate preorder and postorder lists.
+ in.order(in.file)
+
+ // Split into whole-line comments and suffix comments.
+ var line, suffix []Comment
+ for _, com := range in.comments {
+ if com.Suffix {
+ suffix = append(suffix, com)
+ } else {
+ line = append(line, com)
+ }
+ }
+
+ if debug {
+ for _, c := range line {
+ fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
+ }
+ }
+
+ // Assign line comments to syntax immediately following.
+ for _, x := range in.pre {
+ start, _ := x.Span()
+ if debug {
+ fmt.Printf("pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte)
+ }
+ xcom := x.Comment()
+ for len(line) > 0 && start.Byte >= line[0].Start.Byte {
+ if debug {
+ fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte)
+ }
+ xcom.Before = append(xcom.Before, line[0])
+ line = line[1:]
+ }
+ }
+
+ // Remaining line comments go at end of file.
+ in.file.After = append(in.file.After, line...)
+
+ if debug {
+ for _, c := range suffix {
+ fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
+ }
+ }
+
+ // Assign suffix comments to syntax immediately before.
+ for i := len(in.post) - 1; i >= 0; i-- {
+ x := in.post[i]
+
+ start, end := x.Span()
+ if debug {
+ fmt.Printf("post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
+ }
+
+ // Do not assign suffix comments to end of line block or whole file.
+ // Instead assign them to the last element inside.
+ switch x.(type) {
+ case *FileSyntax:
+ continue
+ }
+
+ // Do not assign suffix comments to something that starts
+ // on an earlier line, so that in
+ //
+ // x ( y
+ // z ) // comment
+ //
+ // we assign the comment to z and not to x ( ... ).
+ if start.Line != end.Line {
+ continue
+ }
+ xcom := x.Comment()
+ for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte {
+ if debug {
+ fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte)
+ }
+ xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1])
+ suffix = suffix[:len(suffix)-1]
+ }
+ }
+
+ // We assigned suffix comments in reverse.
+ // If multiple suffix comments were appended to the same
+ // expression node, they are now in reverse. Fix that.
+ for _, x := range in.post {
+ reverseComments(x.Comment().Suffix)
+ }
+
+ // Remaining suffix comments go at beginning of file.
+ in.file.Before = append(in.file.Before, suffix...)
+}
+
+// reverseComments reverses the []Comment list.
+func reverseComments(list []Comment) {
+ for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
+ list[i], list[j] = list[j], list[i]
+ }
+}
+
+func (in *input) parseFile() {
+ in.file = new(FileSyntax)
+ var sym symType
+ var cb *CommentBlock
+ for {
+ tok := in.lex(&sym)
+ switch tok {
+ case '\n':
+ if cb != nil {
+ in.file.Stmt = append(in.file.Stmt, cb)
+ cb = nil
+ }
+ case _COMMENT:
+ if cb == nil {
+ cb = &CommentBlock{Start: sym.pos}
+ }
+ com := cb.Comment()
+ com.Before = append(com.Before, Comment{Start: sym.pos, Token: sym.text})
+ case _EOF:
+ if cb != nil {
+ in.file.Stmt = append(in.file.Stmt, cb)
+ }
+ return
+ default:
+ in.parseStmt(&sym)
+ if cb != nil {
+ in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before
+ cb = nil
+ }
+ }
+ }
+}
+
+func (in *input) parseStmt(sym *symType) {
+ start := sym.pos
+ end := sym.endPos
+ token := []string{sym.text}
+ for {
+ tok := in.lex(sym)
+ switch tok {
+ case '\n', _EOF, _EOL:
+ in.file.Stmt = append(in.file.Stmt, &Line{
+ Start: start,
+ Token: token,
+ End: end,
+ })
+ return
+ case '(':
+ in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, token, sym))
+ return
+ default:
+ token = append(token, sym.text)
+ end = sym.endPos
+ }
+ }
+}
+
+func (in *input) parseLineBlock(start Position, token []string, sym *symType) *LineBlock {
+ x := &LineBlock{
+ Start: start,
+ Token: token,
+ LParen: LParen{Pos: sym.pos},
+ }
+ var comments []Comment
+ for {
+ tok := in.lex(sym)
+ switch tok {
+ case _EOL:
+ // ignore
+ case '\n':
+ if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" {
+ comments = append(comments, Comment{})
+ }
+ case _COMMENT:
+ comments = append(comments, Comment{Start: sym.pos, Token: sym.text})
+ case _EOF:
+ in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune))
+ case ')':
+ x.RParen.Before = comments
+ x.RParen.Pos = sym.pos
+ tok = in.lex(sym)
+ if tok != '\n' && tok != _EOF && tok != _EOL {
+ in.Error("syntax error (expected newline after closing paren)")
+ }
+ return x
+ default:
+ l := in.parseLine(sym)
+ x.Line = append(x.Line, l)
+ l.Comment().Before = comments
+ comments = nil
+ }
+ }
+}
+
+func (in *input) parseLine(sym *symType) *Line {
+ start := sym.pos
+ end := sym.endPos
+ token := []string{sym.text}
+ for {
+ tok := in.lex(sym)
+ switch tok {
+ case '\n', _EOF, _EOL:
+ return &Line{
+ Start: start,
+ Token: token,
+ End: end,
+ }
+ default:
+ token = append(token, sym.text)
+ end = sym.endPos
+ }
+ }
+}
+
+const (
+ _EOF = -(1 + iota)
+ _EOL
+ _IDENT
+ _STRING
+ _COMMENT
+)
--- /dev/null
+// 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 modfile
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+// exists reports whether the named file exists.
+func exists(name string) bool {
+ _, err := os.Stat(name)
+ return err == nil
+}
+
+// Test that reading and then writing the golden files
+// does not change their output.
+func TestPrintGolden(t *testing.T) {
+ outs, err := filepath.Glob("testdata/*.golden")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, out := range outs {
+ testPrint(t, out, out)
+ }
+}
+
+// testPrint is a helper for testing the printer.
+// It reads the file named in, reformats it, and compares
+// the result to the file named out.
+func testPrint(t *testing.T, in, out string) {
+ data, err := ioutil.ReadFile(in)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ golden, err := ioutil.ReadFile(out)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ base := "testdata/" + filepath.Base(in)
+ f, err := parse(in, data)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ ndata := Format(f)
+
+ if !bytes.Equal(ndata, golden) {
+ t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
+ tdiff(t, string(golden), string(ndata))
+ return
+ }
+}
+
+// Test that when files in the testdata directory are parsed
+// and printed and parsed again, we get the same parse tree
+// both times.
+func TestPrintParse(t *testing.T) {
+ outs, err := filepath.Glob("testdata/*")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, out := range outs {
+ data, err := ioutil.ReadFile(out)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ base := "testdata/" + filepath.Base(out)
+ f, err := parse(base, data)
+ if err != nil {
+ t.Errorf("parsing original: %v", err)
+ continue
+ }
+
+ ndata := Format(f)
+ f2, err := parse(base, ndata)
+ if err != nil {
+ t.Errorf("parsing reformatted: %v", err)
+ continue
+ }
+
+ eq := eqchecker{file: base}
+ if err := eq.check(f, f2); err != nil {
+ t.Errorf("not equal (parse/Format/parse): %v", err)
+ }
+
+ pf1, err := Parse(base, data, nil)
+ if err != nil {
+ switch base {
+ case "testdata/replace2.in", "testdata/gopkg.in.golden":
+ t.Errorf("should parse %v: %v", base, err)
+ }
+ }
+ if err == nil {
+ pf2, err := Parse(base, ndata, nil)
+ if err != nil {
+ t.Errorf("Parsing reformatted: %v", err)
+ continue
+ }
+ eq := eqchecker{file: base}
+ if err := eq.check(pf1, pf2); err != nil {
+ t.Errorf("not equal (parse/Format/Parse): %v", err)
+ }
+
+ ndata2, err := pf1.Format()
+ if err != nil {
+ t.Errorf("reformat: %v", err)
+ }
+ pf3, err := Parse(base, ndata2, nil)
+ if err != nil {
+ t.Errorf("Parsing reformatted2: %v", err)
+ continue
+ }
+ eq = eqchecker{file: base}
+ if err := eq.check(pf1, pf3); err != nil {
+ t.Errorf("not equal (Parse/Format/Parse): %v", err)
+ }
+ ndata = ndata2
+ }
+
+ if strings.HasSuffix(out, ".in") {
+ golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden")
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if !bytes.Equal(ndata, golden) {
+ t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base)
+ tdiff(t, string(golden), string(ndata))
+ return
+ }
+ }
+ }
+}
+
+// An eqchecker holds state for checking the equality of two parse trees.
+type eqchecker struct {
+ file string
+ pos Position
+}
+
+// errorf returns an error described by the printf-style format and arguments,
+// inserting the current file position before the error text.
+func (eq *eqchecker) errorf(format string, args ...interface{}) error {
+ return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line,
+ fmt.Sprintf(format, args...))
+}
+
+// check checks that v and w represent the same parse tree.
+// If not, it returns an error describing the first difference.
+func (eq *eqchecker) check(v, w interface{}) error {
+ return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w))
+}
+
+var (
+ posType = reflect.TypeOf(Position{})
+ commentsType = reflect.TypeOf(Comments{})
+)
+
+// checkValue checks that v and w represent the same parse tree.
+// If not, it returns an error describing the first difference.
+func (eq *eqchecker) checkValue(v, w reflect.Value) error {
+ // inner returns the innermost expression for v.
+ // if v is a non-nil interface value, it returns the concrete
+ // value in the interface.
+ inner := func(v reflect.Value) reflect.Value {
+ for {
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ v = v.Elem()
+ continue
+ }
+ break
+ }
+ return v
+ }
+
+ v = inner(v)
+ w = inner(w)
+ if v.Kind() == reflect.Invalid && w.Kind() == reflect.Invalid {
+ return nil
+ }
+ if v.Kind() == reflect.Invalid {
+ return eq.errorf("nil interface became %s", w.Type())
+ }
+ if w.Kind() == reflect.Invalid {
+ return eq.errorf("%s became nil interface", v.Type())
+ }
+
+ if v.Type() != w.Type() {
+ return eq.errorf("%s became %s", v.Type(), w.Type())
+ }
+
+ if p, ok := v.Interface().(Expr); ok {
+ eq.pos, _ = p.Span()
+ }
+
+ switch v.Kind() {
+ default:
+ return eq.errorf("unexpected type %s", v.Type())
+
+ case reflect.Bool, reflect.Int, reflect.String:
+ vi := v.Interface()
+ wi := w.Interface()
+ if vi != wi {
+ return eq.errorf("%v became %v", vi, wi)
+ }
+
+ case reflect.Slice:
+ vl := v.Len()
+ wl := w.Len()
+ for i := 0; i < vl || i < wl; i++ {
+ if i >= vl {
+ return eq.errorf("unexpected %s", w.Index(i).Type())
+ }
+ if i >= wl {
+ return eq.errorf("missing %s", v.Index(i).Type())
+ }
+ if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil {
+ return err
+ }
+ }
+
+ case reflect.Struct:
+ // Fields in struct must match.
+ t := v.Type()
+ n := t.NumField()
+ for i := 0; i < n; i++ {
+ tf := t.Field(i)
+ switch {
+ default:
+ if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil {
+ return err
+ }
+
+ case tf.Type == posType: // ignore positions
+ case tf.Type == commentsType: // ignore comment assignment
+ }
+ }
+
+ case reflect.Ptr, reflect.Interface:
+ if v.IsNil() != w.IsNil() {
+ if v.IsNil() {
+ return eq.errorf("unexpected %s", w.Elem().Type())
+ }
+ return eq.errorf("missing %s", v.Elem().Type())
+ }
+ if err := eq.checkValue(v.Elem(), w.Elem()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// diff returns the output of running diff on b1 and b2.
+func diff(b1, b2 []byte) (data []byte, err error) {
+ f1, err := ioutil.TempFile("", "testdiff")
+ if err != nil {
+ return nil, err
+ }
+ defer os.Remove(f1.Name())
+ defer f1.Close()
+
+ f2, err := ioutil.TempFile("", "testdiff")
+ if err != nil {
+ return nil, err
+ }
+ defer os.Remove(f2.Name())
+ defer f2.Close()
+
+ f1.Write(b1)
+ f2.Write(b2)
+
+ data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
+ if len(data) > 0 {
+ // diff exits with a non-zero status when the files don't match.
+ // Ignore that failure as long as we get output.
+ err = nil
+ }
+ return
+}
+
+// tdiff logs the diff output to t.Error.
+func tdiff(t *testing.T, a, b string) {
+ data, err := diff([]byte(a), []byte(b))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ t.Error(string(data))
+}
--- /dev/null
+// 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 modfile
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+)
+
+type File struct {
+ Module *Module
+ Require []*Require
+ Exclude []*Exclude
+ Replace []*Replace
+
+ Syntax *FileSyntax
+}
+
+type Module struct {
+ Mod module.Version
+ Major string
+}
+
+type Require struct {
+ Mod module.Version
+ Syntax *Line
+}
+
+type Exclude struct {
+ Mod module.Version
+ Syntax *Line
+}
+
+type Replace struct {
+ Old module.Version
+ New module.Version
+
+ Syntax *Line
+}
+
+func (f *File) AddModuleStmt(path string) {
+ f.Module = &Module{
+ Mod: module.Version{Path: path},
+ }
+ if f.Syntax == nil {
+ f.Syntax = new(FileSyntax)
+ }
+ f.Syntax.Stmt = append(f.Syntax.Stmt, &Line{
+ Token: []string{"module", AutoQuote(path)},
+ })
+}
+
+func (f *File) AddComment(text string) {
+ if f.Syntax == nil {
+ f.Syntax = new(FileSyntax)
+ }
+ f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{
+ Comments: Comments{
+ Before: []Comment{
+ {
+ Token: text,
+ },
+ },
+ },
+ })
+}
+
+type VersionFixer func(path, version string) (string, error)
+
+func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
+ fs, err := parse(file, data)
+ if err != nil {
+ return nil, err
+ }
+ f := &File{
+ Syntax: fs,
+ }
+
+ var errs bytes.Buffer
+ for _, x := range fs.Stmt {
+ switch x := x.(type) {
+ case *Line:
+ f.add(&errs, x, x.Token[0], x.Token[1:], fix)
+
+ case *LineBlock:
+ if len(x.Token) > 1 {
+ fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
+ continue
+ }
+ switch x.Token[0] {
+ default:
+ fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
+ continue
+ case "module", "require", "exclude", "replace":
+ for _, l := range x.Line {
+ f.add(&errs, l, x.Token[0], l.Token, fix)
+ }
+ }
+ }
+ }
+
+ if errs.Len() > 0 {
+ return nil, errors.New(strings.TrimRight(errs.String(), "\n"))
+ }
+ return f, nil
+}
+
+func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer) {
+ // TODO: We should pass in a flag saying whether this module is a dependency.
+ // If so, we should ignore all unknown directives and not attempt to parse
+ // replace and exclude either. They don't matter, and it will work better for
+ // forward compatibility if we can depend on modules that have local changes.
+
+ // TODO: For the target module (not dependencies), maybe we should
+ // relax the semver requirement and rewrite the file with updated info
+ // after resolving any versions. That would let people type commit hashes
+ // or tags or branch names, and then vgo would fix them.
+
+ switch verb {
+ default:
+ fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
+ case "module":
+ if f.Module != nil {
+ fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
+ return
+ }
+ f.Module = new(Module)
+ if len(args) != 1 {
+
+ fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line)
+ return
+ }
+ s, err := parseString(&args[0])
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ f.Module.Mod = module.Version{Path: s}
+ case "require", "exclude":
+ if len(args) != 2 {
+ fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3\n", f.Syntax.Name, line.Start.Line, verb)
+ return
+ }
+ s, err := parseString(&args[0])
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ old := args[1]
+ v, err := parseVersion(s, &args[1], fix)
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+ return
+ }
+ v1, err := moduleMajorVersion(s)
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
+ fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
+ return
+ }
+ if verb == "require" {
+ f.Require = append(f.Require, &Require{
+ Mod: module.Version{Path: s, Version: v},
+ Syntax: line,
+ })
+ } else {
+ f.Exclude = append(f.Exclude, &Exclude{
+ Mod: module.Version{Path: s, Version: v},
+ Syntax: line,
+ })
+ }
+ case "replace":
+ if len(args) < 4 || len(args) > 5 || args[2] != "=>" {
+ fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3 => other/module v1.4\n\t or %s module/path v1.2.3 => ../local/directory", f.Syntax.Name, line.Start.Line, verb, verb)
+ return
+ }
+ s, err := parseString(&args[0])
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ old := args[1]
+ v, err := parseVersion(s, &args[1], fix)
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+ return
+ }
+ v1, err := moduleMajorVersion(s)
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
+ fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
+ return
+ }
+ ns, err := parseString(&args[3])
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
+ return
+ }
+ nv := ""
+ if len(args) == 4 {
+ if !isDirectoryPath(ns) {
+ fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)", f.Syntax.Name, line.Start.Line)
+ return
+ }
+ if filepath.Separator == '/' && strings.Contains(ns, `\`) {
+ fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)", f.Syntax.Name, line.Start.Line)
+ return
+ }
+ }
+ if len(args) == 5 {
+ old := args[4]
+ nv, err = parseVersion(ns, &args[4], fix)
+ if err != nil {
+ fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+ return
+ }
+ if isDirectoryPath(ns) {
+ fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version", f.Syntax.Name, line.Start.Line, ns)
+ return
+ }
+ }
+ // TODO: More sanity checks about directories vs module paths.
+ f.Replace = append(f.Replace, &Replace{
+ Old: module.Version{Path: s, Version: v},
+ New: module.Version{Path: ns, Version: nv},
+ Syntax: line,
+ })
+ }
+}
+
+func isDirectoryPath(ns string) bool {
+ // Because go.mod files can move from one system to another,
+ // we check all known path syntaxes, both Unix and Windows.
+ return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
+ strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) ||
+ len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
+}
+
+func mustQuote(t string) bool {
+ for _, r := range t {
+ if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' {
+ return true
+ }
+ }
+ return t == "" || strings.Contains(t, "//") || strings.Contains(t, "/*")
+}
+
+// AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
+// the quotation of s.
+func AutoQuote(s string) string {
+ if mustQuote(s) {
+ return strconv.Quote(s)
+ }
+ return s
+}
+
+func parseString(s *string) (string, error) {
+ t := *s
+ if strings.HasPrefix(t, `"`) {
+ var err error
+ if t, err = strconv.Unquote(t); err != nil {
+ return "", err
+ }
+ } else if strings.ContainsAny(t, "\"'`") {
+ // Other quotes are reserved both for possible future expansion
+ // and to avoid confusion. For example if someone types 'x'
+ // we want that to be a syntax error and not a literal x in literal quotation marks.
+ return "", fmt.Errorf("unquoted string cannot contain quote")
+ }
+ *s = AutoQuote(t)
+ return t, nil
+}
+
+func parseVersion(path string, s *string, fix VersionFixer) (string, error) {
+ t, err := parseString(s)
+ if err != nil {
+ return "", err
+ }
+ if fix != nil {
+ var err error
+ t, err = fix(path, t)
+ if err != nil {
+ return "", err
+ }
+ }
+ if semver.IsValid(t) {
+ *s = semver.Canonical(t)
+ return *s, nil
+ }
+ return "", fmt.Errorf("version must be of the form v1.2.3")
+}
+
+func moduleMajorVersion(p string) (string, error) {
+ if _, _, major, _, ok := ParseGopkgIn(p); ok {
+ return major, nil
+ }
+
+ start := strings.LastIndex(p, "/") + 1
+ v := p[start:]
+ if !isMajorVersion(v) {
+ return "v1", nil
+ }
+ if v[1] == '0' || v == "v1" {
+ return "", fmt.Errorf("module path has invalid version number %s", v)
+ }
+ return v, nil
+}
+
+func isMajorVersion(v string) bool {
+ if len(v) < 2 || v[0] != 'v' {
+ return false
+ }
+ for i := 1; i < len(v); i++ {
+ if v[i] < '0' || '9' < v[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func (f *File) Format() ([]byte, error) {
+ return Format(f.Syntax), nil
+}
+
+func (x *File) AddRequire(path, vers string) {
+ var syntax *Line
+
+ for i, stmt := range x.Syntax.Stmt {
+ switch stmt := stmt.(type) {
+ case *LineBlock:
+ if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+ syntax = &Line{Token: []string{AutoQuote(path), vers}}
+ stmt.Line = append(stmt.Line, syntax)
+ goto End
+ }
+ case *Line:
+ if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+ stmt.Token = stmt.Token[1:]
+ syntax = &Line{Token: []string{AutoQuote(path), vers}}
+ x.Syntax.Stmt[i] = &LineBlock{
+ Comments: stmt.Comments,
+ Token: []string{"require"},
+ Line: []*Line{
+ stmt,
+ syntax,
+ },
+ }
+ goto End
+ }
+ }
+ }
+
+ syntax = &Line{Token: []string{"require", AutoQuote(path), vers}}
+ x.Syntax.Stmt = append(x.Syntax.Stmt, syntax)
+
+End:
+ x.Require = append(x.Require, &Require{module.Version{Path: path, Version: vers}, syntax})
+}
+
+func (f *File) SetRequire(req []module.Version) {
+ need := make(map[string]string)
+ for _, m := range req {
+ need[m.Path] = m.Version
+ }
+
+ for _, r := range f.Require {
+ if v, ok := need[r.Mod.Path]; ok {
+ r.Mod.Version = v
+ }
+ }
+
+ var newStmts []Expr
+ for _, stmt := range f.Syntax.Stmt {
+ switch stmt := stmt.(type) {
+ case *LineBlock:
+ if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+ var newLines []*Line
+ for _, line := range stmt.Line {
+ if p, err := strconv.Unquote(line.Token[0]); err == nil && need[p] != "" {
+ line.Token[1] = need[p]
+ delete(need, p)
+ newLines = append(newLines, line)
+ }
+ }
+ if len(newLines) == 0 {
+ continue // drop stmt
+ }
+ stmt.Line = newLines
+ }
+
+ case *Line:
+ if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
+ if p, err := strconv.Unquote(stmt.Token[1]); err == nil && need[p] != "" {
+ stmt.Token[2] = need[p]
+ delete(need, p)
+ } else {
+ continue // drop stmt
+ }
+ }
+ }
+ newStmts = append(newStmts, stmt)
+ }
+ f.Syntax.Stmt = newStmts
+
+ for path, vers := range need {
+ f.AddRequire(path, vers)
+ }
+ f.SortBlocks()
+}
+
+func (f *File) SortBlocks() {
+ f.removeDups() // otherwise sorting is unsafe
+
+ for _, stmt := range f.Syntax.Stmt {
+ block, ok := stmt.(*LineBlock)
+ if !ok {
+ continue
+ }
+ sort.Slice(block.Line, func(i, j int) bool {
+ li := block.Line[i]
+ lj := block.Line[j]
+ for k := 0; k < len(li.Token) && k < len(lj.Token); k++ {
+ if li.Token[k] != lj.Token[k] {
+ return li.Token[k] < lj.Token[k]
+ }
+ }
+ return len(li.Token) < len(lj.Token)
+ })
+ }
+}
+
+func (f *File) removeDups() {
+ have := make(map[module.Version]bool)
+ kill := make(map[*Line]bool)
+ for _, x := range f.Exclude {
+ if have[x.Mod] {
+ kill[x.Syntax] = true
+ continue
+ }
+ have[x.Mod] = true
+ }
+ var excl []*Exclude
+ for _, x := range f.Exclude {
+ if !kill[x.Syntax] {
+ excl = append(excl, x)
+ }
+ }
+ f.Exclude = excl
+
+ have = make(map[module.Version]bool)
+ // Later replacements take priority over earlier ones.
+ for i := len(f.Replace) - 1; i >= 0; i-- {
+ x := f.Replace[i]
+ if have[x.Old] {
+ kill[x.Syntax] = true
+ continue
+ }
+ have[x.Old] = true
+ }
+ var repl []*Replace
+ for _, x := range f.Replace {
+ if !kill[x.Syntax] {
+ repl = append(repl, x)
+ }
+ }
+ f.Replace = repl
+
+ var stmts []Expr
+ for _, stmt := range f.Syntax.Stmt {
+ switch stmt := stmt.(type) {
+ case *Line:
+ if kill[stmt] {
+ continue
+ }
+ case *LineBlock:
+ var lines []*Line
+ for _, line := range stmt.Line {
+ if !kill[line] {
+ lines = append(lines, line)
+ }
+ }
+ stmt.Line = lines
+ if len(lines) == 0 {
+ continue
+ }
+ }
+ stmts = append(stmts, stmt)
+ }
+ f.Syntax.Stmt = stmts
+}
--- /dev/null
+// comment
+x "y" z
+
+// block
+block ( // block-eol
+ // x-before-line
+
+ "x" ( y // x-eol
+ "x1"
+ "x2"
+ // line
+ "x3"
+ "x4"
+
+ "x5"
+
+ // y-line
+ "y" // y-eol
+
+ "z" // z-eol
+) // block-eol2
+
+block2 (
+ x
+ y
+ z
+)
+
+// eof
--- /dev/null
+// comment
+x "y" z
+
+// block
+block ( // block-eol
+ // x-before-line
+
+ "x" ( y // x-eol
+ "x1"
+ "x2"
+ // line
+ "x3"
+ "x4"
+
+ "x5"
+
+ // y-line
+ "y" // y-eol
+
+ "z" // z-eol
+) // block-eol2
+
+
+block2 (x
+ y
+ z
+)
+
+// eof
--- /dev/null
+// comment
+module "x" // eol
+
+// mid comment
+
+// comment 2
+// comment 2 line 2
+module "y" // eoy
+
+// comment 3
--- /dev/null
+// comment
+module "x" // eol
+// mid comment
+
+// comment 2
+// comment 2 line 2
+module "y" // eoy
+// comment 3
--- /dev/null
+module x
+
+require (
+ gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528
+ gopkg.in/yaml.v2 v2.2.1
+)
--- /dev/null
+module abc
--- /dev/null
+module "abc"
--- /dev/null
+module abc
+
+replace xyz v1.2.3 => /tmp/z
+
+replace xyz v1.3.4 => my/xyz v1.3.4-me
--- /dev/null
+module "abc"
+
+replace "xyz" v1.2.3 => "/tmp/z"
+
+replace "xyz" v1.3.4 => "my/xyz" v1.3.4-me
--- /dev/null
+module abc
+
+replace (
+ xyz v1.2.3 => /tmp/z
+ xyz v1.3.4 => my/xyz v1.3.4-me
+ xyz v1.4.5 => "/tmp/my dir"
+ xyz v1.5.6 => my/xyz v1.5.6
+)
--- /dev/null
+module "abc"
+
+replace (
+ "xyz" v1.2.3 => "/tmp/z"
+ "xyz" v1.3.4 => "my/xyz" "v1.3.4-me"
+ xyz "v1.4.5" => "/tmp/my dir"
+ xyz v1.5.6 => my/xyz v1.5.6
+)
--- /dev/null
+module "x"
+
+module "y"
+
+require "x"
+
+require x
--- /dev/null
+// 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 modinfo
+
+type ModulePublic struct {
+ Top bool
+ Path string
+ Version string
+}
--- /dev/null
+// 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 module defines the module.Version type
+// along with support code.
+package module
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/go/internal/semver"
+)
+
+// A Version is defined by a module path and version pair.
+type Version struct {
+ Path string
+ Version string
+}
+
+// Check checks that a given module path, version pair is valid.
+// In addition to the path being a valid module path
+// and the version being a valid semantic version,
+// the two must correspond.
+// For example, the path "yaml/v2" only corresponds to
+// semantic versions beginning with "v2.".
+func Check(path, version string) error {
+ if err := CheckPath(path); err != nil {
+ return err
+ }
+ if !semver.IsValid(version) {
+ return fmt.Errorf("malformed semantic version %v", version)
+ }
+ vm := semver.Major(version)
+ _, pathVersion, _ := SplitPathVersion(path)
+
+ if strings.HasPrefix(pathVersion, ".") {
+ // Special-case gopkg.in path requirements.
+ pathVersion = pathVersion[1:] // cut .
+ if vm == pathVersion {
+ return nil
+ }
+ } else {
+ // Standard path requirements.
+ if pathVersion != "" {
+ pathVersion = pathVersion[1:] // cut /
+ }
+ if vm == "v0" || vm == "v1" {
+ vm = ""
+ }
+ if vm == pathVersion {
+ return nil
+ }
+ if pathVersion == "" {
+ pathVersion = "v0 or v1"
+ }
+ }
+ return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathVersion)
+}
+
+// firstPathOK reports whether r can appear in the first element of a module path.
+// The first element of the path must be an LDH domain name, at least for now.
+func firstPathOK(r rune) bool {
+ return r == '-' || r == '.' ||
+ '0' <= r && r <= '9' ||
+ 'A' <= r && r <= 'Z' ||
+ 'a' <= r && r <= 'z'
+}
+
+// pathOK reports whether r can appear in a module path.
+// Paths must avoid potentially problematic ASCII punctuation
+// and control characters but otherwise can be any Unicode printable character,
+// as defined by Go's IsPrint.
+func pathOK(r rune) bool {
+ if r < utf8.RuneSelf {
+ return r == '+' || r == ',' || r == '-' || r == '.' || r == '/' || r == '_' || r == '~' ||
+ '0' <= r && r <= '9' ||
+ 'A' <= r && r <= 'Z' ||
+ 'a' <= r && r <= 'z'
+ }
+ return unicode.IsPrint(r)
+}
+
+// CheckPath checks that a module path is valid.
+func CheckPath(path string) error {
+ if !utf8.ValidString(path) {
+ return fmt.Errorf("malformed module path %q: invalid UTF-8", path)
+ }
+ if path == "" {
+ return fmt.Errorf("malformed module path %q: empty string", path)
+ }
+
+ i := strings.Index(path, "/")
+ if i < 0 {
+ i = len(path)
+ }
+ if i == 0 {
+ return fmt.Errorf("malformed module path %q: leading slash", path)
+ }
+ if !strings.Contains(path[:i], ".") {
+ return fmt.Errorf("malformed module path %q: missing dot in first path element", path)
+ }
+ if path[i-1] == '.' {
+ return fmt.Errorf("malformed module path %q: trailing dot in first path element", path)
+ }
+ if path[0] == '.' {
+ return fmt.Errorf("malformed module path %q: leading dot in first path element", path)
+ }
+ if path[0] == '-' {
+ return fmt.Errorf("malformed module path %q: leading dash in first path element", path)
+ }
+ if strings.Contains(path, "..") {
+ return fmt.Errorf("malformed module path %q: double dot", path)
+ }
+ if strings.Contains(path, "//") {
+ return fmt.Errorf("malformed module path %q: double slash", path)
+ }
+ for _, r := range path[:i] {
+ if !firstPathOK(r) {
+ return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r)
+ }
+ }
+ if path[len(path)-1] == '/' {
+ return fmt.Errorf("malformed module path %q: trailing slash", path)
+ }
+ for _, r := range path {
+ if !pathOK(r) {
+ return fmt.Errorf("malformed module path %q: invalid char %q", path, r)
+ }
+ }
+ if _, _, ok := SplitPathVersion(path); !ok {
+ return fmt.Errorf("malformed module path %q: invalid version %s", path, path[strings.LastIndex(path, "/")+1:])
+ }
+ return nil
+}
+
+// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path
+// and version is either empty or "/vN" for N >= 2.
+// As a special case, gopkg.in paths are recognized directly;
+// they require ".vN" instead of "/vN", and for all N, not just N >= 2.
+func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) {
+ if strings.HasPrefix(path, "gopkg.in/") {
+ return splitGopkgIn(path)
+ }
+
+ i := len(path)
+ dot := false
+ for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
+ if path[i-1] == '.' {
+ dot = true
+ }
+ i--
+ }
+ if i <= 1 || path[i-1] != 'v' || path[i-2] != '/' {
+ return path, "", true
+ }
+ prefix, pathMajor = path[:i-2], path[i-2:]
+ if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" {
+ return path, "", false
+ }
+ return prefix, pathMajor, true
+}
+
+// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths.
+func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) {
+ if !strings.HasPrefix(path, "gopkg.in/") {
+ return path, "", false
+ }
+ i := len(path)
+ for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') {
+ i--
+ }
+ if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' {
+ // All gopkg.in paths must end in vN for some N.
+ return path, "", false
+ }
+ prefix, pathMajor = path[:i-2], path[i-2:]
+ if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" {
+ return path, "", false
+ }
+ return prefix, pathMajor, true
+}
+
+// MatchPathMajor reports whether the semantic version v
+// matches the path major version pathMajor.
+func MatchPathMajor(v, pathMajor string) bool {
+ m := semver.Major(v)
+ if pathMajor == "" {
+ return m == "v0" || m == "v1"
+ }
+ return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:]
+}
--- /dev/null
+// 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 module
+
+import "testing"
+
+var checkTests = []struct {
+ path string
+ version string
+ ok bool
+}{
+ {"rsc.io/quote", "0.1.0", false},
+ {"rsc io/quote", "v1.0.0", false},
+
+ {"github.com/go-yaml/yaml", "v0.8.0", true},
+ {"github.com/go-yaml/yaml", "v1.0.0", true},
+ {"github.com/go-yaml/yaml", "v2.0.0", false},
+ {"github.com/go-yaml/yaml", "v2.1.5", false},
+ {"github.com/go-yaml/yaml", "v3.0.0", false},
+
+ {"github.com/go-yaml/yaml/v2", "v1.0.0", false},
+ {"github.com/go-yaml/yaml/v2", "v2.0.0", true},
+ {"github.com/go-yaml/yaml/v2", "v2.1.5", true},
+ {"github.com/go-yaml/yaml/v2", "v3.0.0", false},
+
+ {"gopkg.in/yaml.v0", "v0.8.0", true},
+ {"gopkg.in/yaml.v0", "v1.0.0", false},
+ {"gopkg.in/yaml.v0", "v2.0.0", false},
+ {"gopkg.in/yaml.v0", "v2.1.5", false},
+ {"gopkg.in/yaml.v0", "v3.0.0", false},
+
+ {"gopkg.in/yaml.v1", "v0.8.0", false},
+ {"gopkg.in/yaml.v1", "v1.0.0", true},
+ {"gopkg.in/yaml.v1", "v2.0.0", false},
+ {"gopkg.in/yaml.v1", "v2.1.5", false},
+ {"gopkg.in/yaml.v1", "v3.0.0", false},
+
+ {"gopkg.in/yaml.v2", "v1.0.0", false},
+ {"gopkg.in/yaml.v2", "v2.0.0", true},
+ {"gopkg.in/yaml.v2", "v2.1.5", true},
+ {"gopkg.in/yaml.v2", "v3.0.0", false},
+}
+
+func TestCheck(t *testing.T) {
+ for _, tt := range checkTests {
+ err := Check(tt.path, tt.version)
+ if tt.ok && err != nil {
+ t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err)
+ } else if !tt.ok && err == nil {
+ t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version)
+ }
+ }
+}
+
+var checkPathTests = []struct {
+ path string
+ ok bool
+}{
+ {"x.y/z", true},
+ {"x.y", true},
+
+ {"", false},
+ {"x.y/\xFFz", false},
+ {"/x.y/z", false},
+ {"x./z", false},
+ {".x/z", false},
+ {"-x/z", false},
+ {"x..y/z", false},
+ {"x.y/z/../../w", false},
+ {"x.y//z", false},
+ {"x.y/z//w", false},
+ {"x.y/z/", false},
+
+ {"x.y/z/v0", false},
+ {"x.y/z/v1", false},
+ {"x.y/z/v2", true},
+ {"x.y/z/v2.0", false},
+
+ {"!x.y/z", false},
+ {"_x.y/z", false},
+ {"x.y!/z", false},
+ {"x.y\"/z", false},
+ {"x.y#/z", false},
+ {"x.y$/z", false},
+ {"x.y%/z", false},
+ {"x.y&/z", false},
+ {"x.y'/z", false},
+ {"x.y(/z", false},
+ {"x.y)/z", false},
+ {"x.y*/z", false},
+ {"x.y+/z", false},
+ {"x.y,/z", false},
+ {"x.y-/z", true},
+ {"x.y./zt", false},
+ {"x.y:/z", false},
+ {"x.y;/z", false},
+ {"x.y</z", false},
+ {"x.y=/z", false},
+ {"x.y>/z", false},
+ {"x.y?/z", false},
+ {"x.y@/z", false},
+ {"x.y[/z", false},
+ {"x.y\\/z", false},
+ {"x.y]/z", false},
+ {"x.y^/z", false},
+ {"x.y_/z", false},
+ {"x.y`/z", false},
+ {"x.y{/z", false},
+ {"x.y}/z", false},
+ {"x.y~/z", false},
+ {"x.y/z!", false},
+ {"x.y/z\"", false},
+ {"x.y/z#", false},
+ {"x.y/z$", false},
+ {"x.y/z%", false},
+ {"x.y/z&", false},
+ {"x.y/z'", false},
+ {"x.y/z(", false},
+ {"x.y/z)", false},
+ {"x.y/z*", false},
+ {"x.y/z+", true},
+ {"x.y/z,", true},
+ {"x.y/z-", true},
+ {"x.y/z.t", true},
+ {"x.y/z/t", true},
+ {"x.y/z:", false},
+ {"x.y/z;", false},
+ {"x.y/z<", false},
+ {"x.y/z=", false},
+ {"x.y/z>", false},
+ {"x.y/z?", false},
+ {"x.y/z@", false},
+ {"x.y/z[", false},
+ {"x.y/z\\", false},
+ {"x.y/z]", false},
+ {"x.y/z^", false},
+ {"x.y/z_", true},
+ {"x.y/z`", false},
+ {"x.y/z{", false},
+ {"x.y/z}", false},
+ {"x.y/z~", true},
+}
+
+func TestCheckPath(t *testing.T) {
+ for _, tt := range checkPathTests {
+ err := CheckPath(tt.path)
+ if tt.ok && err != nil {
+ t.Errorf("CheckPath(%q) = %v, wanted nil error", tt.path, err)
+ } else if !tt.ok && err == nil {
+ t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path)
+ }
+ }
+}
+
+var splitPathVersionTests = []struct {
+ pathPrefix string
+ version string
+}{
+ {"x.y/z", ""},
+ {"x.y/z", "/v2"},
+ {"x.y/z", "/v3"},
+ {"gopkg.in/yaml", ".v0"},
+ {"gopkg.in/yaml", ".v1"},
+ {"gopkg.in/yaml", ".v2"},
+ {"gopkg.in/yaml", ".v3"},
+}
+
+func TestSplitPathVersion(t *testing.T) {
+ for _, tt := range splitPathVersionTests {
+ pathPrefix, version, ok := SplitPathVersion(tt.pathPrefix + tt.version)
+ if pathPrefix != tt.pathPrefix || version != tt.version || !ok {
+ t.Errorf("SplitPathVersion(%q) = %q, %q, %v, want %q, %q, true", tt.pathPrefix+tt.version, pathPrefix, version, ok, tt.pathPrefix, tt.version)
+ }
+ }
+
+ for _, tt := range checkPathTests {
+ pathPrefix, version, ok := SplitPathVersion(tt.path)
+ if pathPrefix+version != tt.path {
+ t.Errorf("SplitPathVersion(%q) = %q, %q, %v, doesn't add to input", tt.path, pathPrefix, version, ok)
+ }
+ }
+}
--- /dev/null
+// 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 mvs implements Minimal Version Selection.
+// See https://research.swtch.com/vgo-mvs.
+package mvs
+
+import (
+ "fmt"
+ "sort"
+
+ "cmd/go/internal/module"
+)
+
+type Reqs interface {
+ Required(m module.Version) ([]module.Version, error)
+ Max(v1, v2 string) string
+ Latest(path string) (module.Version, error)
+ Previous(m module.Version) (module.Version, error)
+}
+
+type MissingModuleError struct {
+ Module module.Version
+}
+
+func (e *MissingModuleError) Error() string {
+ return fmt.Sprintf("missing module: %v", e.Module)
+}
+
+// BuildList returns the build list for the target module.
+func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
+ return buildList(target, reqs, nil, nil)
+}
+
+func buildList(target module.Version, reqs Reqs, uses map[module.Version][]module.Version, vers map[string]string) ([]module.Version, error) {
+ var (
+ min = map[string]string{target.Path: target.Version}
+ todo = []module.Version{target}
+ seen = map[module.Version]bool{target: true}
+ )
+ for len(todo) > 0 {
+ m := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+ required, _ := reqs.Required(m)
+ for _, r := range required {
+ if uses != nil {
+ uses[r] = append(uses[r], m)
+ }
+ if !seen[r] {
+ if v, ok := min[r.Path]; !ok {
+ min[r.Path] = r.Version
+ } else if max := reqs.Max(v, r.Version); max != v {
+ min[r.Path] = max
+ }
+ todo = append(todo, r)
+ seen[r] = true
+ }
+ }
+ }
+
+ if min[target.Path] != target.Version {
+ panic("unbuildable") // TODO
+ }
+
+ if vers == nil {
+ vers = make(map[string]string)
+ }
+ list := []module.Version{target}
+ for i := 0; i < len(list); i++ {
+ m := list[i]
+ required, err := reqs.Required(m)
+ if err != nil {
+ // TODO: Check error is decent.
+ return nil, err
+ }
+ for _, r := range required {
+ v := min[r.Path]
+ if reqs.Max(v, r.Version) != v {
+ panic("mistake") // TODO
+ }
+ if _, ok := vers[r.Path]; !ok {
+ vers[r.Path] = v
+ list = append(list, module.Version{Path: r.Path, Version: v})
+ }
+ }
+ }
+ tail := list[1:]
+ sort.Slice(tail, func(i, j int) bool {
+ return tail[i].Path < tail[j].Path
+ })
+ return list, nil
+}
+
+// Req returns the minimal requirement list for the target module
+// that result in the given build list.
+func Req(target module.Version, list []module.Version, reqs Reqs) ([]module.Version, error) {
+ // Compute postorder, cache requirements.
+ var postorder []module.Version
+ reqCache := map[module.Version][]module.Version{}
+ reqCache[target] = nil
+ var walk func(module.Version) error
+ walk = func(m module.Version) error {
+ _, ok := reqCache[m]
+ if ok {
+ return nil
+ }
+ required, err := reqs.Required(m)
+ if err != nil {
+ return err
+ }
+ reqCache[m] = required
+ for _, m1 := range required {
+ if err := walk(m1); err != nil {
+ return err
+ }
+ }
+ postorder = append(postorder, m)
+ return nil
+ }
+ for _, m := range list {
+ if err := walk(m); err != nil {
+ return nil, err
+ }
+ }
+
+ // Walk modules in reverse post-order, only adding those not implied already.
+ have := map[string]string{}
+ walk = func(m module.Version) error {
+ if v, ok := have[m.Path]; ok && reqs.Max(m.Version, v) == v {
+ return nil
+ }
+ have[m.Path] = m.Version
+ for _, m1 := range reqCache[m] {
+ walk(m1)
+ }
+ return nil
+ }
+ max := map[string]string{}
+ for _, m := range list {
+ if max[m.Path] == "" {
+ max[m.Path] = m.Version
+ } else {
+ max[m.Path] = reqs.Max(m.Version, max[m.Path])
+ }
+ }
+ var min []module.Version
+ for i := len(postorder) - 1; i >= 0; i-- {
+ m := postorder[i]
+ if max[m.Path] != m.Version {
+ // Older version.
+ continue
+ }
+ if have[m.Path] != m.Version {
+ min = append(min, m)
+ walk(m)
+ }
+ }
+ sort.Slice(min, func(i, j int) bool {
+ return min[i].Path < min[j].Path
+ })
+ return min, nil
+}
+
+// UpgradeAll returns a build list for the target module
+// in which every module is upgraded to its latest version.
+func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
+ have := map[string]bool{target.Path: true}
+ list := []module.Version{target}
+ for i := 0; i < len(list); i++ {
+ m := list[i]
+ required, err := reqs.Required(m)
+ if err != nil {
+ panic(err) // TODO
+ }
+ for _, r := range required {
+ latest, err := reqs.Latest(r.Path)
+ if err != nil {
+ panic(err) // TODO
+ }
+ if reqs.Max(latest.Version, r.Version) != latest.Version {
+ panic("mistake") // TODO
+ }
+ if !have[r.Path] {
+ have[r.Path] = true
+ list = append(list, module.Version{Path: r.Path, Version: latest.Version})
+ }
+ }
+ }
+ tail := list[1:]
+ sort.Slice(tail, func(i, j int) bool {
+ return tail[i].Path < tail[j].Path
+ })
+ return list, nil
+}
+
+// Upgrade returns a build list for the target module
+// in which the given additional modules are upgraded.
+func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]module.Version, error) {
+ list, err := reqs.Required(target)
+ if err != nil {
+ panic(err) // TODO
+ }
+ // TODO: Maybe if an error is given,
+ // rerun with BuildList(upgrade[0], reqs) etc
+ // to find which ones are the buggy ones.
+ list = append([]module.Version(nil), list...)
+ list = append(list, upgrade...)
+ return BuildList(target, &override{target, list, reqs})
+}
+
+// Downgrade returns a build list for the target module
+// in which the given additional modules are downgraded.
+func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) {
+ list, err := reqs.Required(target)
+ if err != nil {
+ panic(err) // TODO
+ }
+ max := make(map[string]string)
+ for _, r := range list {
+ max[r.Path] = r.Version
+ }
+ for _, d := range downgrade {
+ if v, ok := max[d.Path]; !ok || reqs.Max(v, d.Version) != d.Version {
+ max[d.Path] = d.Version
+ }
+ }
+
+ var (
+ added = make(map[module.Version]bool)
+ rdeps = make(map[module.Version][]module.Version)
+ excluded = make(map[module.Version]bool)
+ )
+ var exclude func(module.Version)
+ exclude = func(m module.Version) {
+ if excluded[m] {
+ return
+ }
+ excluded[m] = true
+ for _, p := range rdeps[m] {
+ exclude(p)
+ }
+ }
+ var add func(module.Version)
+ add = func(m module.Version) {
+ if added[m] {
+ return
+ }
+ added[m] = true
+ if v, ok := max[m.Path]; ok && reqs.Max(m.Version, v) != v {
+ exclude(m)
+ return
+ }
+ list, err := reqs.Required(m)
+ if err != nil {
+ panic(err) // TODO
+ }
+ for _, r := range list {
+ add(r)
+ if excluded[r] {
+ exclude(m)
+ return
+ }
+ rdeps[r] = append(rdeps[r], m)
+ }
+ }
+
+ var out []module.Version
+ out = append(out, target)
+List:
+ for _, r := range list {
+ add(r)
+ for excluded[r] {
+ p, err := reqs.Previous(r)
+ if err != nil {
+ return nil, err // TODO
+ }
+ // If the target version is a pseudo-version, it may not be
+ // included when iterating over prior versions using reqs.Previous.
+ // Insert it into the right place in the iteration.
+ // If v is excluded, p should be returned again by reqs.Previous on the next iteration.
+ if v := max[r.Path]; reqs.Max(v, r.Version) != v && reqs.Max(p.Version, v) != p.Version {
+ p.Version = v
+ }
+ if p.Version == "none" {
+ continue List
+ }
+ add(p)
+ r = p
+ }
+ out = append(out, r)
+ }
+
+ return out, nil
+}
+
+type override struct {
+ target module.Version
+ list []module.Version
+ Reqs
+}
+
+func (r *override) Required(m module.Version) ([]module.Version, error) {
+ if m == r.target {
+ return r.list, nil
+ }
+ return r.Reqs.Required(m)
+}
--- /dev/null
+// 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 mvs
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "cmd/go/internal/module"
+)
+
+var tests = `
+# Scenario from blog.
+name: blog
+A: B1 C2
+B1: D3
+C1: D2
+C2: D4
+C3: D5
+C4: G1
+D2: E1
+D3: E2
+D4: E2 F1
+D5: E2
+G1: C4
+A2: B1 C4 D4
+build A: A B1 C2 D4 E2 F1
+upgrade* A: A B1 C4 D5 E2 G1
+upgrade A C4: A B1 C4 D4 E2 F1 G1
+downgrade A2 D2: A2 C4 D2
+
+name: trim
+A: B1 C2
+B1: D3
+C2: B2
+B2:
+build A: A B2 C2
+
+# Cross-dependency between D and E.
+# No matter how it arises, should get result of merging all build lists via max,
+# which leads to including both D2 and E2.
+
+name: cross1
+A: B C
+B: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross1V
+A: B2 C D2 E1
+B1:
+B2: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B2 C D2 E2
+
+name: cross1U
+A: B1 C
+B1:
+B2: D1
+C: D2
+D1: E2
+D2: E1
+build A: A B1 C D2 E1
+upgrade A B2: A B2 C D2 E2
+
+name: cross1R
+A: B C
+B: D2
+C: D1
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross1X
+A: B C
+B: D1 E2
+C: D2
+D1: E2
+D2: E1
+build A: A B C D2 E2
+
+name: cross2
+A: B D2
+B: D1
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross2X
+A: B D2
+B: D1 E2
+C: D2
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross3
+A: B D2 E1
+B: D1
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+name: cross3X
+A: B D2 E1
+B: D1 E2
+D1: E2
+D2: E1
+build A: A B D2 E2
+
+# Should not get E2 here, because B has been updated
+# not to depend on D1 anymore.
+name: cross4
+A1: B1 D2
+A2: B2 D2
+B1: D1
+B2: D2
+D1: E2
+D2: E1
+build A1: A1 B1 D2 E2
+build A2: A2 B2 D2 E1
+
+# But the upgrade from A1 preserves the E2 dep explicitly.
+upgrade A1 B2: A1 B2 D2 E2
+upgradereq A1 B2: B2 E2
+
+name: cross5
+A: D1
+D1: E2
+D2: E1
+build A: A D1 E2
+upgrade* A: A D2 E2
+upgrade A D2: A D2 E2
+upgradereq A D2: D2 E2
+
+name: cross6
+A: D2
+D1: E2
+D2: E1
+build A: A D2 E1
+upgrade* A: A D2 E2
+upgrade A E2: A D2 E2
+
+name: cross7
+A: B C
+B: D1
+C: E1
+D1: E2
+E1: D2
+build A: A B C D2 E2
+
+name: down1
+A: B2
+B1: C1
+B2: C2
+build A: A B2 C2
+downgrade A C1: A B1
+
+name: down2
+A: B2 E2
+B1:
+B2: C2 F2
+C1:
+D1:
+C2: D2 E2
+D2: B2
+E2: D2
+E1:
+F1:
+downgrade A F1: A B1 E1
+
+name: down3
+A:
+
+# golang.org/issue/25542.
+name: noprev1
+A: B4 C2
+B2.hidden:
+C2:
+downgrade A B2.hidden: A B2.hidden C2
+
+name: noprev2
+A: B4 C2
+B2.hidden:
+B1:
+C2:
+downgrade A B2.hidden: A B2.hidden C2
+
+name: noprev3
+A: B4 C2
+B3:
+B2.hidden:
+C2:
+downgrade A B2.hidden: A B2.hidden C2
+`
+
+func Test(t *testing.T) {
+ var (
+ name string
+ reqs reqsMap
+ fns []func(*testing.T)
+ )
+ flush := func() {
+ if name != "" {
+ t.Run(name, func(t *testing.T) {
+ for _, fn := range fns {
+ fn(t)
+ }
+ })
+ }
+ }
+ m := func(s string) module.Version {
+ return module.Version{Path: s[:1], Version: s[1:]}
+ }
+ ms := func(list []string) []module.Version {
+ var mlist []module.Version
+ for _, s := range list {
+ mlist = append(mlist, m(s))
+ }
+ return mlist
+ }
+ checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) {
+ if err != nil {
+ t.Fatalf("%s: %v", desc, err)
+ }
+ vs := ms(strings.Fields(val))
+ if !reflect.DeepEqual(list, vs) {
+ t.Errorf("%s = %v, want %v", desc, list, vs)
+ }
+ }
+
+ for _, line := range strings.Split(tests, "\n") {
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "#") || line == "" {
+ continue
+ }
+ i := strings.Index(line, ":")
+ if i < 0 {
+ t.Fatalf("missing colon: %q", line)
+ }
+ key := strings.TrimSpace(line[:i])
+ val := strings.TrimSpace(line[i+1:])
+ if key == "" {
+ t.Fatalf("missing key: %q", line)
+ }
+ kf := strings.Fields(key)
+ switch kf[0] {
+ case "name":
+ if len(kf) != 1 {
+ t.Fatalf("name takes no arguments: %q", line)
+ }
+ flush()
+ reqs = make(reqsMap)
+ fns = nil
+ name = val
+ continue
+ case "build":
+ if len(kf) != 2 {
+ t.Fatalf("build takes one argument: %q", line)
+ }
+ fns = append(fns, func(t *testing.T) {
+ list, err := BuildList(m(kf[1]), reqs)
+ checkList(t, key, list, err, val)
+ })
+ continue
+ case "upgrade*":
+ if len(kf) != 2 {
+ t.Fatalf("upgrade* takes one argument: %q", line)
+ }
+ fns = append(fns, func(t *testing.T) {
+ list, err := UpgradeAll(m(kf[1]), reqs)
+ checkList(t, key, list, err, val)
+ })
+ continue
+ case "upgradereq":
+ if len(kf) < 2 {
+ t.Fatalf("upgrade takes at least one arguments: %q", line)
+ }
+ fns = append(fns, func(t *testing.T) {
+ list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
+ if err == nil {
+ list, err = Req(m(kf[1]), list, reqs)
+ }
+ checkList(t, key, list, err, val)
+ })
+ continue
+ case "upgrade":
+ if len(kf) < 2 {
+ t.Fatalf("upgrade takes at least one arguments: %q", line)
+ }
+ fns = append(fns, func(t *testing.T) {
+ list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
+ checkList(t, key, list, err, val)
+ })
+ continue
+ case "downgrade":
+ if len(kf) < 2 {
+ t.Fatalf("downgrade takes at least one arguments: %q", line)
+ }
+ fns = append(fns, func(t *testing.T) {
+ list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
+ checkList(t, key, list, err, val)
+ })
+ continue
+ }
+ if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
+ var rs []module.Version
+ for _, f := range strings.Fields(val) {
+ r := m(f)
+ if reqs[r] == nil {
+ reqs[r] = []module.Version{}
+ }
+ rs = append(rs, r)
+ }
+ reqs[m(key)] = rs
+ continue
+ }
+ t.Fatalf("bad line: %q", line)
+ }
+ flush()
+}
+
+type reqsMap map[module.Version][]module.Version
+
+func (r reqsMap) Max(v1, v2 string) string {
+ if v1 == "none" {
+ return v2
+ }
+ if v2 == "none" {
+ return v1
+ }
+ if v1 < v2 {
+ return v2
+ }
+ return v1
+}
+
+func (r reqsMap) Latest(path string) (module.Version, error) {
+ var m module.Version
+ for k := range r {
+ if k.Path == path && m.Version < k.Version {
+ m = k
+ }
+ }
+ if m.Path == "" {
+ return module.Version{}, &MissingModuleError{module.Version{Path: path, Version: ""}}
+ }
+ return m, nil
+}
+
+func (r reqsMap) Previous(m module.Version) (module.Version, error) {
+ var p module.Version
+ for k := range r {
+ if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version && !strings.HasSuffix(k.Version, ".hidden") {
+ p = k
+ }
+ }
+ if p.Path == "" {
+ return module.Version{Path: m.Path, Version: "none"}, nil
+ }
+ return p, nil
+}
+
+func (r reqsMap) Required(m module.Version) ([]module.Version, error) {
+ rr, ok := r[m]
+ if !ok {
+ return nil, &MissingModuleError{m}
+ }
+ return rr, nil
+}
--- /dev/null
+// Copyright 2017 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 search
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "fmt"
+ "go/build"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// AllPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func AllPackages(pattern string) []string {
+ pkgs := MatchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// AllPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func AllPackagesInFS(pattern string) []string {
+ pkgs := MatchPackagesInFS(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func MatchPackages(pattern string) []string {
+ match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
+ if !IsMetaPackage(pattern) {
+ match = MatchPattern(pattern)
+ treeCanMatch = TreeCanMatchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !cfg.BuildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ for _, src := range cfg.BuildContext.SrcDirs() {
+ if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
+ continue
+ }
+ src = filepath.Clean(src) + string(filepath.Separator)
+ root := src
+ if pattern == "cmd" {
+ root += "cmd" + string(filepath.Separator)
+ }
+ filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || path == src {
+ return nil
+ }
+
+ want := true
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ want = false
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
+ // The name "std" is only the standard library.
+ // If the name is cmd, it's the root of the command tree.
+ want = false
+ }
+ if !treeCanMatch(name) {
+ want = false
+ }
+
+ if !fi.IsDir() {
+ if fi.Mode()&os.ModeSymlink != 0 && want {
+ if target, err := os.Stat(path); err == nil && target.IsDir() {
+ fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+ }
+ }
+ return nil
+ }
+ if !want {
+ return filepath.SkipDir
+ }
+
+ if have[name] {
+ return nil
+ }
+ have[name] = true
+ if !match(name) {
+ return nil
+ }
+ pkg, err := cfg.BuildContext.ImportDir(path, 0)
+ if err != nil {
+ if _, noGo := err.(*build.NoGoError); noGo {
+ return nil
+ }
+ }
+
+ // If we are expanding "cmd", skip main
+ // packages under cmd/vendor. At least as of
+ // March, 2017, there is one there for the
+ // vendored pprof tool.
+ if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
+ return nil
+ }
+
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ }
+ return pkgs
+}
+
+var modRoot string
+
+func SetModRoot(dir string) {
+ modRoot = dir
+}
+
+// MatchPackagesInFS returns a list of package paths matching pattern,
+// which must begin with ./ or ../
+// (see go help packages for pattern syntax).
+func MatchPackagesInFS(pattern string) []string {
+ // Find directory to begin the scan.
+ // Could be smarter but this one optimization
+ // is enough for now, since ... is usually at the
+ // end of a path.
+ i := strings.Index(pattern, "...")
+ dir, _ := path.Split(pattern[:i])
+
+ // pattern begins with ./ or ../.
+ // path.Clean will discard the ./ but not the ../.
+ // We need to preserve the ./ for pattern matching
+ // and in the returned import paths.
+ prefix := ""
+ if strings.HasPrefix(pattern, "./") {
+ prefix = "./"
+ }
+ match := MatchPattern(pattern)
+
+ if modRoot != "" {
+ abs, err := filepath.Abs(dir)
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
+ if !hasFilepathPrefix(abs, modRoot) {
+ base.Fatalf("go: pattern %s refers to dir %s, outside module root %s", pattern, abs, modRoot)
+ return nil
+ }
+ }
+
+ var pkgs []string
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() {
+ return nil
+ }
+ if path == dir {
+ // filepath.Walk starts at dir and recurses. For the recursive case,
+ // the path is the result of filepath.Join, which calls filepath.Clean.
+ // The initial case is not Cleaned, though, so we do this explicitly.
+ //
+ // This converts a path like "./io/" to "io". Without this step, running
+ // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
+ // package, because prepending the prefix "./" to the unclean path would
+ // result in "././io", and match("././io") returns false.
+ path = filepath.Clean(path)
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
+ _, elem := filepath.Split(path)
+ dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+ if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := prefix + filepath.ToSlash(path)
+ if !match(name) {
+ return nil
+ }
+
+ // We keep the directory if we can import it, or if we can't import it
+ // due to invalid Go source files. This means that directories containing
+ // parse errors will be built (and fail) instead of being silently skipped
+ // as not matching the pattern. Go 1.5 and earlier skipped, but that
+ // behavior means people miss serious mistakes.
+ // See golang.org/issue/11407.
+ if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
+ return nil
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ return pkgs
+}
+
+// TreeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func TreeCanMatchPattern(pattern string) func(name string) bool {
+ wildCard := false
+ if i := strings.Index(pattern, "..."); i >= 0 {
+ wildCard = true
+ pattern = pattern[:i]
+ }
+ return func(name string) bool {
+ return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+ wildCard && strings.HasPrefix(name, pattern)
+ }
+}
+
+// MatchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+// Unfortunately, there are two special cases. Quoting "go help packages":
+//
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separted pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
+func MatchPattern(pattern string) func(name string) bool {
+ // Convert pattern to regular expression.
+ // The strategy for the trailing /... is to nest it in an explicit ? expression.
+ // The strategy for the vendor exclusion is to change the unmatchable
+ // vendor strings to a disallowed code point (vendorChar) and to use
+ // "(anything but that codepoint)*" as the implementation of the ... wildcard.
+ // This is a bit complicated but the obvious alternative,
+ // namely a hand-written search like in most shell glob matchers,
+ // is too easy to make accidentally exponential.
+ // Using package regexp guarantees linear-time matching.
+
+ const vendorChar = "\x00"
+
+ if strings.Contains(pattern, vendorChar) {
+ return func(name string) bool { return false }
+ }
+
+ re := regexp.QuoteMeta(pattern)
+ re = replaceVendor(re, vendorChar)
+ switch {
+ case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
+ re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case re == vendorChar+`/\.\.\.`:
+ re = `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case strings.HasSuffix(re, `/\.\.\.`):
+ re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
+ }
+ re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
+
+ reg := regexp.MustCompile(`^` + re + `$`)
+
+ return func(name string) bool {
+ if strings.Contains(name, vendorChar) {
+ return false
+ }
+ return reg.MatchString(replaceVendor(name, vendorChar))
+ }
+}
+
+// replaceVendor returns the result of replacing
+// non-trailing vendor path elements in x with repl.
+func replaceVendor(x, repl string) string {
+ if !strings.Contains(x, "vendor") {
+ return x
+ }
+ elem := strings.Split(x, "/")
+ for i := 0; i < len(elem)-1; i++ {
+ if elem[i] == "vendor" {
+ elem[i] = repl
+ }
+ }
+ return strings.Join(elem, "/")
+}
+
+// ImportPaths returns the import paths to use for the given command line.
+func ImportPaths(args []string) []string {
+ args = CleanImportPaths(args)
+ var out []string
+ for _, a := range args {
+ if IsMetaPackage(a) {
+ out = append(out, AllPackages(a)...)
+ continue
+ }
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, AllPackagesInFS(a)...)
+ } else {
+ out = append(out, AllPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// CleanImportPaths returns the import paths to use for the given
+// command line, but it does no wildcard expansion.
+func CleanImportPaths(args []string) []string {
+ if len(args) == 0 {
+ return []string{"."}
+ }
+ var out []string
+ for _, a := range args {
+ // Arguments are supposed to be import paths, but
+ // as a courtesy to Windows developers, rewrite \ to /
+ // in command-line arguments. Handles .\... and so on.
+ if filepath.Separator == '\\' {
+ a = strings.Replace(a, `\`, `/`, -1)
+ }
+
+ // Put argument in canonical form, but preserve leading ./.
+ if strings.HasPrefix(a, "./") {
+ a = "./" + path.Clean(a)
+ if a == "./." {
+ a = "."
+ }
+ } else {
+ a = path.Clean(a)
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// ImportPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+// TODO(vgo): Delete once old go get is gone.
+func ImportPathsNoDotExpansion(args []string) []string {
+ args = CleanImportPaths(args)
+ var out []string
+ for _, a := range args {
+ if IsMetaPackage(a) {
+ out = append(out, AllPackages(a)...)
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func IsMetaPackage(name string) bool {
+ return name == "std" || name == "cmd" || name == "all"
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
+
+// hasFilepathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasFilepathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
+ }
+}
+
+// IsStandardImportPath reports whether $GOROOT/src/path should be considered
+// part of the standard distribution. For historical reasons we allow people to add
+// their own code to $GOROOT instead of using $GOPATH, but we assume that
+// code will start with a domain name (dot in the first element).
+//
+// Note that this function is meant to evaluate whether a directory found in GOROOT
+// should be treated as part of the standard library. It should not be used to decide
+// that a directory found in GOPATH should be rejected: directories in GOPATH
+// need not have dots in the first element, and they just take their chances
+// with future collisions in the standard library.
+func IsStandardImportPath(path string) bool {
+ i := strings.Index(path, "/")
+ if i < 0 {
+ i = len(path)
+ }
+ elem := path[:i]
+ return !strings.Contains(elem, ".")
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package load
+package search
import (
"strings"
`
func TestMatchPattern(t *testing.T) {
- testPatterns(t, "matchPattern", matchPatternTests, func(pattern, name string) bool {
- return matchPattern(pattern)(name)
+ testPatterns(t, "MatchPattern", matchPatternTests, func(pattern, name string) bool {
+ return MatchPattern(pattern)(name)
})
}
`
func TestTreeCanMatchPattern(t *testing.T) {
- testPatterns(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
- return treeCanMatchPattern(pattern)(name)
+ testPatterns(t, "TreeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool {
+ return TreeCanMatchPattern(pattern)(name)
})
}
--- /dev/null
+// 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 semver implements comparison of semantic version strings.
+// In this package, semantic version strings must begin with a leading "v",
+// as in "v1.0.0".
+//
+// The general form of a semantic version string accepted by this package is
+//
+// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
+//
+// where square brackets indicate optional parts of the syntax;
+// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
+// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
+// using only alphanumeric characters and hyphens; and
+// all-numeric PRERELEASE identifiers must not have leading zeros.
+//
+// This package follows Semantic Versioning 2.0.0 (see semver.org)
+// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
+// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
+// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
+package semver
+
+// parsed returns the parsed form of a semantic version string.
+type parsed struct {
+ major string
+ minor string
+ patch string
+ short string
+ prerelease string
+ build string
+ err string
+}
+
+// IsValid reports whether v is a valid semantic version string.
+func IsValid(v string) bool {
+ _, ok := parse(v)
+ return ok
+}
+
+// Canonical returns the canonical formatting of the semantic version v.
+// It fills in any missing .MINOR or .PATCH and discards build metadata.
+// Two semantic versions compare equal only if their canonical formattings
+// are identical strings.
+// The canonical invalid semantic version is the empty string.
+func Canonical(v string) string {
+ p, ok := parse(v)
+ if !ok {
+ return ""
+ }
+ if p.build != "" {
+ return v[:len(v)-len(p.build)]
+ }
+ if p.short != "" {
+ return v + p.short
+ }
+ return v
+}
+
+// Major returns the major version prefix of the semantic version v.
+// For example, Major("v2.1.0") == "v2".
+// If v is an invalid semantic version string, Major returns the empty string.
+func Major(v string) string {
+ pv, ok := parse(v)
+ if !ok {
+ return ""
+ }
+ return v[:1+len(pv.major)]
+}
+
+// Compare returns an integer comparing two versions according to
+// according to semantic version precedence.
+// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
+//
+// An invalid semantic version string is considered less than a valid one.
+// All invalid semantic version strings compare equal to each other.
+func Compare(v, w string) int {
+ pv, ok1 := parse(v)
+ pw, ok2 := parse(w)
+ if !ok1 && !ok2 {
+ return 0
+ }
+ if !ok1 {
+ return -1
+ }
+ if !ok2 {
+ return +1
+ }
+ if c := compareInt(pv.major, pw.major); c != 0 {
+ return c
+ }
+ if c := compareInt(pv.minor, pw.minor); c != 0 {
+ return c
+ }
+ if c := compareInt(pv.patch, pw.patch); c != 0 {
+ return c
+ }
+ return comparePrerelease(pv.prerelease, pw.prerelease)
+}
+
+// Max canonicalizes its arguments and then returns the version string
+// that compares greater.
+func Max(v, w string) string {
+ v = Canonical(v)
+ w = Canonical(w)
+ if Compare(v, w) > 0 {
+ return v
+ }
+ return w
+}
+
+func parse(v string) (p parsed, ok bool) {
+ if v == "" || v[0] != 'v' {
+ p.err = "missing v prefix"
+ return
+ }
+ p.major, v, ok = parseInt(v[1:])
+ if !ok {
+ p.err = "bad major version"
+ return
+ }
+ if v == "" {
+ p.minor = "0"
+ p.patch = "0"
+ p.short = ".0.0"
+ return
+ }
+ if v[0] != '.' {
+ p.err = "bad minor prefix"
+ ok = false
+ return
+ }
+ p.minor, v, ok = parseInt(v[1:])
+ if !ok {
+ p.err = "bad minor version"
+ return
+ }
+ if v == "" {
+ p.patch = "0"
+ p.short = ".0"
+ return
+ }
+ if v[0] != '.' {
+ p.err = "bad patch prefix"
+ ok = false
+ return
+ }
+ p.patch, v, ok = parseInt(v[1:])
+ if !ok {
+ p.err = "bad patch version"
+ return
+ }
+ if len(v) > 0 && v[0] == '-' {
+ p.prerelease, v, ok = parsePrerelease(v)
+ if !ok {
+ p.err = "bad prerelease"
+ return
+ }
+ }
+ if len(v) > 0 && v[0] == '+' {
+ p.build, v, ok = parseBuild(v)
+ if !ok {
+ p.err = "bad build"
+ return
+ }
+ }
+ if v != "" {
+ p.err = "junk on end"
+ ok = false
+ return
+ }
+ ok = true
+ return
+}
+
+func parseInt(v string) (t, rest string, ok bool) {
+ if v == "" {
+ return
+ }
+ if v[0] < '0' || '9' < v[0] {
+ return
+ }
+ i := 1
+ for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+ i++
+ }
+ if v[0] == '0' && i != 1 {
+ return
+ }
+ return v[:i], v[i:], true
+}
+
+func parsePrerelease(v string) (t, rest string, ok bool) {
+ // "A pre-release version MAY be denoted by appending a hyphen and
+ // a series of dot separated identifiers immediately following the patch version.
+ // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
+ // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
+ if v == "" || v[0] != '-' {
+ return
+ }
+ i := 1
+ start := 1
+ for i < len(v) && v[i] != '+' {
+ if !isIdentChar(v[i]) && v[i] != '.' {
+ return
+ }
+ if v[i] == '.' {
+ if start == i || isBadNum(v[start:i]) {
+ return
+ }
+ start = i + 1
+ }
+ i++
+ }
+ if start == i || isBadNum(v[start:i]) {
+ return
+ }
+ return v[:i], v[i:], true
+}
+
+func parseBuild(v string) (t, rest string, ok bool) {
+ if v == "" || v[0] != '+' {
+ return
+ }
+ i := 1
+ start := 1
+ for i < len(v) {
+ if !isIdentChar(v[i]) {
+ return
+ }
+ if v[i] == '.' {
+ if start == i {
+ return
+ }
+ start = i + 1
+ }
+ i++
+ }
+ if start == i {
+ return
+ }
+ return v[:i], v[i:], true
+}
+
+func isIdentChar(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
+}
+
+func isBadNum(v string) bool {
+ i := 0
+ for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+ i++
+ }
+ return i == len(v) && i > 1 && v[0] == '0'
+}
+
+func isNum(v string) bool {
+ i := 0
+ for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+ i++
+ }
+ return i == len(v)
+}
+
+func compareInt(x, y string) int {
+ if x == y {
+ return 0
+ }
+ if len(x) < len(y) {
+ return -1
+ }
+ if len(x) > len(y) {
+ return +1
+ }
+ if x < y {
+ return -1
+ } else {
+ return +1
+ }
+}
+
+func comparePrerelease(x, y string) int {
+ // "When major, minor, and patch are equal, a pre-release version has
+ // lower precedence than a normal version.
+ // Example: 1.0.0-alpha < 1.0.0.
+ // Precedence for two pre-release versions with the same major, minor,
+ // and patch version MUST be determined by comparing each dot separated
+ // identifier from left to right until a difference is found as follows:
+ // identifiers consisting of only digits are compared numerically and
+ // identifiers with letters or hyphens are compared lexically in ASCII
+ // sort order. Numeric identifiers always have lower precedence than
+ // non-numeric identifiers. A larger set of pre-release fields has a
+ // higher precedence than a smaller set, if all of the preceding
+ // identifiers are equal.
+ // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
+ // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
+ if x == y {
+ return 0
+ }
+ if x == "" {
+ return +1
+ }
+ if y == "" {
+ return -1
+ }
+ for x != "" && y != "" {
+ x = x[1:] // skip - or .
+ y = y[1:] // skip - or .
+ var dx, dy string
+ dx, x = nextIdent(x)
+ dy, y = nextIdent(y)
+ if dx != dy {
+ ix := isNum(dx)
+ iy := isNum(dy)
+ if ix != iy {
+ if ix {
+ return -1
+ } else {
+ return +1
+ }
+ }
+ if ix {
+ if len(dx) < len(dy) {
+ return -1
+ }
+ if len(dx) > len(dy) {
+ return +1
+ }
+ }
+ if dx < dy {
+ return -1
+ } else {
+ return +1
+ }
+ }
+ }
+ if x == "" {
+ return -1
+ } else {
+ return +1
+ }
+}
+
+func nextIdent(x string) (dx, rest string) {
+ i := 0
+ for i < len(x) && x[i] != '.' {
+ i++
+ }
+ return x[:i], x[i:]
+}
--- /dev/null
+// 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 semver
+
+import (
+ "strings"
+ "testing"
+)
+
+var tests = []struct {
+ in string
+ out string
+}{
+ {"bad", ""},
+ {"v1-pre", ""},
+ {"v1+meta", ""},
+ {"v1-pre+meta", ""},
+ {"v1.2-pre", ""},
+ {"v1.2+meta", ""},
+ {"v1.2-pre+meta", ""},
+ {"v1.0.0-alpha", "v1.0.0-alpha"},
+ {"v1.0.0-alpha.1", "v1.0.0-alpha.1"},
+ {"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"},
+ {"v1.0.0-beta", "v1.0.0-beta"},
+ {"v1.0.0-beta.2", "v1.0.0-beta.2"},
+ {"v1.0.0-beta.11", "v1.0.0-beta.11"},
+ {"v1.0.0-rc.1", "v1.0.0-rc.1"},
+ {"v1", "v1.0.0"},
+ {"v1.0", "v1.0.0"},
+ {"v1.0.0", "v1.0.0"},
+ {"v1.2", "v1.2.0"},
+ {"v1.2.0", "v1.2.0"},
+ {"v1.2.3-456", "v1.2.3-456"},
+ {"v1.2.3-456.789", "v1.2.3-456.789"},
+ {"v1.2.3-456-789", "v1.2.3-456-789"},
+ {"v1.2.3-456a", "v1.2.3-456a"},
+ {"v1.2.3-pre", "v1.2.3-pre"},
+ {"v1.2.3-pre+meta", "v1.2.3-pre"},
+ {"v1.2.3-pre.1", "v1.2.3-pre.1"},
+ {"v1.2.3-zzz", "v1.2.3-zzz"},
+ {"v1.2.3", "v1.2.3"},
+ {"v1.2.3+meta", "v1.2.3"},
+}
+
+func TestIsValid(t *testing.T) {
+ for _, tt := range tests {
+ ok := IsValid(tt.in)
+ if ok != (tt.out != "") {
+ t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok)
+ }
+ }
+}
+
+func TestCanonical(t *testing.T) {
+ for _, tt := range tests {
+ out := Canonical(tt.in)
+ if out != tt.out {
+ t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out)
+ }
+ }
+}
+
+func TestMajor(t *testing.T) {
+ for _, tt := range tests {
+ out := Major(tt.in)
+ want := ""
+ if i := strings.Index(tt.out, "."); i >= 0 {
+ want = tt.out[:i]
+ }
+ if out != want {
+ t.Errorf("Major(%q) = %q, want %q", tt.in, out, want)
+ }
+ }
+}
+
+func TestCompare(t *testing.T) {
+ for i, ti := range tests {
+ for j, tj := range tests {
+ cmp := Compare(ti.in, tj.in)
+ var want int
+ if ti.out == tj.out {
+ want = 0
+ } else if i < j {
+ want = -1
+ } else {
+ want = +1
+ }
+ if cmp != want {
+ t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want)
+ }
+ }
+ }
+}
+
+func TestMax(t *testing.T) {
+ for i, ti := range tests {
+ for j, tj := range tests {
+ max := Max(ti.in, tj.in)
+ want := Canonical(ti.in)
+ if i < j {
+ want = Canonical(tj.in)
+ }
+ if max != want {
+ t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want)
+ }
+ }
+ }
+}
+
+var (
+ v1 = "v1.0.0+metadata-dash"
+ v2 = "v1.0.0+metadata-dash1"
+)
+
+func BenchmarkCompare(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if Compare(v1, v2) != 0 {
+ b.Fatalf("bad compare")
+ }
+ }
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "bytes"
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/modinfo"
+ "cmd/go/internal/module"
+ "cmd/go/internal/search"
+ "encoding/hex"
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+var (
+ infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
+ infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
+)
+
+func isStandardImportPath(path string) bool {
+ if search.IsStandardImportPath(path) {
+ if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src", path)); err == nil {
+ return true
+ }
+ if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src/vendor", path)); err == nil {
+ return true
+ }
+ }
+ return false
+}
+
+func PackageModuleInfo(path string) *modinfo.ModulePublic {
+ var info modinfo.ModulePublic
+ if isStandardImportPath(path) || !Enabled() {
+ return nil
+ }
+ target := findModule(path, path)
+ info.Top = target.Path == buildList[0].Path
+ info.Path = target.Path
+ info.Version = target.Version
+ return &info
+}
+
+func PackageBuildInfo(path string, deps []string) string {
+ if isStandardImportPath(path) || !Enabled() {
+ return ""
+ }
+ target := findModule(path, path)
+ mdeps := make(map[module.Version]bool)
+ for _, dep := range deps {
+ if !isStandardImportPath(dep) {
+ mdeps[findModule(path, dep)] = true
+ }
+ }
+ var mods []module.Version
+ delete(mdeps, target)
+ for mod := range mdeps {
+ mods = append(mods, mod)
+ }
+ sortModules(mods)
+
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "path\t%s\n", path)
+ tv := target.Version
+ if tv == "" {
+ tv = "(devel)"
+ }
+ fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, findModHash(target))
+ for _, mod := range mods {
+ mv := mod.Version
+ if mv == "" {
+ mv = "(devel)"
+ }
+ r := replaced(mod)
+ h := ""
+ if r == nil {
+ h = "\t" + findModHash(mod)
+ }
+ fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h)
+ if r := replaced(mod); r != nil {
+ fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.New.Path, r.New.Version, findModHash(r.New))
+ }
+ }
+ return buf.String()
+}
+
+func findModule(target, path string) module.Version {
+ if path == "." {
+ return buildList[0]
+ }
+ for _, mod := range buildList {
+ if importPathInModule(path, mod.Path) {
+ return mod
+ }
+ }
+ base.Fatalf("build %v: cannot find module for path %v", target, path)
+ panic("unreachable")
+}
+
+func ModInfoProg(info string) []byte {
+ return []byte(fmt.Sprintf(`
+ package main
+ import _ "unsafe"
+ //go:linkname __debug_modinfo__ runtime/debug.modinfo
+ var __debug_modinfo__ string
+ func init() {
+ __debug_modinfo__ = %q
+ }
+ `, string(infoStart)+info+string(infoEnd)))
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "archive/zip"
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/dirhash"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/module"
+ "cmd/go/internal/semver"
+)
+
+// fetch returns the directory in the local download cache
+// holding the root of mod's source tree.
+// It downloads the module if needed.
+func fetch(mod module.Version) (dir string, err error) {
+ if r := replaced(mod); r != nil {
+ if r.New.Version == "" {
+ dir = r.New.Path
+ if !filepath.IsAbs(dir) {
+ dir = filepath.Join(ModRoot, dir)
+ }
+ return dir, nil
+ }
+ mod = r.New
+ }
+
+ modpath := mod.Path + "@" + mod.Version
+ dir = filepath.Join(srcV, modpath)
+ if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
+ zipfile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".zip")
+ if _, err := os.Stat(zipfile); err == nil {
+ // Use it.
+ // This should only happen if the v/cache directory is preinitialized
+ // or if src/v/modpath was removed but not src/v/cache.
+ fmt.Fprintf(os.Stderr, "vgo: extracting %s %s\n", mod.Path, mod.Version)
+ } else {
+ if err := os.MkdirAll(filepath.Join(srcV, "cache", mod.Path, "@v"), 0777); err != nil {
+ return "", err
+ }
+ fmt.Fprintf(os.Stderr, "vgo: downloading %s %s\n", mod.Path, mod.Version)
+ if err := downloadZip(mod, zipfile); err != nil {
+ return "", err
+ }
+ }
+ if err := modfetch.Unzip(dir, zipfile, modpath, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "-> %s\n", err)
+ return "", err
+ }
+ }
+ checkModHash(mod)
+ return dir, nil
+}
+
+func downloadZip(mod module.Version, target string) error {
+ repo, err := modfetch.Lookup(mod.Path)
+ if err != nil {
+ return err
+ }
+ tmpfile, err := repo.Zip(mod.Version, os.TempDir())
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpfile)
+
+ // Double-check zip file looks OK.
+ z, err := zip.OpenReader(tmpfile)
+ if err != nil {
+ z.Close()
+ return err
+ }
+ prefix := mod.Path + "@" + mod.Version
+ for _, f := range z.File {
+ if !strings.HasPrefix(f.Name, prefix) {
+ z.Close()
+ return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
+ }
+ }
+ z.Close()
+
+ hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
+ if err != nil {
+ return err
+ }
+ r, err := os.Open(tmpfile)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+ w, err := os.Create(target)
+ if err != nil {
+ return err
+ }
+ if _, err := io.Copy(w, r); err != nil {
+ w.Close()
+ return fmt.Errorf("copying: %v", err)
+ }
+ if err := w.Close(); err != nil {
+ return err
+ }
+ return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
+}
+
+var useModHash = false
+var modHash map[module.Version][]string
+
+func initModHash() {
+ if modHash != nil {
+ return
+ }
+ modHash = make(map[module.Version][]string)
+ file := filepath.Join(ModRoot, "go.modverify")
+ data, err := ioutil.ReadFile(file)
+ if err != nil && os.IsNotExist(err) {
+ return
+ }
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ useModHash = true
+ lineno := 0
+ for len(data) > 0 {
+ var line []byte
+ lineno++
+ i := bytes.IndexByte(data, '\n')
+ if i < 0 {
+ line, data = data, nil
+ } else {
+ line, data = data[:i], data[i+1:]
+ }
+ f := strings.Fields(string(line))
+ if len(f) == 0 {
+ // blank line; skip it
+ continue
+ }
+ if len(f) != 3 {
+ base.Fatalf("vgo: malformed go.modverify:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
+ }
+ mod := module.Version{Path: f[0], Version: f[1]}
+ modHash[mod] = append(modHash[mod], f[2])
+ }
+}
+
+func checkModHash(mod module.Version) {
+ initModHash()
+ if !useModHash {
+ return
+ }
+
+ data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
+ if err != nil {
+ base.Fatalf("vgo: verifying %s %s: %v", mod.Path, mod.Version, err)
+ }
+ h := strings.TrimSpace(string(data))
+ if !strings.HasPrefix(h, "h1:") {
+ base.Fatalf("vgo: verifying %s %s: unexpected ziphash: %q", mod.Path, mod.Version, h)
+ }
+
+ for _, vh := range modHash[mod] {
+ if h == vh {
+ return
+ }
+ if strings.HasPrefix(vh, "h1:") {
+ base.Fatalf("vgo: verifying %s %s: module hash mismatch\n\tdownloaded: %v\n\tgo.modverify: %v", mod.Path, mod.Version, h, vh)
+ }
+ }
+ if len(modHash[mod]) > 0 {
+ fmt.Fprintf(os.Stderr, "warning: verifying %s %s: unknown hashes in go.modverify: %v; adding %v", mod.Path, mod.Version, strings.Join(modHash[mod], ", "), h)
+ }
+ modHash[mod] = append(modHash[mod], h)
+}
+
+func findModHash(mod module.Version) string {
+ data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
+ if err != nil {
+ return ""
+ }
+ return strings.TrimSpace(string(data))
+}
+
+func writeModHash() {
+ if !useModHash {
+ return
+ }
+
+ var mods []module.Version
+ for m := range modHash {
+ mods = append(mods, m)
+ }
+ sortModules(mods)
+ var buf bytes.Buffer
+ for _, m := range mods {
+ list := modHash[m]
+ sort.Strings(list)
+ for _, h := range list {
+ fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
+ }
+ }
+
+ file := filepath.Join(ModRoot, "go.modverify")
+ data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "go.modverify"))
+ if bytes.Equal(data, buf.Bytes()) {
+ return
+ }
+
+ if err := ioutil.WriteFile(file, buf.Bytes(), 0666); err != nil {
+ base.Fatalf("vgo: writing go.modverify: %v", err)
+ }
+}
+
+func sortModules(mods []module.Version) {
+ sort.Slice(mods, func(i, j int) bool {
+ mi := mods[i]
+ mj := mods[j]
+ if mi.Path != mj.Path {
+ return mi.Path < mj.Path
+ }
+ return semver.Compare(mi.Version, mj.Version) < 0
+ })
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/module"
+ "cmd/go/internal/mvs"
+ "cmd/go/internal/semver"
+)
+
+var CmdGet = &base.Command{
+ UsageLine: "get [build flags] [packages]",
+ Short: "download and install versioned modules and dependencies",
+ Long: `
+Get downloads the latest versions of modules containing the named packages,
+along with the versions of the dependencies required by those modules
+(not necessarily the latest ones).
+
+It then installs the named packages, like 'go install'.
+
+The -u flag causes get to download the latest version of dependencies as well.
+
+Each package being updated can be suffixed with @version to specify
+the desired version. Specifying a version older than the one currently
+in use causes a downgrade, which may in turn downgrade other
+modules using that one, to keep everything consistent.
+
+TODO: Make this documentation better once the semantic dust settles.
+ `,
+}
+
+var getU = CmdGet.Flag.Bool("u", false, "")
+
+func init() {
+ CmdGet.Run = runGet // break init loop
+}
+
+func runGet(cmd *base.Command, args []string) {
+ if *getU && len(args) > 0 {
+ base.Fatalf("vgo get: -u not supported with argument list")
+ }
+ if !*getU && len(args) == 0 {
+ base.Fatalf("vgo get: need arguments or -u")
+ }
+
+ if *getU {
+ isGetU = true
+ ImportPaths([]string{"."})
+ return
+ }
+
+ Init()
+ InitMod()
+ var upgrade []module.Version
+ var downgrade []module.Version
+ var newPkgs []string
+ for _, pkg := range args {
+ var path, vers string
+ /* OLD CODE
+ if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 {
+ i := strings.Index(pkg, "(")
+ j := strings.Index(pkg, ")")
+ if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' {
+ base.Errorf("vgo get: invalid module version syntax: %s", pkg)
+ continue
+ }
+ path, vers = pkg[:i], pkg[i+1:j]
+ pkg = pkg[:i] + pkg[j+1:]
+ */
+ if i := strings.Index(pkg, "@"); i >= 0 {
+ path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:]
+ if strings.Contains(vers, "@") {
+ base.Errorf("vgo get: invalid module version syntax: %s", pkg)
+ continue
+ }
+ } else {
+ path = pkg
+ vers = "latest"
+ }
+ if vers == "none" {
+ downgrade = append(downgrade, module.Version{Path: path, Version: ""})
+ } else {
+ info, err := modfetch.Query(path, vers, allowed)
+ if err != nil {
+ base.Errorf("vgo get %v: %v", pkg, err)
+ continue
+ }
+ upgrade = append(upgrade, module.Version{Path: path, Version: info.Version})
+ newPkgs = append(newPkgs, pkg)
+ }
+ }
+ args = newPkgs
+
+ // Upgrade.
+ var err error
+ buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...)
+ if err != nil {
+ base.Fatalf("vgo get: %v", err)
+ }
+
+ importPaths([]string{"."})
+
+ // Downgrade anything that went too far.
+ version := make(map[string]string)
+ for _, mod := range buildList {
+ version[mod.Path] = mod.Version
+ }
+ for _, mod := range upgrade {
+ if semver.Compare(mod.Version, version[mod.Path]) < 0 {
+ downgrade = append(downgrade, mod)
+ }
+ }
+
+ if len(downgrade) > 0 {
+ buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...)
+ if err != nil {
+ base.Fatalf("vgo get: %v", err)
+ }
+
+ // TODO: Check that everything we need to import is still available.
+ /*
+ local := v.matchPackages("all", v.Reqs[:1])
+ for _, path := range local {
+ dir, err := v.importDir(path)
+ if err != nil {
+ return err // TODO
+ }
+ imports, testImports, err := imports.ScanDir(dir, v.Tags)
+ for _, path := range imports {
+ xxx
+ }
+ for _, path := range testImports {
+ xxx
+ }
+ }
+ */
+ }
+ writeGoMod()
+
+ if len(args) > 0 {
+ InstallHook(args)
+ }
+}
+
+// Call into "go install". Set by internal/work, which imports us.
+var InstallHook func([]string)
--- /dev/null
+// 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 vgo
+
+import (
+ "bytes"
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/modconv"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/modfetch/codehost"
+ "cmd/go/internal/modfile"
+ "cmd/go/internal/module"
+ "cmd/go/internal/mvs"
+ "cmd/go/internal/search"
+ "cmd/go/internal/semver"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ cwd string
+ enabled = MustBeVgo
+ MustBeVgo = mustBeVgo()
+ initialized bool
+
+ ModRoot string
+ modFile *modfile.File
+ excluded map[module.Version]bool
+ Target module.Version
+
+ gopath string
+ srcV string
+)
+
+func BinDir() string {
+ if !Enabled() {
+ panic("vgo.Bin")
+ }
+ return filepath.Join(gopath, "bin")
+}
+
+func init() {
+ flag.BoolVar(&MustBeVgo, "vgo", MustBeVgo, "require use of modules")
+}
+
+// mustBeVgo reports whether we are invoked as vgo
+// (as opposed to go).
+// If so, we only support builds with go.mod files.
+func mustBeVgo() bool {
+ name := os.Args[0]
+ name = name[strings.LastIndex(name, "/")+1:]
+ name = name[strings.LastIndex(name, `\`)+1:]
+ return strings.HasPrefix(name, "vgo")
+}
+
+func Init() {
+ if initialized {
+ return
+ }
+ initialized = true
+
+ // If this is testgo - the test binary during cmd/go tests - then
+ // do not let it look for a go.mod. Only use vgo support if the
+ // global -vgo flag has been passed on the command line.
+ if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && !MustBeVgo {
+ return
+ }
+
+ // Disable any prompting for passwords by Git.
+ // Only has an effect for 2.3.0 or later, but avoiding
+ // the prompt in earlier versions is just too hard.
+ // If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
+ // prompting.
+ // See golang.org/issue/9341 and golang.org/issue/12706.
+ if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
+ os.Setenv("GIT_TERMINAL_PROMPT", "0")
+ }
+
+ // Disable any ssh connection pooling by Git.
+ // If a Git subprocess forks a child into the background to cache a new connection,
+ // that child keeps stdout/stderr open. After the Git subprocess exits,
+ // os /exec expects to be able to read from the stdout/stderr pipe
+ // until EOF to get all the data that the Git subprocess wrote before exiting.
+ // The EOF doesn't come until the child exits too, because the child
+ // is holding the write end of the pipe.
+ // This is unfortunate, but it has come up at least twice
+ // (see golang.org/issue/13453 and golang.org/issue/16104)
+ // and confuses users when it does.
+ // If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
+ // assume they know what they are doing and don't step on it.
+ // But default to turning off ControlMaster.
+ if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
+ os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
+ }
+
+ var err error
+ cwd, err = os.Getwd()
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
+
+ root, _ := FindModuleRoot(cwd, "", MustBeVgo)
+ if root == "" {
+ // If invoked as vgo, insist on a mod file.
+ if MustBeVgo {
+ base.Fatalf("cannot determine module root; please create a go.mod file there")
+ }
+ return
+ }
+ enabled = true
+ ModRoot = root
+ search.SetModRoot(root)
+}
+
+func Enabled() bool {
+ return false // COMPLETELY OFF FOR NOW
+ /*
+ if !initialized {
+ panic("vgo: Enabled called before Init")
+ }
+ return enabled
+ */
+}
+
+func InitMod() {
+ if Init(); !Enabled() || modFile != nil {
+ return
+ }
+
+ list := filepath.SplitList(cfg.BuildContext.GOPATH)
+ if len(list) == 0 || list[0] == "" {
+ base.Fatalf("missing $GOPATH")
+ }
+ gopath = list[0]
+ if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
+ base.Fatalf("$GOPATH/go.mod exists but should not")
+ }
+ srcV = filepath.Join(list[0], "src/v")
+ codehost.WorkRoot = filepath.Join(srcV, "cache/vcswork")
+
+ gomod := filepath.Join(ModRoot, "go.mod")
+ data, err := ioutil.ReadFile(gomod)
+ if err != nil {
+ legacyModInit()
+ return
+ }
+
+ f, err := modfile.Parse(gomod, data, fixVersion)
+ if err != nil {
+ // Errors returned by modfile.Parse begin with file:line.
+ base.Fatalf("vgo: errors parsing go.mod:\n%s\n", err)
+ }
+ modFile = f
+
+ if len(f.Syntax.Stmt) == 0 || f.Module == nil {
+ // Empty mod file. Must add module path.
+ path, err := FindModulePath(ModRoot)
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ f.AddModuleStmt(path)
+ }
+
+ if len(f.Syntax.Stmt) == 1 && f.Module != nil {
+ // Entire file is just a module statement.
+ // Populate require if possible.
+ legacyModInit()
+ }
+
+ excluded = make(map[module.Version]bool)
+ for _, x := range f.Exclude {
+ excluded[x.Mod] = true
+ }
+ Target = f.Module.Mod
+ writeGoMod()
+}
+
+func allowed(m module.Version) bool {
+ return !excluded[m]
+}
+
+func legacyModInit() {
+ if modFile == nil {
+ path, err := FindModulePath(ModRoot)
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ fmt.Fprintf(os.Stderr, "vgo: creating new go.mod: module %s\n", path)
+ modFile = new(modfile.File)
+ modFile.AddModuleStmt(path)
+ }
+
+ Target = modFile.Module.Mod
+ for _, name := range altConfigs {
+ cfg := filepath.Join(ModRoot, name)
+ data, err := ioutil.ReadFile(cfg)
+ if err == nil {
+ convert := modconv.Converters[name]
+ if convert == nil {
+ return
+ }
+ fmt.Fprintf(os.Stderr, "vgo: copying requirements from %s\n", cfg)
+ cfg = filepath.ToSlash(cfg)
+ if err := modfetch.ConvertLegacyConfig(modFile, cfg, data); err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ if len(modFile.Syntax.Stmt) == 1 {
+ // Add comment to prevent vgo from re-converting every time it runs.
+ modFile.AddComment("// vgo: no requirements found in " + name)
+ }
+ return
+ }
+ }
+}
+
+var altConfigs = []string{
+ "Gopkg.lock",
+
+ "GLOCKFILE",
+ "Godeps/Godeps.json",
+ "dependencies.tsv",
+ "glide.lock",
+ "vendor.conf",
+ "vendor.yml",
+ "vendor/manifest",
+ "vendor/vendor.json",
+
+ ".git/config",
+}
+
+// Exported only for testing.
+func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
+ dir = filepath.Clean(dir)
+ dir1 := dir
+ limit = filepath.Clean(limit)
+
+ // Look for enclosing go.mod.
+ for {
+ if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
+ return dir, "go.mod"
+ }
+ if dir == limit {
+ break
+ }
+ d := filepath.Dir(dir)
+ if d == dir {
+ break
+ }
+ dir = d
+ }
+
+ // Failing that, look for enclosing alternate version config.
+ if legacyConfigOK {
+ dir = dir1
+ for {
+ for _, name := range altConfigs {
+ if _, err := os.Stat(filepath.Join(dir, name)); err == nil {
+ return dir, name
+ }
+ }
+ if dir == limit {
+ break
+ }
+ d := filepath.Dir(dir)
+ if d == dir {
+ break
+ }
+ dir = d
+ }
+ }
+
+ return "", ""
+}
+
+// Exported only for testing.
+func FindModulePath(dir string) (string, error) {
+ for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
+ src := filepath.Join(gpdir, "src") + string(filepath.Separator)
+ if strings.HasPrefix(dir, src) {
+ return filepath.ToSlash(dir[len(src):]), nil
+ }
+ }
+
+ // Cast about for import comments,
+ // first in top-level directory, then in subdirectories.
+ list, _ := ioutil.ReadDir(dir)
+ for _, info := range list {
+ if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
+ if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
+ return com, nil
+ }
+ }
+ }
+ for _, info1 := range list {
+ if info1.IsDir() {
+ files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name()))
+ for _, info2 := range files {
+ if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
+ if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
+ return path.Dir(com), nil
+ }
+ }
+ }
+ }
+ }
+
+ // Look for Godeps.json declaring import path.
+ data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
+ var cfg struct{ ImportPath string }
+ json.Unmarshal(data, &cfg)
+ if cfg.ImportPath != "" {
+ return cfg.ImportPath, nil
+ }
+
+ // Look for vendor.json declaring import path.
+ data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
+ var cfg2 struct{ RootPath string }
+ json.Unmarshal(data, &cfg2)
+ if cfg2.RootPath != "" {
+ return cfg2.RootPath, nil
+ }
+
+ // Look for .git/config with github origin as last resort.
+ data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config"))
+ if m := gitOriginRE.FindSubmatch(data); m != nil {
+ return "github.com/" + string(m[1]), nil
+ }
+
+ return "", fmt.Errorf("cannot determine module path for source directory %s (outside GOPATH, no import comments)", dir)
+}
+
+var (
+ gitOriginRE = regexp.MustCompile(`(?m)^\[remote "origin"\]\r?\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\r?\n`)
+ importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
+)
+
+func findImportComment(file string) string {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ return ""
+ }
+ m := importCommentRE.FindSubmatch(data)
+ if m == nil {
+ return ""
+ }
+ path, err := strconv.Unquote(string(m[1]))
+ if err != nil {
+ return ""
+ }
+ return path
+}
+
+func writeGoMod() {
+ writeModHash()
+
+ if buildList != nil {
+ min, err := mvs.Req(Target, buildList, newReqs())
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ modFile.SetRequire(min)
+ }
+
+ file := filepath.Join(ModRoot, "go.mod")
+ old, _ := ioutil.ReadFile(file)
+ new, err := modFile.Format()
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ if bytes.Equal(old, new) {
+ return
+ }
+ if err := ioutil.WriteFile(file, new, 0666); err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+}
+
+func fixVersion(path, vers string) (string, error) {
+ // Special case: remove the old -gopkgin- hack.
+ if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
+ vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
+ }
+
+ // fixVersion is called speculatively on every
+ // module, version pair from every go.mod file.
+ // Avoid the query if it looks OK.
+ _, pathMajor, ok := module.SplitPathVersion(path)
+ if !ok {
+ return "", fmt.Errorf("malformed module path: %s", path)
+ }
+ if semver.IsValid(vers) && vers == semver.Canonical(vers) && module.MatchPathMajor(vers, pathMajor) {
+ return vers, nil
+ }
+
+ info, err := modfetch.Query(path, vers, nil)
+ if err != nil {
+ return "", err
+ }
+ return info.Version, nil
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "unicode/utf8"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/module"
+)
+
+func ListT(pkgs []string) {
+ if Init(); !Enabled() {
+ base.Fatalf("go list: cannot use -t outside module")
+ }
+ InitMod()
+
+ if len(pkgs) == 0 {
+ base.Fatalf("vgo list -t: need list of modules")
+ }
+
+ for _, pkg := range pkgs {
+ repo, err := modfetch.Lookup(pkg)
+ if err != nil {
+ base.Errorf("vgo list -t: %v", err)
+ continue
+ }
+ path := repo.ModulePath()
+ fmt.Printf("%s\n", path)
+ tags, err := repo.Versions("")
+ if err != nil {
+ base.Errorf("vgo list -t: %v", err)
+ continue
+ }
+ for _, t := range tags {
+ if excluded[module.Version{Path: path, Version: t}] {
+ t += " # excluded"
+ }
+ fmt.Printf("\t%s\n", t)
+ }
+ }
+}
+
+func ListM() {
+ if Init(); !Enabled() {
+ base.Fatalf("go list: cannot use -m outside module")
+ }
+ InitMod()
+ iterate(func(*loader) {})
+ printListM(os.Stdout)
+}
+
+func printListM(w io.Writer) {
+ var rows [][]string
+ rows = append(rows, []string{"MODULE", "VERSION"})
+ for _, mod := range buildList {
+ v := mod.Version
+ if v == "" {
+ v = "-"
+ }
+ rows = append(rows, []string{mod.Path, v})
+ if r := replaced(mod); r != nil {
+ rows = append(rows, []string{" => " + r.New.Path, r.New.Version})
+ }
+ }
+ printTable(w, rows)
+}
+
+func ListMU() {
+ if Init(); !Enabled() {
+ base.Fatalf("go list: cannot use -m outside module")
+ }
+ InitMod()
+
+ quietLookup = true // do not chatter in v.Lookup
+ iterate(func(*loader) {})
+
+ var rows [][]string
+ rows = append(rows, []string{"MODULE", "VERSION", "LATEST"})
+ for _, mod := range buildList {
+ var latest string
+ v := mod.Version
+ if v == "" {
+ v = "-"
+ latest = "-"
+ } else {
+ info, err := modfetch.Query(mod.Path, "latest", allowed)
+ if err != nil {
+ latest = "ERR: " + err.Error()
+ } else {
+ latest = info.Version
+ if !isPseudoVersion(latest) && !info.Time.IsZero() {
+ latest += info.Time.Local().Format(" (2006-01-02 15:04)")
+ }
+ }
+ if !isPseudoVersion(mod.Version) {
+ if info, err := modfetch.Query(mod.Path, mod.Version, nil); err == nil && !info.Time.IsZero() {
+ v += info.Time.Local().Format(" (2006-01-02 15:04)")
+ }
+ }
+ }
+ if latest == v {
+ latest = "-"
+ }
+ rows = append(rows, []string{mod.Path, v, latest})
+ }
+ printTable(os.Stdout, rows)
+}
+
+var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
+
+func isPseudoVersion(v string) bool {
+ return pseudoVersionRE.MatchString(v)
+}
+
+func printTable(w io.Writer, rows [][]string) {
+ var max []int
+ for _, row := range rows {
+ for i, c := range row {
+ n := utf8.RuneCountInString(c)
+ if i >= len(max) {
+ max = append(max, n)
+ } else if max[i] < n {
+ max[i] = n
+ }
+ }
+ }
+
+ b := bufio.NewWriter(w)
+ for _, row := range rows {
+ for len(row) > 0 && row[len(row)-1] == "" {
+ row = row[:len(row)-1]
+ }
+ for i, c := range row {
+ b.WriteString(c)
+ if i+1 < len(row) {
+ for j := utf8.RuneCountInString(c); j < max[i]+2; j++ {
+ b.WriteRune(' ')
+ }
+ }
+ }
+ b.WriteRune('\n')
+ }
+ b.Flush()
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "go/build"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/imports"
+ "cmd/go/internal/modconv"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/modfile"
+ "cmd/go/internal/module"
+ "cmd/go/internal/mvs"
+ "cmd/go/internal/search"
+ "cmd/go/internal/semver"
+)
+
+type importLevel int
+
+const (
+ levelNone importLevel = 0
+ levelBuild importLevel = 1
+ levelTest importLevel = 2
+ levelTestRecursive importLevel = 3
+)
+
+var (
+ buildList []module.Version
+ tags map[string]bool
+ importmap map[string]string
+ pkgdir map[string]string
+ pkgmod map[string]module.Version
+ isGetU bool
+)
+
+func AddImports(gofiles []string) {
+ if Init(); !Enabled() {
+ return
+ }
+ InitMod()
+
+ imports, testImports, err := imports.ScanFiles(gofiles, tags)
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+
+ iterate(func(ld *loader) {
+ ld.importList(imports, levelBuild)
+ ld.importList(testImports, levelBuild)
+ })
+ writeGoMod()
+}
+
+func ImportPaths(args []string) []string {
+ if Init(); !Enabled() {
+ return search.ImportPaths(args)
+ }
+ InitMod()
+
+ paths := importPaths(args)
+ writeGoMod()
+ return paths
+}
+
+func importPaths(args []string) []string {
+ level := levelBuild
+ switch cfg.CmdName {
+ case "test", "vet":
+ level = levelTest
+ }
+ cleaned := search.CleanImportPaths(args)
+ iterate(func(ld *loader) {
+ args = expandImportPaths(cleaned)
+ for i, pkg := range args {
+ if pkg == "." || pkg == ".." || strings.HasPrefix(pkg, "./") || strings.HasPrefix(pkg, "../") {
+ dir := filepath.Join(cwd, pkg)
+ if dir == ModRoot {
+ pkg = Target.Path
+ } else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
+ pkg = Target.Path + filepath.ToSlash(dir[len(ModRoot):])
+ } else {
+ base.Errorf("vgo: package %s outside module root", pkg)
+ continue
+ }
+ args[i] = pkg
+ }
+ ld.importPkg(pkg, level)
+ }
+ })
+ return args
+}
+
+func Lookup(parentPath, path string) (dir, realPath string, err error) {
+ realPath = importmap[path]
+ if realPath == "" {
+ if isStandardImportPath(path) {
+ dir := filepath.Join(cfg.GOROOT, "src", path)
+ if _, err := os.Stat(dir); err == nil {
+ return dir, path, nil
+ }
+ }
+ return "", "", fmt.Errorf("no such package in module")
+ }
+ return pkgdir[realPath], realPath, nil
+}
+
+func iterate(doImports func(*loader)) {
+ var err error
+ mvsOp := mvs.BuildList
+ if isGetU {
+ mvsOp = mvs.UpgradeAll
+ }
+ buildList, err = mvsOp(Target, newReqs())
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+
+ var ld *loader
+ for {
+ ld = newLoader()
+ doImports(ld)
+ if len(ld.missing) == 0 {
+ break
+ }
+ for _, m := range ld.missing {
+ findMissing(m)
+ }
+ base.ExitIfErrors()
+ buildList, err = mvsOp(Target, newReqs())
+ if err != nil {
+ base.Fatalf("vgo: %v", err)
+ }
+ }
+ base.ExitIfErrors()
+
+ importmap = ld.importmap
+ pkgdir = ld.pkgdir
+ pkgmod = ld.pkgmod
+}
+
+type loader struct {
+ imported map[string]importLevel
+ importmap map[string]string
+ pkgdir map[string]string
+ pkgmod map[string]module.Version
+ tags map[string]bool
+ missing []missing
+ imports []string
+ stack []string
+}
+
+type missing struct {
+ path string
+ stack string
+}
+
+func newLoader() *loader {
+ ld := &loader{
+ imported: make(map[string]importLevel),
+ importmap: make(map[string]string),
+ pkgdir: make(map[string]string),
+ pkgmod: make(map[string]module.Version),
+ tags: imports.Tags(),
+ }
+ ld.imported["C"] = 100
+ return ld
+}
+
+func (ld *loader) stackText() string {
+ var buf bytes.Buffer
+ for _, p := range ld.stack[:len(ld.stack)-1] {
+ fmt.Fprintf(&buf, "import %q ->\n\t", p)
+ }
+ fmt.Fprintf(&buf, "import %q", ld.stack[len(ld.stack)-1])
+ return buf.String()
+}
+
+func (ld *loader) importList(pkgs []string, level importLevel) {
+ for _, pkg := range pkgs {
+ ld.importPkg(pkg, level)
+ }
+}
+
+func (ld *loader) importPkg(path string, level importLevel) {
+ if ld.imported[path] >= level {
+ return
+ }
+
+ ld.stack = append(ld.stack, path)
+ defer func() {
+ ld.stack = ld.stack[:len(ld.stack)-1]
+ }()
+
+ // Any rewritings go here.
+ realPath := path
+
+ ld.imported[path] = level
+ ld.importmap[path] = realPath
+ if realPath != path && ld.imported[realPath] >= level {
+ // Already handled.
+ return
+ }
+
+ dir := ld.importDir(realPath)
+ if dir == "" {
+ return
+ }
+
+ ld.pkgdir[realPath] = dir
+
+ imports, testImports, err := imports.ScanDir(dir, ld.tags)
+ if err != nil {
+ base.Errorf("vgo: %s [%s]: %v", ld.stackText(), dir, err)
+ return
+ }
+ nextLevel := level
+ if level == levelTest {
+ nextLevel = levelBuild
+ }
+ for _, pkg := range imports {
+ ld.importPkg(pkg, nextLevel)
+ }
+ if level >= levelTest {
+ for _, pkg := range testImports {
+ ld.importPkg(pkg, nextLevel)
+ }
+ }
+}
+
+func (ld *loader) importDir(path string) string {
+ if importPathInModule(path, Target.Path) {
+ dir := ModRoot
+ if len(path) > len(Target.Path) {
+ dir = filepath.Join(dir, path[len(Target.Path)+1:])
+ }
+ ld.pkgmod[path] = Target
+ return dir
+ }
+
+ i := strings.Index(path, "/")
+ if i < 0 || !strings.Contains(path[:i], ".") {
+ if strings.HasPrefix(path, "golang_org/") {
+ return filepath.Join(cfg.GOROOT, "src/vendor", path)
+ }
+ dir := filepath.Join(cfg.GOROOT, "src", path)
+ if _, err := os.Stat(dir); err == nil {
+ return dir
+ }
+ }
+
+ var mod1 module.Version
+ var dir1 string
+ for _, mod := range buildList {
+ if !importPathInModule(path, mod.Path) {
+ continue
+ }
+ dir, err := fetch(mod)
+ if err != nil {
+ base.Errorf("vgo: %s: %v", ld.stackText(), err)
+ return ""
+ }
+ if len(path) > len(mod.Path) {
+ dir = filepath.Join(dir, path[len(mod.Path)+1:])
+ }
+ if dir1 != "" {
+ base.Errorf("vgo: %s: found in both %v %v and %v %v", ld.stackText(),
+ mod1.Path, mod1.Version, mod.Path, mod.Version)
+ return ""
+ }
+ dir1 = dir
+ mod1 = mod
+ }
+ if dir1 != "" {
+ ld.pkgmod[path] = mod1
+ return dir1
+ }
+ ld.missing = append(ld.missing, missing{path, ld.stackText()})
+ return ""
+}
+
+func replaced(mod module.Version) *modfile.Replace {
+ var found *modfile.Replace
+ for _, r := range modFile.Replace {
+ if r.Old == mod {
+ found = r // keep going
+ }
+ }
+ return found
+}
+
+func importPathInModule(path, mpath string) bool {
+ return mpath == path ||
+ len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath
+}
+
+var found = make(map[string]bool)
+
+func findMissing(m missing) {
+ for _, mod := range buildList {
+ if importPathInModule(m.path, mod.Path) {
+ // Leave for ordinary build to complain about the missing import.
+ return
+ }
+ }
+ if build.IsLocalImport(m.path) {
+ base.Errorf("vgo: relative import is not supported: %s", m.path)
+ return
+ }
+ fmt.Fprintf(os.Stderr, "vgo: resolving import %q\n", m.path)
+ repo, info, err := modfetch.Import(m.path, allowed)
+ if err != nil {
+ base.Errorf("vgo: %s: %v", m.stack, err)
+ return
+ }
+ root := repo.ModulePath()
+ fmt.Fprintf(os.Stderr, "vgo: finding %s (latest)\n", root)
+ if found[root] {
+ base.Fatalf("internal error: findmissing loop on %s", root)
+ }
+ found[root] = true
+ fmt.Fprintf(os.Stderr, "vgo: adding %s %s\n", root, info.Version)
+ buildList = append(buildList, module.Version{Path: root, Version: info.Version})
+ modFile.AddRequire(root, info.Version)
+}
+
+type mvsReqs struct {
+ extra []module.Version
+}
+
+func newReqs(extra ...module.Version) *mvsReqs {
+ r := &mvsReqs{
+ extra: extra,
+ }
+ return r
+}
+
+func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
+ list, err := r.required(mod)
+ if err != nil {
+ return nil, err
+ }
+ if *getU {
+ for i := range list {
+ list[i].Version = "none"
+ }
+ return list, nil
+ }
+ for i, mv := range list {
+ for excluded[mv] {
+ mv1, err := r.Next(mv)
+ if err != nil {
+ return nil, err
+ }
+ if mv1.Version == "" {
+ return nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)
+ }
+ mv = mv1
+ }
+ list[i] = mv
+ }
+ return list, nil
+}
+
+var vgoVersion = []byte(modconv.Prefix)
+
+func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
+ if mod == Target {
+ var list []module.Version
+ if buildList != nil {
+ list = append(list, buildList[1:]...)
+ return list, nil
+ }
+ for _, r := range modFile.Require {
+ list = append(list, r.Mod)
+ }
+ list = append(list, r.extra...)
+ return list, nil
+ }
+
+ origPath := mod.Path
+ if repl := replaced(mod); repl != nil {
+ if repl.New.Version == "" {
+ // TODO: need to slip the new version into the tags list etc.
+ dir := repl.New.Path
+ if !filepath.IsAbs(dir) {
+ dir = filepath.Join(ModRoot, dir)
+ }
+ gomod := filepath.Join(dir, "go.mod")
+ data, err := ioutil.ReadFile(gomod)
+ if err != nil {
+ return nil, err
+ }
+ f, err := modfile.Parse(gomod, data, nil)
+ if err != nil {
+ return nil, err
+ }
+ var list []module.Version
+ for _, r := range f.Require {
+ list = append(list, r.Mod)
+ }
+ return list, nil
+ }
+ mod = repl.New
+ }
+
+ if mod.Version == "none" {
+ return nil, nil
+ }
+
+ if !semver.IsValid(mod.Version) {
+ // Disallow the broader queries supported by fetch.Lookup.
+ panic(fmt.Errorf("invalid semantic version %q for %s", mod.Version, mod.Path))
+ // TODO return nil, fmt.Errorf("invalid semantic version %q", mod.Version)
+ }
+
+ gomod := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".mod")
+ infofile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".info")
+ var f *modfile.File
+ if data, err := ioutil.ReadFile(gomod); err == nil {
+ // If go.mod has a //vgo comment at the start,
+ // it was auto-converted from a legacy lock file.
+ // The auto-conversion details may have bugs and
+ // may be fixed in newer versions of vgo.
+ // We ignore cached go.mod files if they do not match
+ // our own vgoVersion.
+ if !bytes.HasPrefix(data, vgoVersion[:len("//vgo")]) || bytes.HasPrefix(data, vgoVersion) {
+ f, err := modfile.Parse(gomod, data, nil)
+ if err != nil {
+ return nil, err
+ }
+ var list []module.Version
+ for _, r := range f.Require {
+ list = append(list, r.Mod)
+ }
+ return list, nil
+ }
+ f, err = modfile.Parse("go.mod", data, nil)
+ if err != nil {
+ return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
+ }
+ } else {
+ if !quietLookup {
+ fmt.Fprintf(os.Stderr, "vgo: finding %s %s\n", mod.Path, mod.Version)
+ }
+ repo, err := modfetch.Lookup(mod.Path)
+ if err != nil {
+ base.Errorf("vgo: %s: %v\n", mod.Path, err)
+ return nil, err
+ }
+ info, err := repo.Stat(mod.Version)
+ if err != nil {
+ base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
+ return nil, err
+ }
+ data, err := repo.GoMod(info.Version)
+ if err != nil {
+ base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
+ return nil, err
+ }
+
+ f, err = modfile.Parse("go.mod", data, nil)
+ if err != nil {
+ return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
+ }
+
+ dir := filepath.Dir(gomod)
+ if err := os.MkdirAll(dir, 0777); err != nil {
+ return nil, fmt.Errorf("caching go.mod: %v", err)
+ }
+ js, err := json.Marshal(info)
+ if err != nil {
+ return nil, fmt.Errorf("internal error: json failure: %v", err)
+ }
+ if err := ioutil.WriteFile(infofile, js, 0666); err != nil {
+ return nil, fmt.Errorf("caching info: %v", err)
+ }
+ if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
+ return nil, fmt.Errorf("caching go.mod: %v", err)
+ }
+ }
+ if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
+ return nil, fmt.Errorf("downloaded %q and got module %q", mod.Path, mpath)
+ }
+
+ var list []module.Version
+ for _, req := range f.Require {
+ list = append(list, req.Mod)
+ }
+ if false {
+ fmt.Fprintf(os.Stderr, "REQLIST %v:\n", mod)
+ for _, req := range list {
+ fmt.Fprintf(os.Stderr, "\t%v\n", req)
+ }
+ }
+ return list, nil
+}
+
+var quietLookup bool
+
+func (*mvsReqs) Max(v1, v2 string) string {
+ if semver.Compare(v1, v2) == -1 {
+ return v2
+ }
+ return v1
+}
+
+func (*mvsReqs) Latest(path string) (module.Version, error) {
+ // Note that query "latest" is not the same as
+ // using repo.Latest.
+ // The query only falls back to untagged versions
+ // if nothing is tagged. The Latest method
+ // only ever returns untagged versions,
+ // which is not what we want.
+ fmt.Fprintf(os.Stderr, "vgo: finding %s latest\n", path)
+ info, err := modfetch.Query(path, "latest", allowed)
+ if err != nil {
+ return module.Version{}, err
+ }
+ return module.Version{Path: path, Version: info.Version}, nil
+}
+
+var versionCache = make(map[string][]string)
+
+func versions(path string) ([]string, error) {
+ list, ok := versionCache[path]
+ if !ok {
+ var err error
+ repo, err := modfetch.Lookup(path)
+ if err != nil {
+ return nil, err
+ }
+ list, err = repo.Versions("")
+ if err != nil {
+ return nil, err
+ }
+ versionCache[path] = list
+ }
+ return list, nil
+}
+
+func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
+ list, err := versions(m.Path)
+ if err != nil {
+ return module.Version{}, err
+ }
+ i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
+ if i > 0 {
+ return module.Version{Path: m.Path, Version: list[i-1]}, nil
+ }
+ return module.Version{Path: m.Path, Version: "none"}, nil
+}
+
+func (*mvsReqs) Next(m module.Version) (module.Version, error) {
+ list, err := versions(m.Path)
+ if err != nil {
+ return module.Version{}, err
+ }
+ i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
+ if i < len(list) {
+ return module.Version{Path: m.Path, Version: list[i]}, nil
+ }
+ return module.Version{Path: m.Path, Version: "none"}, nil
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "fmt"
+ "go/build"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/imports"
+ "cmd/go/internal/module"
+ "cmd/go/internal/search"
+)
+
+func expandImportPaths(args []string) []string {
+ var out []string
+ for _, a := range args {
+ // TODO(rsc): Move a == "ALL" test into search.IsMetaPackage
+ // once we officially lock in all the module work (tentatively, Go 1.12).
+ if search.IsMetaPackage(a) || a == "ALL" {
+ switch a {
+ default:
+ fmt.Fprintf(os.Stderr, "vgo: warning: %q matches no packages when using modules\n", a)
+ case "all", "ALL":
+ out = append(out, AllPackages(a)...)
+ }
+ continue
+ }
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, search.AllPackagesInFS(a)...)
+ } else {
+ out = append(out, AllPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// AllPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func AllPackages(pattern string) []string {
+ pkgs := MatchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func MatchPackages(pattern string) []string {
+ if pattern == "std" || pattern == "cmd" {
+ return nil
+ }
+ if pattern == "all" {
+ return MatchAll()
+ }
+ if pattern == "ALL" {
+ return MatchALL()
+ }
+
+ return matchPackages(pattern, buildList)
+}
+
+func matchPackages(pattern string, buildList []module.Version) []string {
+ match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
+ if !search.IsMetaPackage(pattern) && pattern != "ALL" {
+ match = search.MatchPattern(pattern)
+ treeCanMatch = search.TreeCanMatchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !cfg.BuildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ for _, mod := range buildList {
+ if !treeCanMatch(mod.Path) {
+ continue
+ }
+ var root string
+ if mod.Version == "" {
+ root = ModRoot
+ } else {
+ var err error
+ root, err = fetch(mod)
+ if err != nil {
+ base.Errorf("vgo: %v", err)
+ continue
+ }
+ }
+ root = filepath.Clean(root)
+
+ filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ return nil
+ }
+
+ want := true
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ want = false
+ }
+
+ name := mod.Path + filepath.ToSlash(path[len(root):])
+ if !treeCanMatch(name) {
+ want = false
+ }
+
+ if !fi.IsDir() {
+ if fi.Mode()&os.ModeSymlink != 0 && want {
+ if target, err := os.Stat(path); err == nil && target.IsDir() {
+ fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+ }
+ }
+ return nil
+ }
+
+ if !want {
+ return filepath.SkipDir
+ }
+ if path != root {
+ if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil {
+ return filepath.SkipDir
+ }
+ }
+
+ if !have[name] {
+ have[name] = true
+ if match(name) {
+ if _, _, err := imports.ScanDir(path, imports.Tags()); err != imports.ErrNoGo {
+ pkgs = append(pkgs, name)
+ }
+ }
+ }
+
+ if elem == "vendor" {
+ return filepath.SkipDir
+ }
+ return nil
+ })
+ }
+ return pkgs
+}
+
+// MatchAll returns a list of the packages matching the pattern "all".
+// We redefine "all" to mean start with the packages in the current module
+// and then follow imports into other modules to add packages imported
+// (directly or indirectly) as part of builds in this module.
+// It does not include packages in other modules that are not needed
+// by builds of this module.
+func MatchAll() []string {
+ return matchAll(imports.Tags())
+}
+
+// MatchALL returns a list of the packages matching the pattern "ALL".
+// The pattern "ALL" is like "all" but looks at all source files,
+// even ones that would be ignored by current build tag settings.
+// That's useful for identifying which packages to include in a vendor directory.
+func MatchALL() []string {
+ return matchAll(map[string]bool{"*": true})
+}
+
+// matchAll is the common implementation of MatchAll and MatchALL,
+// which differ only in the set of tags to apply to select files.
+func matchAll(tags map[string]bool) []string {
+ local := matchPackages("all", buildList[:1])
+ ld := newLoader()
+ ld.tags = tags
+ ld.importList(local, levelTestRecursive)
+ var all []string
+ for _, pkg := range ld.importmap {
+ if !isStandardImportPath(pkg) {
+ all = append(all, pkg)
+ }
+ }
+ sort.Strings(all)
+ return all
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/module"
+)
+
+var CmdVendor = &base.Command{
+ UsageLine: "vendor [-v]",
+ Short: "vendor dependencies of current module",
+ Long: `
+Vendor resets the module's vendor directory to include all
+packages needed to build and test all packages in the module
+and their dependencies.
+
+The -v flag causes vendor to print to standard error the
+module paths of the modules processed and the import paths
+of the packages copied.
+ `,
+}
+
+var vendorV = CmdVendor.Flag.Bool("v", false, "")
+
+func init() {
+ CmdVendor.Run = runVendor // break init cycle
+}
+
+func runVendor(cmd *base.Command, args []string) {
+ if Init(); !Enabled() {
+ base.Fatalf("vgo vendor: cannot use -m outside module")
+ }
+ if len(args) != 0 {
+ base.Fatalf("vgo vendor: vendor takes no arguments")
+ }
+ InitMod()
+ pkgs := ImportPaths([]string{"ALL"})
+
+ vdir := filepath.Join(ModRoot, "vendor")
+ if err := os.RemoveAll(vdir); err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+
+ modpkgs := make(map[module.Version][]string)
+ for _, pkg := range pkgs {
+ m := pkgmod[pkg]
+ if m == Target {
+ continue
+ }
+ modpkgs[m] = append(modpkgs[m], pkg)
+ }
+
+ var buf bytes.Buffer
+ for _, m := range buildList[1:] {
+ if pkgs := modpkgs[m]; len(pkgs) > 0 {
+ repl := ""
+ if r := replaced(m); r != nil {
+ repl = " => " + r.New.Path
+ if r.New.Version != "" {
+ repl += " " + r.New.Version
+ }
+ }
+ fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
+ if *vendorV {
+ fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
+ }
+ for _, pkg := range pkgs {
+ fmt.Fprintf(&buf, "%s\n", pkg)
+ if *vendorV {
+ fmt.Fprintf(os.Stderr, "%s\n", pkg)
+ }
+ vendorPkg(vdir, pkg)
+ }
+ }
+ }
+ if err := ioutil.WriteFile(filepath.Join(vdir, "vgo.list"), buf.Bytes(), 0666); err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+}
+
+func vendorPkg(vdir, pkg string) {
+ realPath := importmap[pkg]
+ if realPath != pkg && importmap[realPath] != "" {
+ fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
+ }
+
+ dst := filepath.Join(vdir, pkg)
+ src := pkgdir[realPath]
+ if src == "" {
+ fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
+ }
+ copyDir(dst, src, false)
+}
+
+func copyDir(dst, src string, recursive bool) {
+ files, err := ioutil.ReadDir(src)
+ if err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ if err := os.MkdirAll(dst, 0777); err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ for _, file := range files {
+ if file.IsDir() {
+ if recursive || file.Name() == "testdata" {
+ copyDir(filepath.Join(dst, file.Name()), filepath.Join(src, file.Name()), true)
+ }
+ continue
+ }
+ if !file.Mode().IsRegular() {
+ continue
+ }
+ r, err := os.Open(filepath.Join(src, file.Name()))
+ if err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ w, err := os.Create(filepath.Join(dst, file.Name()))
+ if err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ if _, err := io.Copy(w, r); err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ r.Close()
+ if err := w.Close(); err != nil {
+ base.Fatalf("vgo vendor: %v", err)
+ }
+ }
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
--- /dev/null
+// 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 vgo
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/dirhash"
+ "cmd/go/internal/module"
+)
+
+var CmdVerify = &base.Command{
+ UsageLine: "verify",
+ Run: runVerify,
+ Short: "verify downloaded modules against expected hashes",
+ Long: `
+Verify checks that the dependencies of the current module,
+which are stored in a local downloaded source cache,
+have not been modified since being downloaded.
+
+If all the modules are unmodified, verify prints
+
+ all modules verified
+
+and exits successfully (status 0). Otherwise, verify reports
+which modules have been changed and exits with a non-zero status.
+ `,
+}
+
+func runVerify(cmd *base.Command, args []string) {
+ if Init(); !Enabled() {
+ base.Fatalf("vgo verify: cannot use outside module")
+ }
+ if len(args) != 0 {
+ // TODO: take arguments
+ base.Fatalf("vgo verify: verify takes no arguments")
+ }
+
+ // Make go.mod consistent but don't load any packages.
+ InitMod()
+ iterate(func(*loader) {})
+ writeGoMod()
+
+ ok := true
+ for _, mod := range buildList[1:] {
+ ok = verifyMod(mod) && ok
+ }
+ if ok {
+ fmt.Printf("all modules verified\n")
+ }
+}
+
+func verifyMod(mod module.Version) bool {
+ ok := true
+ zip := filepath.Join(srcV, "cache", mod.Path, "/@v/", mod.Version+".zip")
+ _, zipErr := os.Stat(zip)
+ dir := filepath.Join(srcV, mod.Path+"@"+mod.Version)
+ _, dirErr := os.Stat(dir)
+ data, err := ioutil.ReadFile(zip + "hash")
+ if err != nil {
+ if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
+ // Nothing downloaded yet. Nothing to verify.
+ return true
+ }
+ base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
+ return false
+ }
+ h := string(bytes.TrimSpace(data))
+
+ if zipErr != nil && os.IsNotExist(zipErr) {
+ // ok
+ } else {
+ hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
+ if err != nil {
+ base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+ return false
+ } else if hZ != h {
+ base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
+ ok = false
+ }
+ }
+ if dirErr != nil && os.IsNotExist(dirErr) {
+ // ok
+ } else {
+ hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
+ if err != nil {
+
+ base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+ return false
+ }
+ if hD != h {
+ base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
+ ok = false
+ }
+ }
+ return ok
+}
--- /dev/null
+// 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 web2
+
+import (
+ "bytes"
+ "cmd/go/internal/base"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "sync"
+)
+
+var TraceGET = false
+var webstack = false
+
+func init() {
+ flag.BoolVar(&TraceGET, "webtrace", TraceGET, "trace GET requests")
+ flag.BoolVar(&webstack, "webstack", webstack, "print stack for GET requests")
+}
+
+type netrcLine struct {
+ machine string
+ login string
+ password string
+}
+
+var netrcOnce sync.Once
+var netrc []netrcLine
+
+func parseNetrc(data string) []netrcLine {
+ var nrc []netrcLine
+ var l netrcLine
+ for _, line := range strings.Split(data, "\n") {
+ f := strings.Fields(line)
+ for i := 0; i < len(f)-1; i += 2 {
+ switch f[i] {
+ case "machine":
+ l.machine = f[i+1]
+ case "login":
+ l.login = f[i+1]
+ case "password":
+ l.password = f[i+1]
+ }
+ }
+ if l.machine != "" && l.login != "" && l.password != "" {
+ nrc = append(nrc, l)
+ l = netrcLine{}
+ }
+ }
+ return nrc
+}
+
+func havePassword(machine string) bool {
+ netrcOnce.Do(readNetrc)
+ for _, line := range netrc {
+ if line.machine == machine {
+ return true
+ }
+ }
+ return false
+}
+
+func netrcPath() string {
+ switch runtime.GOOS {
+ case "windows":
+ return filepath.Join(os.Getenv("USERPROFILE"), "_netrc")
+ case "plan9":
+ return filepath.Join(os.Getenv("home"), ".netrc")
+ default:
+ return filepath.Join(os.Getenv("HOME"), ".netrc")
+ }
+}
+
+func readNetrc() {
+ data, err := ioutil.ReadFile(netrcPath())
+ if err != nil {
+ return
+ }
+ netrc = parseNetrc(string(data))
+}
+
+type getState struct {
+ req *http.Request
+ resp *http.Response
+ body io.ReadCloser
+ non200ok bool
+}
+
+type Option interface {
+ option(*getState) error
+}
+
+func Non200OK() Option {
+ return optionFunc(func(g *getState) error {
+ g.non200ok = true
+ return nil
+ })
+}
+
+type optionFunc func(*getState) error
+
+func (f optionFunc) option(g *getState) error {
+ return f(g)
+}
+
+func DecodeJSON(dst interface{}) Option {
+ return optionFunc(func(g *getState) error {
+ if g.resp != nil {
+ return json.NewDecoder(g.body).Decode(dst)
+ }
+ return nil
+ })
+}
+
+func ReadAllBody(body *[]byte) Option {
+ return optionFunc(func(g *getState) error {
+ if g.resp != nil {
+ var err error
+ *body, err = ioutil.ReadAll(g.body)
+ return err
+ }
+ return nil
+ })
+}
+
+func Body(body *io.ReadCloser) Option {
+ return optionFunc(func(g *getState) error {
+ if g.resp != nil {
+ *body = g.body
+ g.body = nil
+ }
+ return nil
+ })
+}
+
+func Header(hdr *http.Header) Option {
+ return optionFunc(func(g *getState) error {
+ if g.resp != nil {
+ *hdr = CopyHeader(g.resp.Header)
+ }
+ return nil
+ })
+}
+
+func CopyHeader(hdr http.Header) http.Header {
+ if hdr == nil {
+ return nil
+ }
+ h2 := make(http.Header)
+ for k, v := range hdr {
+ v2 := make([]string, len(v))
+ copy(v2, v)
+ h2[k] = v2
+ }
+ return h2
+}
+
+var cache struct {
+ mu sync.Mutex
+ byURL map[string]*cacheEntry
+}
+
+type cacheEntry struct {
+ mu sync.Mutex
+ resp *http.Response
+ body []byte
+}
+
+var httpDo = http.DefaultClient.Do
+
+func SetHTTPDoForTesting(do func(*http.Request) (*http.Response, error)) {
+ if do == nil {
+ do = http.DefaultClient.Do
+ }
+ httpDo = do
+}
+
+func Get(url string, options ...Option) error {
+ if TraceGET || webstack {
+ println("GET", url)
+ if webstack {
+ println(string(debug.Stack()))
+ }
+ }
+
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return err
+ }
+
+ netrcOnce.Do(readNetrc)
+ for _, l := range netrc {
+ if l.machine == req.URL.Host {
+ req.SetBasicAuth(l.login, l.password)
+ break
+ }
+ }
+
+ g := &getState{req: req}
+ for _, o := range options {
+ if err := o.option(g); err != nil {
+ return err
+ }
+ }
+
+ cache.mu.Lock()
+ e := cache.byURL[url]
+ if e == nil {
+ e = new(cacheEntry)
+ if !strings.HasPrefix(url, "file:") {
+ if cache.byURL == nil {
+ cache.byURL = make(map[string]*cacheEntry)
+ }
+ cache.byURL[url] = e
+ }
+ }
+ cache.mu.Unlock()
+
+ e.mu.Lock()
+ if strings.HasPrefix(url, "file:") {
+ body, err := ioutil.ReadFile(req.URL.Path)
+ if err != nil {
+ e.mu.Unlock()
+ return err
+ }
+ e.body = body
+ e.resp = &http.Response{
+ StatusCode: 200,
+ }
+ } else if e.resp == nil {
+ resp, err := httpDo(req)
+ if err != nil {
+ e.mu.Unlock()
+ return err
+ }
+ e.resp = resp
+ // TODO: Spool to temp file.
+ body, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ resp.Body = nil
+ if err != nil {
+ e.mu.Unlock()
+ return err
+ }
+ e.body = body
+ }
+ g.resp = e.resp
+ g.body = ioutil.NopCloser(bytes.NewReader(e.body))
+ e.mu.Unlock()
+
+ defer func() {
+ if g.body != nil {
+ g.body.Close()
+ }
+ }()
+
+ if g.resp.StatusCode == 403 && req.URL.Host == "api.github.com" && !havePassword("api.github.com") {
+ base.Errorf("%s", githubMessage)
+ }
+ if !g.non200ok && g.resp.StatusCode != 200 {
+ return fmt.Errorf("unexpected status (%s): %v", url, g.resp.Status)
+ }
+
+ for _, o := range options {
+ if err := o.option(g); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+var githubMessage = `vgo: 403 response from api.github.com
+
+GitHub applies fairly small rate limits to unauthenticated users, and
+you appear to be hitting them. To authenticate, please visit
+https://github.com/settings/tokens and click "Generate New Token" to
+create a Personal Access Token. The token only needs "public_repo"
+scope, but you can add "repo" if you want to access private
+repositories too.
+
+Add the token to your $HOME/.netrc (%USERPROFILE%\_netrc on Windows):
+
+ machine api.github.com login YOU password TOKEN
+
+Sorry for the interruption.
+`
--- /dev/null
+// 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 web2
+
+import (
+ "reflect"
+ "testing"
+)
+
+var testNetrc = `
+machine api.github.com
+ login user
+ password pwd
+
+machine incomlete.host
+ login justlogin
+
+machine test.host
+login user2
+password pwd2
+`
+
+func TestReadNetrc(t *testing.T) {
+ lines := parseNetrc(testNetrc)
+ want := []netrcLine{
+ {"api.github.com", "user", "pwd"},
+ {"test.host", "user2", "pwd2"},
+ }
+
+ if !reflect.DeepEqual(lines, want) {
+ t.Errorf("parseNetrc:\nhave %q\nwant %q", lines, want)
+ }
+}
--- /dev/null
+// 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 webtest
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "unicode/utf8"
+
+ web "cmd/go/internal/web2"
+)
+
+var mode = flag.String("webtest", "replay", "set webtest `mode` - record, replay, bypass")
+
+func Hook() {
+ if *mode == "bypass" {
+ return
+ }
+ web.SetHTTPDoForTesting(Do)
+}
+
+func Unhook() {
+ web.SetHTTPDoForTesting(nil)
+}
+
+func Print() {
+ web.SetHTTPDoForTesting(DoPrint)
+}
+
+var responses struct {
+ mu sync.Mutex
+ byURL map[string]*respEntry
+}
+
+type respEntry struct {
+ status string
+ code int
+ hdr http.Header
+ body []byte
+}
+
+func Serve(url string, status string, hdr http.Header, body []byte) {
+ if status == "" {
+ status = "200 OK"
+ }
+ code, err := strconv.Atoi(strings.Fields(status)[0])
+ if err != nil {
+ panic("bad Serve status - " + status + " - " + err.Error())
+ }
+
+ responses.mu.Lock()
+ defer responses.mu.Unlock()
+
+ if responses.byURL == nil {
+ responses.byURL = make(map[string]*respEntry)
+ }
+ responses.byURL[url] = &respEntry{status: status, code: code, hdr: web.CopyHeader(hdr), body: body}
+}
+
+func Do(req *http.Request) (*http.Response, error) {
+ if req.Method != "GET" {
+ return nil, fmt.Errorf("bad method - must be GET")
+ }
+
+ responses.mu.Lock()
+ e := responses.byURL[req.URL.String()]
+ responses.mu.Unlock()
+
+ if e == nil {
+ if *mode == "record" {
+ loaded.mu.Lock()
+ if len(loaded.did) != 1 {
+ loaded.mu.Unlock()
+ return nil, fmt.Errorf("cannot use -webtest=record with multiple loaded response files")
+ }
+ var file string
+ for file = range loaded.did {
+ break
+ }
+ loaded.mu.Unlock()
+ return doSave(file, req)
+ }
+ e = &respEntry{code: 599, status: "599 unexpected request (no canned response)"}
+ }
+ resp := &http.Response{
+ Status: e.status,
+ StatusCode: e.code,
+ Header: web.CopyHeader(e.hdr),
+ Body: ioutil.NopCloser(bytes.NewReader(e.body)),
+ }
+ return resp, nil
+}
+
+func DoPrint(req *http.Request) (*http.Response, error) {
+ return doSave("", req)
+}
+
+func doSave(file string, req *http.Request) (*http.Response, error) {
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ data, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ return nil, err
+ }
+ resp.Body = ioutil.NopCloser(bytes.NewReader(data))
+
+ var f *os.File
+ if file == "" {
+ f = os.Stderr
+ } else {
+ f, err = os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+ }
+
+ fmt.Fprintf(f, "GET %s\n", req.URL.String())
+ fmt.Fprintf(f, "%s\n", resp.Status)
+ var keys []string
+ for k := range resp.Header {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ if k == "Set-Cookie" {
+ continue
+ }
+ for _, v := range resp.Header[k] {
+ fmt.Fprintf(f, "%s: %s\n", k, v)
+ }
+ }
+ fmt.Fprintf(f, "\n")
+ if utf8.Valid(data) && !bytes.Contains(data, []byte("\nGET")) && !isHexDump(data) {
+ fmt.Fprintf(f, "%s\n\n", data)
+ } else {
+ fmt.Fprintf(f, "%s\n", hex.Dump(data))
+ }
+ return resp, err
+}
+
+var loaded struct {
+ mu sync.Mutex
+ did map[string]bool
+}
+
+func LoadOnce(file string) {
+ loaded.mu.Lock()
+ if loaded.did[file] {
+ loaded.mu.Unlock()
+ return
+ }
+ if loaded.did == nil {
+ loaded.did = make(map[string]bool)
+ }
+ loaded.did[file] = true
+ loaded.mu.Unlock()
+
+ f, err := os.Open(file)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+
+ b := bufio.NewReader(f)
+ var ungetLine string
+ nextLine := func() string {
+ if ungetLine != "" {
+ l := ungetLine
+ ungetLine = ""
+ return l
+ }
+ line, err := b.ReadString('\n')
+ if err != nil {
+ if err == io.EOF {
+ return ""
+ }
+ log.Fatalf("%s: unexpected read error: %v", file, err)
+ }
+ return line
+ }
+
+ for {
+ line := nextLine()
+ if line == "" { // EOF
+ break
+ }
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "#") || line == "" {
+ continue
+ }
+ if !strings.HasPrefix(line, "GET ") {
+ log.Fatalf("%s: malformed GET line: %s", file, line)
+ }
+ url := line[len("GET "):]
+ status := nextLine()
+ if _, err := strconv.Atoi(strings.Fields(status)[0]); err != nil {
+ log.Fatalf("%s: malformed status line (after GET %s): %s", file, url, status)
+ }
+ hdr := make(http.Header)
+ for {
+ kv := strings.TrimSpace(nextLine())
+ if kv == "" {
+ break
+ }
+ i := strings.Index(kv, ":")
+ if i < 0 {
+ log.Fatalf("%s: malformed header line (after GET %s): %s", file, url, kv)
+ }
+ k, v := kv[:i], strings.TrimSpace(kv[i+1:])
+ hdr[k] = append(hdr[k], v)
+ }
+
+ var body []byte
+ Body:
+ for n := 0; ; n++ {
+ line := nextLine()
+ if n == 0 && isHexDump([]byte(line)) {
+ ungetLine = line
+ b, err := parseHexDump(nextLine)
+ if err != nil {
+ log.Fatalf("%s: malformed hex dump (after GET %s): %v", file, url, err)
+ }
+ body = b
+ break
+ }
+ if line == "" { // EOF
+ for i := 0; i < 2; i++ {
+ if len(body) > 0 && body[len(body)-1] == '\n' {
+ body = body[:len(body)-1]
+ }
+ }
+ break
+ }
+ body = append(body, line...)
+ for line == "\n" {
+ line = nextLine()
+ if strings.HasPrefix(line, "GET ") {
+ ungetLine = line
+ body = body[:len(body)-1]
+ if len(body) > 0 {
+ body = body[:len(body)-1]
+ }
+ break Body
+ }
+ body = append(body, line...)
+ }
+ }
+
+ Serve(url, status, hdr, body)
+ }
+}
+
+func isHexDump(data []byte) bool {
+ return bytes.HasPrefix(data, []byte("00000000 ")) || bytes.HasPrefix(data, []byte("0000000 "))
+}
+
+// parseHexDump parses the hex dump in text, which should be the
+// output of "hexdump -C" or Plan 9's "xd -b" or Go's hex.Dump
+// and returns the original data used to produce the dump.
+// It is meant to enable storing golden binary files as text, so that
+// changes to the golden files can be seen during code reviews.
+func parseHexDump(nextLine func() string) ([]byte, error) {
+ var out []byte
+ for {
+ line := nextLine()
+ if line == "" || line == "\n" {
+ break
+ }
+ if i := strings.Index(line, "|"); i >= 0 { // remove text dump
+ line = line[:i]
+ }
+ f := strings.Fields(line)
+ if len(f) > 1+16 {
+ return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
+ }
+ if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
+ continue
+ }
+ addr64, err := strconv.ParseUint(f[0], 16, 0)
+ if err != nil {
+ return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
+ }
+ addr := int(addr64)
+ if len(out) < addr {
+ out = append(out, make([]byte, addr-len(out))...)
+ }
+ for _, x := range f[1:] {
+ val, err := strconv.ParseUint(x, 16, 8)
+ if err != nil {
+ return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
+ }
+ out = append(out, byte(val))
+ }
+ }
+ return out, nil
+}
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
+ "cmd/go/internal/search"
)
var CmdBuild = &base.Command{
}
var haveNonMeta bool
for _, arg := range args {
- if load.IsMetaPackage(arg) {
+ if search.IsMetaPackage(arg) {
appendName(arg)
} else {
haveNonMeta = true
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
+ "cmd/go/internal/vgo"
)
+func init() {
+ vgo.InstallHook = func(args []string) {
+ CmdInstall.Run(CmdInstall, args)
+ }
+}
+
// actionList returns the list of actions in the dag rooted at root
// as visited in a depth-first post-order traversal.
func actionList(root *Action) []*Action {
fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)
}
+ if p.Internal.BuildInfo != "" {
+ if err := b.writeFile(objdir+"_gomod_.go", vgo.ModInfoProg(p.Internal.BuildInfo)); err != nil {
+ return err
+ }
+ gofiles = append(gofiles, objdir+"_gomod_.go")
+ }
+
// Compile Go.
objpkg := objdir + "_pkg_.a"
ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles)
--- /dev/null
+package x
+
+import _ "appengine"
+import _ "nonexistent.rsc.io" // domain does not exist
--- /dev/null
+module m
+
+replace x v1.0.0 => ./x
+
+replace y v1.0.0 => ./y
+
+replace z v1.0.0 => ./z
+
+replace w v1.0.0 => ./w
+
+require (
+ w v1.0.0
+ x v1.0.0
+ y v1.0.0
+ z v1.0.0
+)
--- /dev/null
+package m
+
+import _ "x"
--- /dev/null
+// +build abc
+
+package mMmMmMm
+
+import _ "y"
--- /dev/null
+// +build !abc
+
+package m
+
+import _ "z"