aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2018-05-27 06:17:06 +0700
committerShulhan <ms@kilabit.info>2018-05-27 06:17:06 +0700
commitc8635866b09a92ca42db2be2caf42bb6d4aab32c (patch)
treefe41ed98fc99d496c4b2654225aa132113964949
parent7c4a8ec9afba55a7dbe7fbe56cb9e7d365ca892f (diff)
downloadbeku-c8635866b09a92ca42db2be2caf42bb6d4aab32c.tar.xz
Implement freeze operation
Freeze operation 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.
-rw-r--r--README.md10
-rw-r--r--cmd/beku/command.go17
-rw-r--r--cmd/beku/command_test.go3
-rw-r--r--cmd/beku/main.go12
-rw-r--r--cmd/beku/operation.go1
-rw-r--r--env.go168
6 files changed, 204 insertions, 7 deletions
diff --git a/README.md b/README.md
index 5d2234b..94ccb97 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/env.go b/env.go
index a2ce351..cf23771 100644
--- a/env.go
+++ b/env.go
@@ -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)