Catch back up to master again
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c1187a..68828a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+# Unreleased
+
+## Added
+- #533: Log VCS output with debug (`--debug` switch) when there was a VCS error (thanks @atombender)
+
+## Changed
+- #521: Sort subpackages for glide.yaml and glide.lock to avoid spurious diffs
+- #487: Skip lookup of subpackage location when parent repo is already known.
+ This skips unnecessary network requests (thanks @hori-ryota)
+
+## Fixed
+- #517: Fixed failure to install testImport from lock when no imports present
+ or when same dependency on both import and testImport
+- #440: Fixed panic in `glide tree` when walking the filesystem (thanks @abhin4v)
+- #529: --delete flag deleted and re-downloaded transitive dependencies
+- #535: Resolve vendor directory symlinks (thanks @Fugiman)
+
+# Release 0.11.1 (2016-07-21)
+
+## Fixed
+- #505: Ignored dependency showing up in testImport
+
# Release 0.11.0 (2016-07-05)
## Added
diff --git a/action/get.go b/action/get.go
index ff9ed7d..8b29cdb 100644
--- a/action/get.go
+++ b/action/get.go
@@ -116,7 +116,10 @@
if err != nil {
msg.Die("Failed to generate config hash. Unable to generate lock file.")
}
- lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ lock, err := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
+ if err != nil {
+ msg.Die("Failed to generate lock file: %s", err)
+ }
if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
msg.Die("Failed to write glide lock file: %s", err)
}
diff --git a/cfg/config.go b/cfg/config.go
index 3046a74..a9e6930 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -6,6 +6,7 @@
"fmt"
"io/ioutil"
"reflect"
+ "sort"
"strings"
"github.com/Masterminds/glide/util"
@@ -693,6 +694,7 @@
s = append(s, item)
}
}
+ sort.Strings(s)
return s
}
diff --git a/cfg/lock.go b/cfg/lock.go
index ac03f91..b822f29 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -3,6 +3,7 @@
import (
"crypto/sha256"
"encoding/hex"
+ "fmt"
"io/ioutil"
"sort"
"strings"
@@ -76,6 +77,39 @@
return yml, nil
}
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts import subpackages lexicographically for reproducibility.
+func (lf *Lockfile) MarshalYAML() (interface{}, error) {
+ for _, imp := range lf.Imports {
+ sort.Strings(imp.Subpackages)
+ }
+
+ // Ensure elements on testImport don't already exist on import.
+ var newDI Locks
+ var found bool
+ for _, imp := range lf.DevImports {
+ found = false
+ for i := 0; i < len(lf.Imports); i++ {
+ if lf.Imports[i].Name == imp.Name {
+ found = true
+ if lf.Imports[i].Version != imp.Version {
+ return lf, fmt.Errorf("Generating lock YAML produced conflicting versions of %s. import (%s), testImport (%s)", imp.Name, lf.Imports[i].Version, imp.Version)
+ }
+ }
+ }
+
+ if !found {
+ newDI = append(newDI, imp)
+ }
+ }
+ lf.DevImports = newDI
+
+ for _, imp := range lf.DevImports {
+ sort.Strings(imp.Subpackages)
+ }
+ return lf, nil
+}
+
// WriteFile writes a Glide lock file.
//
// This is a convenience function that marshals the YAML and then writes it to
@@ -238,7 +272,7 @@
}
// NewLockfile is used to create an instance of Lockfile.
-func NewLockfile(ds, tds Dependencies, hash string) *Lockfile {
+func NewLockfile(ds, tds Dependencies, hash string) (*Lockfile, error) {
lf := &Lockfile{
Hash: hash,
Updated: time.Now(),
@@ -252,13 +286,26 @@
sort.Sort(lf.Imports)
+ var found bool
for i := 0; i < len(tds); i++ {
- lf.DevImports[i] = LockFromDependency(tds[i])
+ found = false
+ for ii := 0; ii < len(ds); ii++ {
+ if ds[ii].Name == tds[i].Name {
+ found = true
+ if ds[ii].Reference != tds[i].Reference {
+ return &Lockfile{}, fmt.Errorf("Generating lock produced conflicting versions of %s. import (%s), testImport (%s)", tds[i].Name, ds[ii].Reference, tds[i].Reference)
+ }
+ break
+ }
+ }
+ if !found {
+ lf.DevImports[i] = LockFromDependency(tds[i])
+ }
}
sort.Sort(lf.DevImports)
- return lf
+ return lf, nil
}
// LockfileFromMap takes a map of dependencies and generates a lock Lockfile instance.
diff --git a/cfg/lock_test.go b/cfg/lock_test.go
index 78dcee3..81edcd0 100644
--- a/cfg/lock_test.go
+++ b/cfg/lock_test.go
@@ -2,6 +2,7 @@
import (
"sort"
+ "strings"
"testing"
)
@@ -32,3 +33,80 @@
t.Error("Sorting of dependencies failed")
}
}
+
+const inputSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+ version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+ subpackages:
+ - plugin/equal
+ - sortkeys
+ - plugin/face
+ - plugin/gostring
+ - vanity
+ - plugin/grpc
+ - plugin/marshalto
+ - plugin/populate
+ - plugin/oneofcheck
+ - plugin/size
+ - plugin/stringer
+ - plugin/defaultcheck
+ - plugin/embedcheck
+ - plugin/description
+ - plugin/enumstringer
+ - gogoproto
+ - plugin/testgen
+ - plugin/union
+ - plugin/unmarshal
+ - protoc-gen-gogo/generator
+ - protoc-gen-gogo/plugin
+ - vanity/command
+ - protoc-gen-gogo/descriptor
+ - proto
+`
+const expectSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+ version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+ subpackages:
+ - gogoproto
+ - plugin/defaultcheck
+ - plugin/description
+ - plugin/embedcheck
+ - plugin/enumstringer
+ - plugin/equal
+ - plugin/face
+ - plugin/gostring
+ - plugin/grpc
+ - plugin/marshalto
+ - plugin/oneofcheck
+ - plugin/populate
+ - plugin/size
+ - plugin/stringer
+ - plugin/testgen
+ - plugin/union
+ - plugin/unmarshal
+ - proto
+ - protoc-gen-gogo/descriptor
+ - protoc-gen-gogo/generator
+ - protoc-gen-gogo/plugin
+ - sortkeys
+ - vanity
+ - vanity/command
+`
+
+func TestSortSubpackages(t *testing.T) {
+ lf, err := LockfileFromYaml([]byte(inputSubpkgYaml))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := lf.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !strings.Contains(string(out), expectSubpkgYaml) {
+ t.Errorf("Expected %q\nto contain\n%q")
+ }
+}
diff --git a/dependency/delete.go b/dependency/delete.go
index 39a6367..6456370 100644
--- a/dependency/delete.go
+++ b/dependency/delete.go
@@ -28,6 +28,9 @@
for _, dep := range conf.Imports {
pkgList = append(pkgList, dep.Name)
}
+ for _, dep := range conf.DevImports {
+ pkgList = append(pkgList, dep.Name)
+ }
var searchPath string
var markForDelete []string
diff --git a/godep/strip/strip.go b/godep/strip/strip.go
index e70d607..39d54e1 100644
--- a/godep/strip/strip.go
+++ b/godep/strip/strip.go
@@ -82,11 +82,11 @@
func rewriteGodepfilesHandler(path string, info os.FileInfo, err error) error {
name := info.Name()
- if name == "testdata" || name == "vendor" {
- return filepath.SkipDir
- }
if info.IsDir() {
+ if name == "testdata" || name == "vendor" {
+ return filepath.SkipDir
+ }
return nil
}
diff --git a/msg/msg.go b/msg/msg.go
index 34213fd..8b0f077 100644
--- a/msg/msg.go
+++ b/msg/msg.go
@@ -7,6 +7,8 @@
"os"
"strings"
"sync"
+
+ "github.com/Masterminds/vcs"
)
// Messenger provides the underlying implementation that displays output to
@@ -176,7 +178,6 @@
// locked to avoid displaying one message in the middle of another one.
m.Lock()
defer m.Unlock()
-
// Get rid of the annoying fact that messages need \n at the end, but do
// it in a backward compatible way.
if !strings.HasSuffix(msg, "\n") {
@@ -188,6 +189,21 @@
} else {
fmt.Fprintf(m.Stderr, msg, args...)
}
+
+ // If an arg is a vcs error print the output if in debug mode. This is
+ // capured here rather than calling Debug because concurrent operations
+ // could cause other messages to appear between the initial error and the
+ // debug output by unlocking and calling Debug.
+ if len(args) != 0 && !m.Quiet && m.IsDebugging {
+ if err, ok := args[len(args)-1].(error); ok {
+ switch t := err.(type) {
+ case *vcs.LocalError:
+ fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+ case *vcs.RemoteError:
+ fmt.Fprintf(m.Stderr, "[DEBUG]\tOutput was: %s", strings.TrimSpace(t.Out()))
+ }
+ }
+ }
}
// Msg prints a message with optional arguments, that can be printed, of
diff --git a/path/path.go b/path/path.go
index 0b19442..0c3cee1 100644
--- a/path/path.go
+++ b/path/path.go
@@ -80,6 +80,29 @@
gopath := filepath.Join(yamldir, VendorDir)
+ // Resolve symlinks
+ info, err := os.Lstat(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+ for i := 0; IsLink(info) && i < 255; i++ {
+ p, err := os.Readlink(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+
+ if filepath.IsAbs(p) {
+ gopath = p
+ } else {
+ gopath = filepath.Join(filepath.Dir(gopath), p)
+ }
+
+ info, err = os.Lstat(gopath)
+ if err != nil {
+ return gopath, nil
+ }
+ }
+
return gopath, nil
}
diff --git a/path/path_test.go b/path/path_test.go
index 0c8a221..77a0276 100644
--- a/path/path_test.go
+++ b/path/path_test.go
@@ -34,6 +34,7 @@
t.Fatal(err)
}
wd, _ := os.Getwd()
+
os.Chdir(filepath.Join(td, "a/b/c"))
res, err := Vendor()
if err != nil {
@@ -43,6 +44,17 @@
if res != expect {
t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
}
+
+ os.Chdir(filepath.Join(td, "x/y/z"))
+ res, err = Vendor()
+ if err != nil {
+ t.Errorf("Failed to resolve vendor directory: %s", err)
+ }
+ expect = filepath.Join(td, "x", "symlinked_vendor")
+ if res != expect {
+ t.Errorf("Failed to find vendor: expected %s got %s", expect, res)
+ }
+
os.Chdir(wd)
}
func TestGlide(t *testing.T) {
diff --git a/path/strip.go b/path/strip.go
index 1162f38..be00be9 100644
--- a/path/strip.go
+++ b/path/strip.go
@@ -11,36 +11,36 @@
// StripVcs removes VCS metadata (.git, .hg, .bzr, .svn) from the vendor/
// directory.
func StripVcs() error {
- if _, err := os.Stat(VendorDir); err != nil {
+ searchPath, _ := Vendor()
+ if _, err := os.Stat(searchPath); err != nil {
if os.IsNotExist(err) {
msg.Debug("Vendor directory does not exist.")
}
return err
}
- return filepath.Walk(VendorDir, stripHandler)
-}
-func stripHandler(path string, info os.FileInfo, err error) error {
+ return filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
+ name := info.Name()
+ if name == ".git" || name == ".bzr" || name == ".svn" || name == ".hg" {
+ if _, err := os.Stat(path); err == nil {
+ if info.IsDir() {
+ msg.Info("Removing: %s", path)
+ return os.RemoveAll(path)
+ }
- name := info.Name()
- if name == ".git" || name == ".bzr" || name == ".svn" || name == ".hg" {
- if _, err := os.Stat(path); err == nil {
- if info.IsDir() {
- msg.Info("Removing: %s", path)
- return os.RemoveAll(path)
+ msg.Debug("%s is not a directory. Skipping removal", path)
+ return nil
}
-
- msg.Debug("%s is not a directory. Skipping removal", path)
- return nil
}
- }
- return nil
+ return nil
+ })
}
// StripVendor removes nested vendor and Godeps/_workspace/ directories.
func StripVendor() error {
- if _, err := os.Stat(VendorDir); err != nil {
+ searchPath, _ := Vendor()
+ if _, err := os.Stat(searchPath); err != nil {
if os.IsNotExist(err) {
msg.Debug("Vendor directory does not exist.")
}
@@ -48,33 +48,29 @@
return err
}
- err := filepath.Walk(VendorDir, stripVendorHandler)
+ err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
+ // Skip the base vendor directory
+ if path == searchPath {
+ return nil
+ }
+
+ name := info.Name()
+ if name == "vendor" {
+ if _, err := os.Stat(path); err == nil {
+ if info.IsDir() {
+ msg.Info("Removing: %s", path)
+ return os.RemoveAll(path)
+ }
+
+ msg.Debug("%s is not a directory. Skipping removal", path)
+ return nil
+ }
+ }
+ return nil
+ })
if err != nil {
return err
}
- err = strip.GodepWorkspace(VendorDir)
-
- return err
-}
-
-func stripVendorHandler(path string, info os.FileInfo, err error) error {
- // Skip the base vendor directory
- if path == VendorDir {
- return nil
- }
-
- name := info.Name()
- if name == "vendor" {
- if _, err := os.Stat(path); err == nil {
- if info.IsDir() {
- msg.Info("Removing: %s", path)
- return os.RemoveAll(path)
- }
-
- msg.Debug("%s is not a directory. Skipping removal", path)
- return nil
- }
- }
- return nil
+ return strip.GodepWorkspace(searchPath)
}
diff --git a/path/strip_test.go b/path/strip_test.go
index 6c11194..23e9d94 100644
--- a/path/strip_test.go
+++ b/path/strip_test.go
@@ -21,36 +21,44 @@
}()
// Make VCS directories.
- gp := filepath.Join(tempDir, ".git")
+ v := filepath.Join(tempDir, VendorDir)
+ err = os.Mkdir(v, 0755)
+ if err != nil {
+ t.Error(err)
+ }
+
+ gp := filepath.Join(tempDir, VendorDir, ".git")
err = os.Mkdir(gp, 0755)
if err != nil {
t.Error(err)
}
- bp := filepath.Join(tempDir, ".bzr")
+ bp := filepath.Join(tempDir, VendorDir, ".bzr")
err = os.Mkdir(bp, 0755)
if err != nil {
t.Error(err)
}
- hp := filepath.Join(tempDir, ".hg")
+ hp := filepath.Join(tempDir, VendorDir, ".hg")
err = os.Mkdir(hp, 0755)
if err != nil {
t.Error(err)
}
- sp := filepath.Join(tempDir, ".svn")
+ sp := filepath.Join(tempDir, VendorDir, ".svn")
err = os.Mkdir(sp, 0755)
if err != nil {
t.Error(err)
}
- ov := VendorDir
- VendorDir = tempDir
+ wd, _ := os.Getwd()
+ os.Chdir(tempDir)
- StripVcs()
+ if err := StripVcs(); err != nil {
+ t.Errorf("Failed to strip vcs: %s", err)
+ }
- VendorDir = ov
+ os.Chdir(wd)
if _, err := os.Stat(gp); !os.IsNotExist(err) {
t.Error(".git directory not deleted")
diff --git a/repo/installer.go b/repo/installer.go
index cce24ef..0d6a335 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -104,8 +104,8 @@
newConf.DeDupe()
- if len(newConf.Imports) == 0 {
- msg.Info("No dependencies found. Nothing installed.\n")
+ if len(newConf.Imports) == 0 && len(newConf.DevImports) == 0 {
+ msg.Info("No dependencies found. Nothing installed.")
return newConf, nil
}
@@ -192,6 +192,9 @@
var tdeps cfg.Dependencies
for _, v := range imps {
n := res.Stripv(v)
+ if conf.HasIgnore(n) {
+ continue
+ }
rt, sub := util.NormalizeName(n)
if sub == "" {
sub = "."
@@ -210,6 +213,9 @@
if i.ResolveTest {
for _, v := range timps {
n := res.Stripv(v)
+ if conf.HasIgnore(n) {
+ continue
+ }
rt, sub := util.NormalizeName(n)
if sub == "" {
sub = "."
diff --git a/repo/set_reference.go b/repo/set_reference.go
index 4363459..f5891ac 100644
--- a/repo/set_reference.go
+++ b/repo/set_reference.go
@@ -3,9 +3,11 @@
import (
"sync"
+ "github.com/Masterminds/glide/cache"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
+ "github.com/codegangsta/cli"
)
// SetReference is a command to set the VCS reference (commit id, tag, etc) for
@@ -25,15 +27,39 @@
done := make(chan struct{}, concurrentWorkers)
in := make(chan *cfg.Dependency, concurrentWorkers)
var wg sync.WaitGroup
+ var lock sync.Mutex
+ var returnErr error
for i := 0; i < concurrentWorkers; i++ {
go func(ch <-chan *cfg.Dependency) {
for {
select {
case dep := <-ch:
+ var loc string
+ if dep.Repository != "" {
+ loc = dep.Repository
+ } else {
+ loc = "https://" + dep.Name
+ }
+ key, err := cache.Key(loc)
+ if err != nil {
+ msg.Die(err.Error())
+ }
+ cache.Lock(key)
if err := VcsVersion(dep, cwd); err != nil {
msg.Err("Failed to set version on %s to %s: %s\n", dep.Name, dep.Reference, err)
+
+ // Capture the error while making sure the concurrent
+ // operations don't step on each other.
+ lock.Lock()
+ if returnErr == nil {
+ returnErr = err
+ } else {
+ returnErr = cli.NewMultiError(returnErr, err)
+ }
+ lock.Unlock()
}
+ cache.Unlock(key)
wg.Done()
case <-done:
return
@@ -66,5 +92,5 @@
// close(done)
// close(in)
- return nil
+ return returnErr
}
diff --git a/testdata/path/x/glide.yaml b/testdata/path/x/glide.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/glide.yaml
diff --git a/testdata/path/x/vendor b/testdata/path/x/vendor
new file mode 120000
index 0000000..5b13353
--- /dev/null
+++ b/testdata/path/x/vendor
@@ -0,0 +1 @@
+symlinked_vendor
\ No newline at end of file
diff --git a/testdata/path/x/y/z/placeholder.empty b/testdata/path/x/y/z/placeholder.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/path/x/y/z/placeholder.empty
diff --git a/tree/tree.go b/tree/tree.go
index 0dc3df7..e16478c 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -45,6 +45,10 @@
func walkDeps(b *util.BuildCtxt, base, myName string) []string {
externalDeps := []string{}
filepath.Walk(base, func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
if !dependency.IsSrcDir(fi) {
if fi.IsDir() {
return filepath.SkipDir
diff --git a/util/normalizename_test.go b/util/normalizename_test.go
index c6f4d02..d5a454b 100644
--- a/util/normalizename_test.go
+++ b/util/normalizename_test.go
@@ -31,11 +31,18 @@
extra: "",
},
{
+ input: "otherurl/example/root/sub",
+ root: "otherurl/example/root",
+ extra: "sub",
+ },
+ {
input: "net",
root: "net",
extra: "",
},
}
+ remotePackageCache["otherurl/example/root"] = "otherurl/example/root"
+
for _, test := range packages {
root, extra := NormalizeName(test.input)
if root != test.root {
diff --git a/util/util.go b/util/util.go
index a5b346f..f0854d2 100644
--- a/util/util.go
+++ b/util/util.go
@@ -110,7 +110,7 @@
func checkRemotePackageCache(pkg string) (string, bool) {
for k, v := range remotePackageCache {
- if pkg == k {
+ if pkg == k || strings.HasPrefix(pkg, k + "/") {
return v, true
}
}
@@ -290,7 +290,7 @@
out, err := exec.Command(goExecutable, "env", "GOROOT").Output()
if goRoot = strings.TrimSpace(string(out)); len(goRoot) == 0 || err != nil {
return nil, fmt.Errorf("Please set the $GOROOT environment " +
- "variable to use this command\n")
+ "variable to use this command\n")
}
buildContext.GOROOT = goRoot
}