Rewrite fetching and dependencies logic The new code uses the parser to identify import statements, so ignores build tags (fixes #34) and package validity (fixes #39). Critically, the new code understand vendor folders (fixes #27). The recursion logic is now smart about overlapping packages, ignoring subpackages of existing ones and deleting subpackages when fetching the parent (fixes #32).
diff --git a/alldocs.go b/alldocs.go index f6727ea..149a698 100644 --- a/alldocs.go +++ b/alldocs.go
@@ -25,6 +25,11 @@ fetch vendors an upstream import path. +Recursive dependencies are fetched (at their master/tip/HEAD revision), unless they +or their parent package are already present. + +If a subpackage of a dependency being fetched is already present, it will be deleted. + The import path may include a url scheme. This may be useful when fetching dependencies from private repositories that cannot be probed.
diff --git a/delete.go b/delete.go index 899a993..5f97f93 100644 --- a/delete.go +++ b/delete.go
@@ -36,7 +36,7 @@ return fmt.Errorf("delete: you cannot specify path and --all flag at once") } - m, err := vendor.ReadManifest(manifestFile()) + m, err := vendor.ReadManifest(manifestFile) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } @@ -65,12 +65,12 @@ return fmt.Errorf("dependency could not be deleted: %v", err) } - if err := fileutils.RemoveAll(filepath.Join(vendorDir(), filepath.FromSlash(path))); err != nil { + if err := fileutils.RemoveAll(filepath.Join(vendorDir, filepath.FromSlash(path))); err != nil { // TODO(dfc) need to apply vendor.cleanpath here to remove indermediate directories. return fmt.Errorf("dependency could not be deleted: %v", err) } } - return vendor.WriteManifest(manifestFile(), m) + return vendor.WriteManifest(manifestFile, m) }, AddFlags: addDeleteFlags, }
diff --git a/fetch.go b/fetch.go index 89547a8..cb9814c 100644 --- a/fetch.go +++ b/fetch.go
@@ -3,12 +3,11 @@ import ( "flag" "fmt" - "go/build" "log" "net/url" + "os" "path/filepath" - "runtime" - "sort" + "strings" "github.com/FiloSottile/gvt/fileutils" "github.com/FiloSottile/gvt/gbvendor" @@ -22,8 +21,6 @@ insecure bool // Allow the use of insecure protocols tests bool all bool - - recurse bool // should we fetch recursively ) func addFetchFlags(fs *flag.FlagSet) { @@ -42,8 +39,10 @@ Short: "fetch a remote dependency", Long: `fetch vendors an upstream import path. -Recursive dependencies are fetched at their master/tip/HEAD revision, unless they -or their parent package is already present. +Recursive dependencies are fetched (at their master/tip/HEAD revision), unless they +or their parent package are already present. + +If a subpackage of a dependency being fetched is already present, it will be deleted. The import path may include a url scheme. This may be useful when fetching dependencies from private repositories that cannot be probed. @@ -73,8 +72,7 @@ return fmt.Errorf("fetch: import path missing") case 1: path := args[0] - recurse = !noRecurse - return fetch(path, recurse) + return fetch(path) default: return fmt.Errorf("more than one import path supplied") } @@ -82,31 +80,102 @@ AddFlags: addFetchFlags, } -func fetch(path string, recurse bool) error { - m, err := vendor.ReadManifest(manifestFile()) +var ( + fetchRoot string // where the current session started + fetchedToday []string // packages fetched during this session +) + +func fetch(path string) error { + m, err := vendor.ReadManifest(manifestFile) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } - repo, extra, err := vendor.DeduceRemoteRepo(path, insecure) - if err != nil { - return err + fetchRoot = stripscheme(path) + return fetchRecursive(m, path, 0) +} + +func fetchRecursive(m *vendor.Manifest, fullPath string, level int) error { + path := stripscheme(fullPath) + + // Don't even bother the user about skipping packages we just fetched + for _, p := range fetchedToday { + if contains(p, path) { + return nil + } } - // strip of any scheme portion from the path, it is already - // encoded in the repo. - path = stripscheme(path) - + // First, check if this or a parent is already vendored if m.HasImportpath(path) { - return fmt.Errorf("%s is already vendored", path) + if level == 0 { + return fmt.Errorf("%s or a parent of it is already vendored", path) + } else { + // TODO: print a different message for packages fetched during this session + logIndent(level, "Skipping (existing):", path) + return nil + } } - wc, err := GlobalDownloader.Get(repo, branch, tag, revision) + // Next, check if we are trying to vendor from the same repository we are in + if importPath != "" && contains(importPath, path) { + if level == 0 { + return fmt.Errorf("refusing to vendor a subpackage of \".\"") + } else { + logIndent(level, "Skipping (subpackage of \".\"):", path) + return nil + } + } + if level == 0 { + log.Println("Fetching:", path) + } else { + logIndent(level, "Fetching recursive dependency:", path) + } + + // Finally, check if we already vendored a subpackage and remove it + parentOfRoot := false + for _, subp := range m.GetSubpackages(path) { + if contains(subp.Importpath, fetchRoot) { + // Through dependencies we ended up fetching a parent of the starting package + parentOfRoot = true // use the requested tag/branch/revision + } else { + ignore := false + for _, d := range fetchedToday { + if contains(d, subp.Importpath) { + ignore = true // No need to warn the user if we just downloaded it + } + } + if !ignore { + logIndent(level, "Deleting existing subpackage to prevent overlap:", subp.Importpath) + } + } + if err := m.RemoveDependency(subp); err != nil { + return fmt.Errorf("failed to remove subpackage: %v", err) + } + } + if err := fileutils.RemoveAll(filepath.Join(vendorDir, path)); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to remove existing folder: %v", err) + } + + // Find and download the repository + + repo, extra, err := vendor.DeduceRemoteRepo(fullPath, insecure) if err != nil { return err } + var wc vendor.WorkingCopy + if level == 0 || parentOfRoot { + wc, err = GlobalDownloader.Get(repo, branch, tag, revision) + } else { + wc, err = GlobalDownloader.Get(repo, "", "", "") + } + if err != nil { + return err + } + + // Add the dependency to the manifest + rev, err := wc.Revision() if err != nil { return err @@ -132,7 +201,9 @@ return err } - dst := filepath.Join(vendorDir(), dep.Importpath) + // Copy the code to the vendor folder + + dst := filepath.Join(vendorDir, dep.Importpath) src := filepath.Join(wc.Dir(), dep.Path) if err := fileutils.Copypath(dst, src, !dep.NoTests, dep.AllFiles); err != nil { @@ -143,59 +214,36 @@ return err } - if err := vendor.WriteManifest(manifestFile(), m); err != nil { + if err := vendor.WriteManifest(manifestFile, m); err != nil { return err } - if !recurse { - return nil - } + // Recurse - // if we are recursing, overwrite branch, tag and revision - // values so recursive fetching checks out from HEAD. - branch = "" - tag = "" - revision = "" + fetchedToday = append(fetchedToday, path) - for done := false; !done; { - - paths := []struct { - Root, Prefix string - }{ - {filepath.Join(runtime.GOROOT(), "src"), ""}, + if !noRecurse { + // Look for dependencies in src, not going past wc.Dir() when looking for /vendor/, + // knowing that wc.Dir() corresponds to rootRepoPath + if !strings.HasSuffix(dep.Importpath, dep.Path) { + return fmt.Errorf("unable to derive the root repo import path") } - m, err := vendor.ReadManifest(manifestFile()) + rootRepoPath := strings.TrimRight(strings.TrimSuffix(dep.Importpath, dep.Path), "/") + deps, err := vendor.ParseImports(src, wc.Dir(), rootRepoPath) if err != nil { - return err - } - for _, d := range m.Dependencies { - paths = append(paths, struct{ Root, Prefix string }{filepath.Join(vendorDir(), filepath.FromSlash(d.Importpath)), filepath.FromSlash(d.Importpath)}) + return fmt.Errorf("failed to parse imports: %s", err) } - dsm, err := vendor.LoadPaths(paths...) - if err != nil { - return err - } - - is, ok := dsm[filepath.Join(vendorDir(), path)] - if !ok { - return fmt.Errorf("unable to locate depset for %q", path) - } - - missing := findMissing(pkgs(is.Pkgs), dsm) - switch len(missing) { - case 0: - done = true - default: - - // sort keys in ascending order, so the shortest missing import path - // with be fetched first. - keys := keys(missing) - sort.Strings(keys) - pkg := keys[0] - log.Printf("fetching recursive dependency %s", pkg) - if err := fetch(pkg, false); err != nil { - return err + for d := range deps { + if strings.Index(d, ".") == -1 { // TODO: replace this silly heuristic + continue + } + if err := fetchRecursive(m, d, level+1); err != nil { + if strings.HasPrefix(err.Error(), "error fetching") { // I know, ok? + return err + } else { + return fmt.Errorf("error fetching %s: %s", d, err) + } } } } @@ -203,88 +251,10 @@ return nil } -func keys(m map[string]bool) []string { - var s []string - for k := range m { - s = append(s, k) - } - return s -} - -func pkgs(m map[string]*vendor.Pkg) []*vendor.Pkg { - var p []*vendor.Pkg - for _, v := range m { - p = append(p, v) - } - return p -} - -func findMissing(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { - missing := make(map[string]bool) - imports := make(map[string]*vendor.Pkg) - for _, s := range dsm { - for _, p := range s.Pkgs { - imports[p.ImportPath] = p - } - } - - // make fake C package for cgo - imports["C"] = &vendor.Pkg{ - Depset: nil, // probably a bad idea - Package: &build.Package{ - Name: "C", - }, - } - stk := make(map[string]bool) - push := func(v string) { - if stk[v] { - panic(fmt.Sprintln("import loop:", v, stk)) - } - stk[v] = true - } - pop := func(v string) { - if !stk[v] { - panic(fmt.Sprintln("impossible pop:", v, stk)) - } - delete(stk, v) - } - - // checked records import paths who's dependencies are all present - checked := make(map[string]bool) - - var fn func(string) - fn = func(importpath string) { - p, ok := imports[importpath] - if !ok { - missing[importpath] = true - return - } - - // have we already walked this arm, if so, skip it - if checked[importpath] { - return - } - - sz := len(missing) - push(importpath) - for _, i := range p.Imports { - if i == importpath { - continue - } - fn(i) - } - - // if the size of the missing map has not changed - // this entire subtree is complete, mark it as such - if len(missing) == sz { - checked[importpath] = true - } - pop(importpath) - } - for _, pkg := range pkgs { - fn(pkg.ImportPath) - } - return missing +func logIndent(level int, v ...interface{}) { + prefix := strings.Repeat("·", level) + v = append([]interface{}{prefix}, v...) + log.Println(v...) } // stripscheme removes any scheme components from url like paths. @@ -295,3 +265,8 @@ } return u.Host + u.Path } + +// Package a contains package b? +func contains(a, b string) bool { + return a == b || strings.HasPrefix(b, a+"/") +}
diff --git a/gbvendor/depset.go b/gbvendor/depset.go deleted file mode 100644 index b4c40d8..0000000 --- a/gbvendor/depset.go +++ /dev/null
@@ -1,114 +0,0 @@ -package vendor - -import ( - "fmt" - "go/build" - "os" - "path/filepath" - "strings" -) - -// Pkg describes a Go package. -type Pkg struct { - *Depset - *build.Package -} - -// Depset describes a set of related Go packages. -type Depset struct { - Root string - Prefix string - Pkgs map[string]*Pkg -} - -// LoadPaths returns a map of paths to Depsets. -func LoadPaths(paths ...struct{ Root, Prefix string }) (map[string]*Depset, error) { - m := make(map[string]*Depset) - for _, p := range paths { - set, err := LoadTree(p.Root, p.Prefix) - if err != nil { - return nil, err - } - m[set.Root] = set - } - return m, nil -} - -// LoadTree parses a tree of source files into a map of *pkgs. -func LoadTree(root string, prefix string) (*Depset, error) { - d := Depset{ - Root: root, - Prefix: prefix, - Pkgs: make(map[string]*Pkg), - } - fn := func(dir string, fi os.FileInfo) error { - importpath := filepath.Join(prefix, dir[len(root)+1:]) - - // if we're at the root of a tree, skip it - if importpath == "" { - return nil - } - - p, err := loadPackage(&d, dir) - if err != nil { - if _, ok := err.(*build.NoGoError); ok { - return nil - } - return fmt.Errorf("loadPackage(%q, %q): %v", dir, importpath, err) - } - p.ImportPath = filepath.ToSlash(importpath) - if p != nil { - d.Pkgs[p.ImportPath] = p - } - return nil - } - - // handle root of the tree - fi, err := os.Stat(root) - if err != nil { - return nil, err - } - if err := fn(root+string(filepath.Separator), fi); err != nil { - return nil, err - } - - // walk sub directories - err = eachDir(root, fn) - return &d, err -} - -func loadPackage(d *Depset, dir string) (*Pkg, error) { - p := Pkg{ - Depset: d, - } - var err error - - // expolit local import logic - p.Package, err = build.ImportDir(dir, build.ImportComment) - return &p, err -} - -func eachDir(dir string, fn func(string, os.FileInfo) error) error { - f, err := os.Open(dir) - if err != nil { - return err - } - defer f.Close() - files, err := f.Readdir(-1) - for _, fi := range files { - if !fi.IsDir() { - continue - } - if strings.HasPrefix(fi.Name(), "_") || strings.HasPrefix(fi.Name(), ".") || fi.Name() == "testdata" { - continue - } - path := filepath.Join(dir, fi.Name()) - if err := fn(path, fi); err != nil { - return err - } - if err := eachDir(path, fn); err != nil { - return err - } - } - return nil -}
diff --git a/gbvendor/imports.go b/gbvendor/imports.go index 373e3f9..db64a5b 100644 --- a/gbvendor/imports.go +++ b/gbvendor/imports.go
@@ -57,15 +57,13 @@ // // It returns the path to pkgName inside the vendor folder, relative to root. func findVendor(root, start, pkgName string) string { - if !strings.HasPrefix(root, start) { - log.Fatal("Assertion failed:", root, "prefix of", start) + if !strings.HasPrefix(start, root) { + log.Fatalln("Assertion failed:", root, "prefix of", start) } levels := strings.Split(strings.TrimPrefix(start, root), string(filepath.Separator)) - log.Println(root, start, levels, pkgName) for { - candidate := filepath.Join(append(append([]string{root}, levels...), pkgName)...) - log.Println(candidate) + candidate := filepath.Join(append(append([]string{root}, levels...), "vendor", pkgName)...) files, err := ioutil.ReadDir(candidate) if err != nil {
diff --git a/gbvendor/manifest.go b/gbvendor/manifest.go index 6d68e74..0c3334b 100644 --- a/gbvendor/manifest.go +++ b/gbvendor/manifest.go
@@ -6,6 +6,7 @@ "errors" "fmt" "io" + "log" "os" "reflect" "sort" @@ -24,19 +25,19 @@ } var ( - depPresent = errors.New("dependency already present") - depSubPkgPresent = errors.New("subpackages of this dependency are already present") - depMissing = errors.New("dependency does not exist") + 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 depPresent + return DepPresent } if m.GetSubpackages(dep.Importpath) != nil { - return depSubPkgPresent + return DepSubPkgPresent } m.Dependencies = append(m.Dependencies, dep) return nil @@ -51,7 +52,7 @@ return nil } } - return depMissing + return DepMissing } // HasImportpath reports whether the Manifest contains the import path, @@ -66,7 +67,7 @@ // If the dependency does not exist it returns an error. func (m *Manifest) GetDependencyForImportpath(path string) (Dependency, error) { for _, d := range m.Dependencies { - if strings.HasPrefix(path, d.Importpath) { + if path == d.Importpath || strings.HasPrefix(path, d.Importpath+"/") { return d, nil } } @@ -169,16 +170,18 @@ var m Manifest d := json.NewDecoder(f) - err = d.Decode(&m) + if err := d.Decode(&m); err != nil { + return nil, err + } // 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") + if err := m.AddDependency(d); err == DepPresent { + log.Println("WARNING: overlapping dependency detected:", d.Importpath) + log.Println("The subpackage will be ignored to fix undefined behavior. See https://git.io/vwK4B") } else if err != nil { return nil, err }
diff --git a/gbvendor/stringset.go b/gbvendor/stringset.go deleted file mode 100644 index 007eae5..0000000 --- a/gbvendor/stringset.go +++ /dev/null
@@ -1,52 +0,0 @@ -package vendor - -// union returns the union of a and b. -func union(a, b map[string]bool) map[string]bool { - r := make(map[string]bool) - for k := range a { - r[k] = true - } - for k := range b { - r[k] = true - } - return r -} - -// intersection returns the intersection of a and b. -func intersection(a, b map[string]bool) map[string]bool { - r := make(map[string]bool) - for k := range a { - if b[k] { - r[k] = true - } - } - return r -} - -// difference returns the symetric difference of a and b. -func difference(a, b map[string]bool) map[string]bool { - r := make(map[string]bool) - for k := range a { - if !b[k] { - r[k] = true - } - } - for k := range b { - if !a[k] { - r[k] = true - } - } - return r -} - -// contains returns true if a contains all the elements in s. -func contains(a map[string]bool, s ...string) bool { - var r bool - for _, e := range s { - if !a[e] { - return false - } - r = true - } - return r -}
diff --git a/gbvendor/stringset_test.go b/gbvendor/stringset_test.go deleted file mode 100644 index d8781a4..0000000 --- a/gbvendor/stringset_test.go +++ /dev/null
@@ -1,147 +0,0 @@ -package vendor - -import "testing" -import "reflect" - -func set(args ...string) map[string]bool { - r := make(map[string]bool) - for _, a := range args { - r[a] = true - } - return r -} - -func TestUnion(t *testing.T) { - tests := []struct { - a, b map[string]bool - want map[string]bool - }{{ - a: nil, b: nil, - want: set(), - }, { - a: nil, b: set("b"), - want: set("b"), - }, { - a: set("a"), b: nil, - want: set("a"), - }, { - a: set("a"), b: set("b"), - want: set("b", "a"), - }, { - a: set("c"), b: set("c"), - want: set("c"), - }} - - for _, tt := range tests { - got := union(tt.a, tt.b) - if !reflect.DeepEqual(tt.want, got) { - t.Errorf("union(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) - } - } -} - -func TestIntersection(t *testing.T) { - tests := []struct { - a, b map[string]bool - want map[string]bool - }{{ - a: nil, b: nil, - want: set(), - }, { - a: nil, b: set("b"), - want: set(), - }, { - a: set("a"), b: nil, - want: set(), - }, { - a: set("a"), b: set("b"), - want: set(), - }, { - a: set("c"), b: set("c"), - want: set("c"), - }, { - a: set("a", "c"), b: set("b", "c"), - want: set("c"), - }} - - for _, tt := range tests { - got := intersection(tt.a, tt.b) - if !reflect.DeepEqual(tt.want, got) { - t.Errorf("intersection(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) - } - } -} - -func TestDifference(t *testing.T) { - tests := []struct { - a, b map[string]bool - want map[string]bool - }{{ - a: nil, b: nil, - want: set(), - }, { - a: nil, b: set("b"), - want: set("b"), - }, { - a: set("a"), b: nil, - want: set("a"), - }, { - a: set("a"), b: set("b"), - want: set("a", "b"), - }, { - a: set("c"), b: set("c"), - want: set(), - }, { - a: set("a", "c"), b: set("b", "c"), - want: set("a", "b"), - }} - - for _, tt := range tests { - got := difference(tt.a, tt.b) - if !reflect.DeepEqual(tt.want, got) { - t.Errorf("difference(%v, %v) want: %v, got %v", tt.a, tt.b, tt.want, got) - } - } -} - -func TestContains(t *testing.T) { - tests := []struct { - a map[string]bool - s []string - want bool - }{{ - a: nil, s: nil, - want: false, - }, { - a: set("a"), s: nil, - want: false, - }, { - a: set("a"), s: []string{"a"}, - want: true, - }, { - a: set("a"), s: []string{"b"}, - want: false, - }, { - a: set("a", "b"), s: []string{"b"}, - want: true, - }, { - a: set("a"), s: []string{"a", "b"}, - want: false, - }, { - a: set("a", "b", "c"), s: []string{"a", "b"}, - want: true, - }, { - a: set("a", "b", "c"), s: []string{"x", "b"}, - want: false, - }, { - a: set("a", "b", "c"), s: []string{"b", "c", "d"}, - want: false, - }} - - for _, tt := range tests { - got := contains(tt.a, tt.s...) - if !reflect.DeepEqual(tt.want, got) { - t.Errorf("contains(%v, %v) want: %v, got %v", tt.a, tt.s, tt.want, got) - } - } -}
diff --git a/list.go b/list.go index 49e7e89..95b50a9 100644 --- a/list.go +++ b/list.go
@@ -31,7 +31,7 @@ `, Run: func(args []string) error { - m, err := vendor.ReadManifest(manifestFile()) + m, err := vendor.ReadManifest(manifestFile) if err != nil { return fmt.Errorf("could not load manifest: %v", err) }
diff --git a/main.go b/main.go index 129b95b..c83d694 100644 --- a/main.go +++ b/main.go
@@ -3,9 +3,11 @@ import ( "flag" "fmt" + "go/build" "log" "os" "path/filepath" + "strings" ) var fs = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) @@ -78,16 +80,22 @@ os.Exit(3) } -const manifestfile = "manifest" +var ( + vendorDir, manifestFile string + importPath string +) -func vendorDir() string { +func init() { wd, err := os.Getwd() if err != nil { log.Fatal(err) } - return filepath.Join(wd, "vendor") -} - -func manifestFile() string { - return filepath.Join(vendorDir(), manifestfile) + vendorDir = filepath.Join(wd, "vendor") + manifestFile = filepath.Join(vendorDir, "manifest") + srcTree := filepath.Join(build.Default.GOPATH, "src") + string(filepath.Separator) + if build.Default.GOPATH == "" || !strings.HasPrefix(wd, srcTree) { + log.Println("WARNING: for go vendoring to work your project needs to be somewhere under $GOPATH/src/") + } else { + importPath = filepath.ToSlash(strings.TrimPrefix(wd, srcTree)) + } }
diff --git a/restore.go b/restore.go index 3601291..dc0b1e3 100644 --- a/restore.go +++ b/restore.go
@@ -47,7 +47,7 @@ Run: func(args []string) error { switch len(args) { case 0: - return restore(manifestFile()) + return restore(manifestFile) default: return fmt.Errorf("restore takes no arguments") } @@ -69,7 +69,7 @@ go func() { defer wg.Done() for d := range depC { - if err := downloadDependency(d, &errors, vendorDir(), false); err != nil { + if err := downloadDependency(d, &errors, vendorDir, false); err != nil { log.Printf("%s: %v", d.Importpath, err) atomic.AddUint32(&errors, 1) }
diff --git a/update.go b/update.go index 8f6c6f1..9c73a49 100644 --- a/update.go +++ b/update.go
@@ -46,7 +46,7 @@ return fmt.Errorf("update: you cannot specify path and -all flag at once") } - m, err := vendor.ReadManifest(manifestFile()) + m, err := vendor.ReadManifest(manifestFile) if err != nil { return fmt.Errorf("could not load manifest: %v", err) } @@ -101,12 +101,12 @@ AllFiles: d.AllFiles, } - if err := fileutils.RemoveAll(filepath.Join(vendorDir(), filepath.FromSlash(d.Importpath))); err != nil { + if err := fileutils.RemoveAll(filepath.Join(vendorDir, filepath.FromSlash(d.Importpath))); err != nil { // TODO(dfc) need to apply vendor.cleanpath here to remove intermediate directories. return fmt.Errorf("dependency could not be deleted: %v", err) } - dst := filepath.Join(vendorDir(), filepath.FromSlash(dep.Importpath)) + dst := filepath.Join(vendorDir, filepath.FromSlash(dep.Importpath)) src := filepath.Join(wc.Dir(), dep.Path) if err := fileutils.Copypath(dst, src, !d.NoTests, d.AllFiles); err != nil { @@ -121,7 +121,7 @@ return err } - if err := vendor.WriteManifest(manifestFile(), m); err != nil { + if err := vendor.WriteManifest(manifestFile, m); err != nil { return err } }