зеркало из https://github.com/golang/dep.git
Add dep prune subcommand
- 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:
Родитель
7f85e04e34
Коммит
2c84fee0cb
|
@ -38,6 +38,7 @@ func main() {
|
|||
&ensureCommand{},
|
||||
&removeCommand{},
|
||||
&hashinCommand{},
|
||||
&pruneCommand{},
|
||||
}
|
||||
|
||||
examples := [][2]string{
|
||||
|
|
|
@ -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)
|
||||
}
|
104
txn_writer.go
104
txn_writer.go
|
@ -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]) }
|
||||
|
|
Загрузка…
Ссылка в новой задаче