- Check the external dependencies match those in the lock file.
- Read all the dependencies out of the lock file, don't calculate them myself.
- Prune by rebuilding the vendor dir with WriteDepTree
This commit is contained in:
Tom Wilkie 2017-03-13 10:43:21 +00:00
Родитель 7f85e04e34
Коммит 2c84fee0cb
3 изменённых файлов: 185 добавлений и 0 удалений

Просмотреть файл

@ -38,6 +38,7 @@ func main() {
&ensureCommand{},
&removeCommand{},
&hashinCommand{},
&pruneCommand{},
}
examples := [][2]string{

80
cmd/dep/prune.go Normal file
Просмотреть файл

@ -0,0 +1,80 @@
// Copyright 2016 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 main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"github.com/golang/dep"
"github.com/sdboyer/gps"
"github.com/sdboyer/gps/pkgtree"
"github.com/pkg/errors"
)
const pruneShortHelp = `Prune the vendor tree of unused packages`
const pruneLongHelp = `
Prune is used to remove unused packages from your vendor tree.
`
type pruneCommand struct {
}
func (cmd *pruneCommand) Name() string { return "prune" }
func (cmd *pruneCommand) Args() string { return "" }
func (cmd *pruneCommand) ShortHelp() string { return pruneShortHelp }
func (cmd *pruneCommand) LongHelp() string { return pruneLongHelp }
func (cmd *pruneCommand) Hidden() bool { return false }
func (cmd *pruneCommand) Register(fs *flag.FlagSet) {
}
func (cmd *pruneCommand) Run(ctx *dep.Ctx, args []string) error {
p, err := ctx.LoadProject("")
if err != nil {
return err
}
sm, err := ctx.SourceManager()
if err != nil {
return err
}
sm.UseDefaultSignalHandling()
defer sm.Release()
// While the network churns on ListVersions() requests, statically analyze
// code from the current project.
ptree, err := pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot))
if err != nil {
return errors.Wrap(err, "analysis of local packages failed: %v")
}
// Set up a solver in order to check the InputHash.
params := gps.SolveParameters{
RootDir: p.AbsRoot,
RootPackageTree: ptree,
Manifest: p.Manifest,
// Locks aren't a part of the input hash check, so we can omit it.
}
if *verbose {
params.Trace = true
params.TraceLogger = log.New(os.Stderr, "", 0)
}
s, err := gps.Prepare(params, sm)
if err != nil {
return errors.Wrap(err, "could not set up solver for input hashing")
}
if !bytes.Equal(s.HashInputs(), p.Lock.Memo) {
return fmt.Errorf("lock hash doesn't match")
}
return dep.PruneProject(p, sm)
}

Просмотреть файл

@ -594,3 +594,107 @@ func diffProjects(lp1 gps.LockedProject, lp2 gps.LockedProject) *LockedProjectDi
}
return &diff
}
func PruneProject(p *Project, sm gps.SourceManager) error {
td, err := ioutil.TempDir(os.TempDir(), "dep")
if err != nil {
return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor")
}
defer os.RemoveAll(td)
if err := gps.WriteDepTree(td, p.Lock, sm, true); err != nil {
return err
}
var toKeep []string
for _, project := range p.Lock.Projects() {
projectRoot := string(project.Ident().ProjectRoot)
for _, pkg := range project.Packages() {
toKeep = append(toKeep, filepath.Join(projectRoot, pkg))
}
}
toDelete, err := calculatePrune(td, toKeep)
if err != nil {
return err
}
if err := deleteDirs(toDelete); err != nil {
return err
}
vpath := filepath.Join(p.AbsRoot, "vendor")
vendorbak := vpath + ".orig"
var failerr error
if _, err := os.Stat(vpath); err == nil {
// Move out the old vendor dir. just do it into an adjacent dir, to
// try to mitigate the possibility of a pointless cross-filesystem
// move with a temp directory.
if _, err := os.Stat(vendorbak); err == nil {
// If the adjacent dir already exists, bite the bullet and move
// to a proper tempdir.
vendorbak = filepath.Join(td, "vendor.orig")
}
failerr = renameWithFallback(vpath, vendorbak)
if failerr != nil {
goto fail
}
}
// Move in the new one.
failerr = renameWithFallback(td, vpath)
if failerr != nil {
goto fail
}
os.RemoveAll(vendorbak)
return nil
fail:
renameWithFallback(vendorbak, vpath)
return failerr
}
func calculatePrune(vendorDir string, keep []string) ([]string, error) {
sort.Strings(keep)
toDelete := []string{}
err := filepath.Walk(vendorDir, func(path string, info os.FileInfo, err error) error {
if _, err := os.Lstat(path); err != nil {
return nil
}
if !info.IsDir() {
return nil
}
if path == vendorDir {
return nil
}
name := strings.TrimPrefix(path, vendorDir+"/")
i := sort.Search(len(keep), func(i int) bool {
return name <= keep[i]
})
if i >= len(keep) || !strings.HasPrefix(keep[i], name) {
toDelete = append(toDelete, path)
}
return nil
})
return toDelete, err
}
func deleteDirs(toDelete []string) error {
// sort by length so we delete sub dirs first
sort.Sort(byLen(toDelete))
for _, path := range toDelete {
if err := os.RemoveAll(path); err != nil {
return err
}
}
return nil
}
type byLen []string
func (a byLen) Len() int { return len(a) }
func (a byLen) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byLen) Less(i, j int) bool { return len(a[i]) > len(a[j]) }