Prevent and detect overlapping dependencies
The fetch rewrite will use these to get a simpler, more consistent
behavior. Updates #32
diff --git a/delete.go b/delete.go
index a1fe3b4..899a993 100644
--- a/delete.go
+++ b/delete.go
@@ -51,6 +51,10 @@
if err != nil {
return fmt.Errorf("could not get dependency: %v", err)
}
+ if p != dependency.Importpath {
+ return fmt.Errorf("a parent of the specified dependency is vendored, remove that instead: %v",
+ dependency.Importpath)
+ }
dependencies = append(dependencies, dependency)
}
diff --git a/gbvendor/manifest.go b/gbvendor/manifest.go
index 6bb0e27..6d68e74 100644
--- a/gbvendor/manifest.go
+++ b/gbvendor/manifest.go
@@ -3,11 +3,13 @@
import (
"bytes"
"encoding/json"
+ "errors"
"fmt"
"io"
"os"
"reflect"
"sort"
+ "strings"
)
// gb-vendor manifest support
@@ -21,11 +23,20 @@
Dependencies []Dependency `json:"dependencies"`
}
+var (
+ depPresent = errors.New("dependency already present")
+ depSubPkgPresent = errors.New("subpackages of this dependency are already present")
+ depMissing = errors.New("dependency does not exist")
+)
+
// AddDependency adds a Dependency to the current Manifest.
// If the dependency exists already then it returns and error.
func (m *Manifest) AddDependency(dep Dependency) error {
if m.HasImportpath(dep.Importpath) {
- return fmt.Errorf("already registered")
+ return depPresent
+ }
+ if m.GetSubpackages(dep.Importpath) != nil {
+ return depSubPkgPresent
}
m.Dependencies = append(m.Dependencies, dep)
return nil
@@ -40,26 +51,39 @@
return nil
}
}
- return fmt.Errorf("dependency does not exist")
+ return depMissing
}
-// HasImportpath reports whether the Manifest contains the import path.
+// HasImportpath reports whether the Manifest contains the import path,
+// or a parent of it.
func (m *Manifest) HasImportpath(path string) bool {
_, err := m.GetDependencyForImportpath(path)
return err == nil
}
-// GetDependencyForRepository return a dependency for specified URL
-// If the dependency does not exist it returns an error
+// GetDependencyForRepository return a dependency for specified import
+// path. Note that it might be a parent of the specified path.
+// If the dependency does not exist it returns an error.
func (m *Manifest) GetDependencyForImportpath(path string) (Dependency, error) {
for _, d := range m.Dependencies {
- if d.Importpath == path {
+ if strings.HasPrefix(path, d.Importpath) {
return d, nil
}
}
return Dependency{}, fmt.Errorf("dependency for %s does not exist", path)
}
+// GetSubpackages returns any Dependency in the Manifest that is a subpackage
+// of the given import path.
+func (m *Manifest) GetSubpackages(path string) (deps []Dependency) {
+ for _, d := range m.Dependencies {
+ if path != d.Importpath && strings.HasPrefix(d.Importpath, path) {
+ deps = append(deps, d)
+ }
+ }
+ return
+}
+
// Dependency describes one vendored import path of code
// A Dependency is an Importpath sources from a Respository
// at Revision from Path.
@@ -142,13 +166,24 @@
return nil, err
}
defer f.Close()
- return readManifest(f)
-}
-func readManifest(r io.Reader) (*Manifest, error) {
var m Manifest
- d := json.NewDecoder(r)
- err := d.Decode(&m)
+ d := json.NewDecoder(f)
+ err = d.Decode(&m)
+
+ // Pass all dependencies through AddDependency to detect overlap
+ deps := m.Dependencies
+ m.Dependencies = nil
+ sort.Sort(byImportpath(deps)) // so that subpackages come after parents
+ for _, d := range deps {
+ if err := m.AddDependency(d); err == depPresent {
+ fmt.Fprintln(os.Stderr, "WARNING: overlapping dependency detected:", d.Importpath)
+ fmt.Fprintln(os.Stderr, "The subpackage will be ignored to fix undefined behavior. See https://git.io/vwK4B")
+ } else if err != nil {
+ return nil, err
+ }
+ }
+
return &m, err
}