From ae7867ff2091a5360bc7eddaff0d1d8a97bdb11e Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sat, 19 May 2018 20:04:28 +0700 Subject: Add remove operation --- README.md | 6 ++++ beku.go | 1 + cmd/beku/command.go | 12 ++++++- cmd/beku/main.go | 18 ++++++++++- cmd/beku/operation.go | 1 + env.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++- package.go | 69 ++++++++++++++++++++++++++++++++++++++++- 7 files changed, 189 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8bf039d..0c2d81e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ package database into "$GOPATH/var/beku/gopath.deps". Query the package database. +## Remove Operation + + -R, --remove [pkg] + +Remove package from GOPATH. + ## Sync Operation diff --git a/beku.go b/beku.go index f63e393..9b838cf 100644 --- a/beku.go +++ b/beku.go @@ -28,6 +28,7 @@ const ( msgUpdateProceed = "Proceed with update?" msgUpdateView = "View commit logs?" + msgContinue = "Continue?" prefixTag = 'v' diff --git a/cmd/beku/command.go b/cmd/beku/command.go index 160b7dc..8558955 100644 --- a/cmd/beku/command.go +++ b/cmd/beku/command.go @@ -14,6 +14,7 @@ const ( flagHelpUsage = "Show the short usage." flagQueryUsage = "Query the package database." + flagRemoveUsage = "Remove package from GOPATH." flagSyncUsage = "Synchronize `package`." flagSyncIntoUsage = "Package download `directory`." ) @@ -24,6 +25,7 @@ type command struct { help bool query bool queryPkg []string + rmPkg string syncPkg string syncInto string } @@ -35,6 +37,8 @@ operations: ` + flagHelpUsage + ` beku {-Q|--query} [pkg ...] ` + flagQueryUsage + ` + beku {-R|--remove} [pkg ...] + ` + flagRemoveUsage + ` beku {-S|--sync} [--into ] ` + flagSyncUsage + ` ` @@ -53,9 +57,11 @@ func (cmd *command) setFlags() { flag.BoolVar(&cmd.query, "Q", false, flagQueryUsage) flag.BoolVar(&cmd.query, "query", false, flagQueryUsage) + flag.StringVar(&cmd.rmPkg, "R", emptyString, flagRemoveUsage) + flag.StringVar(&cmd.rmPkg, "remove", emptyString, flagRemoveUsage) + flag.StringVar(&cmd.syncPkg, "S", emptyString, flagSyncUsage) flag.StringVar(&cmd.syncPkg, "sync", emptyString, flagSyncUsage) - flag.StringVar(&cmd.syncInto, "into", emptyString, flagSyncIntoUsage) flag.Parse() @@ -88,6 +94,10 @@ func (cmd *command) checkFlags() { } } + if len(cmd.rmPkg) > 0 { + cmd.op |= opRemove + } + // Invalid command parameters if cmd.op == opNone || cmd.op == opSyncInto { cmd.usage() diff --git a/cmd/beku/main.go b/cmd/beku/main.go index bf6666c..d3c040b 100644 --- a/cmd/beku/main.go +++ b/cmd/beku/main.go @@ -20,6 +20,12 @@ // // Query the package database. // +// ## Remove Operation +// +// -R, --remove [pkg] +// +// Remove package from GOPATH. +// // // ## Sync Operation // @@ -80,7 +86,9 @@ package main import ( + "fmt" "log" + "os" ) var ( @@ -96,15 +104,23 @@ func main() { switch cmd.op { case opQuery: cmd.env.Query(cmd.queryPkg) + case opRemove: + err = cmd.env.Remove(cmd.rmPkg, false) case opSync: err = cmd.env.Sync(cmd.syncPkg, "") case opSync | opSyncInto: err = cmd.env.Sync(cmd.syncPkg, cmd.syncInto) + default: + fmt.Fprintln(os.Stderr, "Invalid operations or options") + os.Exit(1) } if err != nil { log.Fatal(err) } - cmd.env.Save("") + err = cmd.env.Save("") + if err != nil { + log.Fatal(err) + } } diff --git a/cmd/beku/operation.go b/cmd/beku/operation.go index 213a8ec..b10a446 100644 --- a/cmd/beku/operation.go +++ b/cmd/beku/operation.go @@ -6,6 +6,7 @@ const opNone operation = 0 const ( opQuery operation = 1 << iota + opRemove opSync opSyncInto ) diff --git a/env.go b/env.go index f1fa38e..d6ea134 100644 --- a/env.go +++ b/env.go @@ -37,6 +37,7 @@ type Env struct { pkgsStd []string db *ini.Ini dbFile string + dirty bool } // NewEnvironment will gather all information in user system. @@ -314,9 +315,92 @@ func (env *Env) Query(pkgs []string) { } // -// Save the dependencies to `file`. +// Remove package from GOPATH. If recursive is true, it will also remove their +// dependencies, as long as they are not required by other package. +// +func (env *Env) Remove(rmPkg string, recursive bool) (err error) { + pkg := env.GetPackage(rmPkg, "") + if pkg == nil { + fmt.Println("Package", rmPkg, "not installed") + return + } + + if len(pkg.RequiredBy) > 0 { + fmt.Fprintln(os.Stderr, `Can't remove package. +This package is required by, +`, + pkg.RequiredBy) + return + } + + fmt.Println("The following package will be removed,\n", pkg) + + ok := confirm(os.Stdin, msgContinue, false) + if !ok { + return + } + + if !recursive { + err = pkg.Remove() + if err != nil { + err = fmt.Errorf("Remove: %s", err) + return + } + + env.removePackage(pkg) + } + + pkgImportPath := filepath.Join(env.pkgDir, pkg.ImportPath) + + if Debug >= DebugL1 { + fmt.Println(">>> Remove $GOPATH/pkg:", pkgImportPath) + } + + err = os.RemoveAll(pkgImportPath) + if err != nil { + err = fmt.Errorf("Remove: %s", err) + } + + return +} + +// +// removePackage from list of packages, also remove in other packages +// "RequiredBy" if exist. +// +func (env *Env) removePackage(pkg *Package) { + idx := -1 + for x := 0; x < len(env.pkgs); x++ { + if env.pkgs[x].ImportPath == pkg.ImportPath { + idx = x + continue + } + + ok := env.pkgs[x].RemoveRequiredBy(pkg.ImportPath) + if ok { + env.dirty = true + } + } + + if idx >= 0 { + lenpkgs := len(env.pkgs) + + copy(env.pkgs[idx:], env.pkgs[idx+1:]) + env.pkgs[lenpkgs-1] = nil + env.pkgs = env.pkgs[:lenpkgs-1] + + env.dirty = true + } +} + +// +// Save the dependencies to `file` only if it's dirty flag is true. // func (env *Env) Save(file string) (err error) { + if !env.dirty { + return + } + if len(file) == 0 { if len(env.dbFile) == 0 { file = env.defDBFile diff --git a/package.go b/package.go index e13ddf7..1537b85 100644 --- a/package.go +++ b/package.go @@ -81,6 +81,28 @@ func (pkg *Package) Fetch() (err error) { return } +// +// GoClean will remove the package binaries and archives. +// +func (pkg *Package) GoClean() (err error) { + //nolint:gas + cmd := exec.Command("go", "clean", "-i", "-cache", "-testcache", "./...") + if Debug >= DebugL1 { + fmt.Println(">>>", cmd.Args) + } + cmd.Dir = pkg.FullPath + cmd.Stdout = defStdout + cmd.Stderr = defStderr + + err = cmd.Run() + if err != nil { + err = fmt.Errorf("GoClean: %s", err) + return + } + + return +} + // // Install a package. Clone package to GOPATH/src, set to the latest tag if // exist or to the latest commit, and scan dependencies. @@ -123,6 +145,51 @@ func (pkg *Package) IsEqual(other *Package) bool { return true } +// +// Remove package installed binaries, archives, and source from GOPATH. +// +func (pkg *Package) Remove() (err error) { + err = pkg.GoClean() + if err != nil { + err = fmt.Errorf("Remove: %s", err) + return + } + + if Debug >= DebugL1 { + fmt.Println(">>> Remove source:", pkg.FullPath) + } + + err = os.RemoveAll(pkg.FullPath) + if err != nil { + err = fmt.Errorf("Remove: %s", err) + return + } + + return +} + +// +// RemoveRequiredBy will remove package import path from current +// package list of required-by. +// If import-path found as required-by, it will return true, otherwise it will +// return false. +// +func (pkg *Package) RemoveRequiredBy(importPath string) (ok bool) { + var requiredBy []string + + for x := 0; x < len(pkg.RequiredBy); x++ { + if pkg.RequiredBy[x] == importPath { + ok = true + continue + } + requiredBy = append(requiredBy, pkg.RequiredBy[x]) + } + if ok { + pkg.RequiredBy = requiredBy + } + return +} + // // Scan will set the package version, `isTag` status, and remote URL using // metadata in package repository. @@ -172,7 +239,7 @@ func (pkg *Package) ScanDeps(env *Env) (err error) { func (pkg *Package) GetRecursiveImports() ( imports []string, err error, ) { - //nolint: gas + //nolint:gas cmd := exec.Command("go", "list", "-e", "-f", `{{ join .Deps "\n"}}`, "./...") fmt.Println(">>>", cmd.Args) -- cgit v1.3