diff options
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | cmd/beku/command.go | 17 | ||||
| -rw-r--r-- | cmd/beku/command_test.go | 3 | ||||
| -rw-r--r-- | cmd/beku/main.go | 12 | ||||
| -rw-r--r-- | cmd/beku/operation.go | 1 | ||||
| -rw-r--r-- | env.go | 168 |
6 files changed, 204 insertions, 7 deletions
@@ -22,6 +22,16 @@ directory. If no file found, it will try to open package database, beku will scan entire "$GOPATH/src" and write the package database into "$GOPATH/var/beku/beku.db". +## Freeze Operation + + -B, --freeze + +Operate on the package database and GOPATH. This operation will ensure that +all packages listed on database file is installed with their specific +version on GOPATH. Also, all packages that are not registered will be +removed from GOPATH "src" and "pkg" directories. + + ## Query Operation -Q, --query [pkg ...] diff --git a/cmd/beku/command.go b/cmd/beku/command.go index 11392b3..d64c688 100644 --- a/cmd/beku/command.go +++ b/cmd/beku/command.go @@ -18,6 +18,7 @@ var ( const ( flagOperationHelp = "Show the short usage." + flagOperationFreeze = "Install all packages on database." flagOperationQuery = "Query the package database." flagOperationRemove = "Remove package." flagOperationSync = "Synchronize package. If no package is given, it will do rescan." @@ -41,6 +42,9 @@ operations: beku {-h|--help} ` + flagOperationHelp + ` + beku {-B|--freeze} + ` + flagOperationFreeze + ` + beku {-Q|--query} [pkg ...] ` + flagOperationQuery + ` @@ -116,6 +120,11 @@ func (cmd *command) parseShortFlags(arg string) (operation, error) { if len(arg) > 1 { return opNone, errInvalidOptions } + case 'B': + op = opFreeze + if len(arg) > 1 { + return opNone, errInvalidOptions + } case 'Q': op = opQuery if len(arg) > 1 { @@ -149,6 +158,8 @@ func (cmd *command) parseLongFlags(arg string) (op operation, err error) { switch arg { case "help": op = opHelp + case "freeze": + op = opFreeze case "into": op = opSyncInto case "query": @@ -224,7 +235,7 @@ func (cmd *command) parseFlags(args []string) (err error) { return } } - if cmd.op == opRecursive || cmd.op == opSyncInto { + if cmd.op == opRecursive || cmd.op == opSyncInto || cmd.op == opUpdate { return errInvalidOptions } if cmd.op&opSyncInto == opSyncInto { @@ -234,8 +245,8 @@ func (cmd *command) parseFlags(args []string) (err error) { } // (1) - op = cmd.op & (opQuery | opRemove | opSync) - if op == opQuery|opRemove || op == opQuery|opSync || op == opRemove|opSync { + op = cmd.op & (opFreeze | opQuery | opRemove | opSync) + if op != opFreeze && op != opQuery && op != opRemove && op != opSync { return errMultiOperations } diff --git a/cmd/beku/command_test.go b/cmd/beku/command_test.go index b64d3f4..8478c30 100644 --- a/cmd/beku/command_test.go +++ b/cmd/beku/command_test.go @@ -30,6 +30,9 @@ func TestParseFlags(t *testing.T) { args: []string{"--into"}, expErr: errInvalidOptions.Error(), }, { + args: []string{"--update"}, + expErr: errInvalidOptions.Error(), + }, { args: []string{"-s"}, expErr: errInvalidOptions.Error(), }, { diff --git a/cmd/beku/main.go b/cmd/beku/main.go index 6c15c38..2d0190c 100644 --- a/cmd/beku/main.go +++ b/cmd/beku/main.go @@ -14,6 +14,16 @@ // package database, beku will scan entire "$GOPATH/src" and write the // package database into "$GOPATH/var/beku/beku.db". // +// ## Freeze Operation +// +// -B, --freeze +// +// Operate on the package database and GOPATH. This operation will ensure that +// all packages listed on database file is installed with their specific +// version on GOPATH. Also, all packages that are not registered will be +// removed from GOPATH "src" and "pkg" directories. +// +// // ## Query Operation // // -Q, --query [pkg ...] @@ -143,6 +153,8 @@ func main() { case opHelp: cmd.usage() os.Exit(1) + case opFreeze: + err = cmd.env.Freeze() case opQuery: cmd.env.Query(cmd.pkgs) case opRemove: diff --git a/cmd/beku/operation.go b/cmd/beku/operation.go index 1b51ed7..5925166 100644 --- a/cmd/beku/operation.go +++ b/cmd/beku/operation.go @@ -6,6 +6,7 @@ const opNone operation = 0 const ( opHelp operation = 1 << iota + opFreeze opQuery opRecursive opRemove @@ -33,6 +33,7 @@ type Env struct { pkgs []*Package pkgsMissing []string pkgsStd []string + pkgsUnused []*Package db *ini.Ini dbDefFile string dbFile string @@ -74,6 +75,109 @@ func NewEnvironment() (env *Env, err error) { return } +func (env *Env) cleanUnused() { + for _, pkg := range env.pkgsUnused { + fmt.Println(">>> Removing source at", pkg.FullPath) + _ = pkg.Remove() + + pkgPath := filepath.Join(env.dirPkg, pkg.ImportPath) + + fmt.Println(">>> Removing installed binaries at", pkgPath) + _ = os.RemoveAll(pkgPath) + _ = RmdirEmptyAll(pkgPath) + } +} + +// +// Freeze all packages in GOPATH. Install all registered packages in database +// and remove non-registered from GOPATH "src" and "pkg" directories. +// +func (env *Env) Freeze() (err error) { + var localPkg *Package + + for _, pkg := range env.pkgs { + fmt.Printf(">>> Freezing %s@%s\n", pkg.ImportPath, pkg.Version) + + localPkg, err = env.GetPackage(pkg.ImportPath) + if err != nil { + return + } + if localPkg == nil { + err = pkg.Install() + if err != nil { + return + } + continue + } + + err = localPkg.Update(pkg) + if err != nil { + return + } + } + + env.pkgsUnused = nil + + err = env.GetUnused(env.dirSrc) + if err != nil { + err = fmt.Errorf("Freeze: %s", err.Error()) + return + } + + if len(env.pkgsUnused) == 0 { + fmt.Println(">>> No unused packages found.") + return + } + + fmt.Printf("\n>>> The following packages will be cleaned,\n\n") + for _, pkg := range env.pkgsUnused { + fmt.Printf(" * %s\n", pkg.ImportPath) + } + + fmt.Println() + + ok := confirm(os.Stdin, msgContinue, false) + if !ok { + return + } + + env.cleanUnused() + + fmt.Println(">>> Freeze completed.") + + return +} + +// +// GetPackage will return installed package from system. +// +func (env *Env) GetPackage(importPath string) (pkg *Package, err error) { + fullPath := filepath.Join(env.dirSrc, importPath) + dirGit := filepath.Join(fullPath, gitDir) + + _, err = os.Stat(fullPath) + if err != nil { + err = nil + return + } + + _, err = os.Stat(dirGit) + if err != nil { + if IsDirEmpty(fullPath) { + err = nil + } else { + err = fmt.Errorf(errDirNotEmpty, fullPath) + } + return + } + + pkg = NewPackage(importPath, importPath, VCSModeGit) + + err = pkg.Scan() + + return +} + // // GetPackageFromDB will return installed package registered on database. // If no package found, it will return nil. @@ -92,6 +196,62 @@ func (env *Env) GetPackageFromDB(importPath, remoteURL string) *Package { } // +// GetUnused will get all non-registered packages from GOPATH "src". +// +func (env *Env) GetUnused(srcPath string) (err error) { + fis, err := ioutil.ReadDir(srcPath) + if err != nil { + err = fmt.Errorf("CleanPackages: %s", err) + return + } + + var nextScan []string + + for _, fi := range fis { + // (0) + if !fi.IsDir() { + continue + } + + dirName := fi.Name() + fullPath := filepath.Join(srcPath, dirName) + dirGit := filepath.Join(fullPath, gitDir) + + // (1) + if IsIgnoredDir(dirName) { + continue + } + + // (2) + _, err = os.Stat(dirGit) + if err != nil { + nextScan = append(nextScan, fullPath) + err = nil + continue + } + + importPath := strings.TrimPrefix(fullPath, env.dirSrc+"/") + + pkg := env.GetPackageFromDB(importPath, "") + if pkg != nil { + continue + } + + pkg = NewPackage(importPath, importPath, VCSModeGit) + env.pkgsUnused = append(env.pkgsUnused, pkg) + } + + for x := 0; x < len(nextScan); x++ { + err = env.GetUnused(nextScan[x]) + if err != nil { + return + } + } + + return +} + +// // Scan will gather all package information in user system to start `beku`-ing. // func (env *Env) Scan() (err error) { @@ -154,12 +314,12 @@ func (env *Env) scanStdPackages(srcPath string) error { // (1) skip ignored directory // (2) skip directory without `.git` // -func (env *Env) scanPackages(rootPath string) (err error) { +func (env *Env) scanPackages(srcPath string) (err error) { if Debug >= DebugL2 { - fmt.Println(">>> Scanning", rootPath) + fmt.Println(">>> Scanning", srcPath) } - fis, err := ioutil.ReadDir(rootPath) + fis, err := ioutil.ReadDir(srcPath) if err != nil { err = fmt.Errorf("scanPackages: %s", err) return @@ -174,7 +334,7 @@ func (env *Env) scanPackages(rootPath string) (err error) { } dirName := fi.Name() - fullPath := filepath.Join(rootPath, dirName) + fullPath := filepath.Join(srcPath, dirName) dirGit := filepath.Join(fullPath, gitDir) // (1) |
