diff --git a/README.md b/README.md index 31b9493..2237156 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,18 @@ Reload config. **All DONE!** -### Confirm +### Quick Usage + +Just use command `gobrew` from any dir. + +

+ + gobrew command + +

-```sh -gobrew help -``` -### Usage +### Full Usage Will install and set Go @@ -333,3 +338,4 @@ alias cd='builtin cd "$@" && ls go.mod 2> /dev/null && gobrew use mod' - v1.9.0 - v1.8.6 ~ v1.9.0, updates colors packages, fixes UT issues for Github status codes - v1.9.1 - Minor logging fixes - v1.9.2 - Minor log message updated +- v1.9.4 - `gobrew` interactive diff --git a/cmd/gobrew/main.go b/cmd/gobrew/main.go index a3ecb87..e92b34e 100644 --- a/cmd/gobrew/main.go +++ b/cmd/gobrew/main.go @@ -25,6 +25,8 @@ var allowedArgs = []string{ "install", "use", "uninstall", + "interactive", + "noninteractive", "prune", "version", "self-update", @@ -42,11 +44,11 @@ func init() { flag.Parse() args = flag.Args() if len(args) == 0 { - log.Print(usage()) - return + actionArg = "interactive" + } else { + actionArg = args[0] } - actionArg = args[0] if len(args) == 2 { versionArg = args[1] versionArgSlice := strings.Split(versionArg, ".") @@ -64,6 +66,10 @@ func init() { func main() { gb := gobrew.NewGoBrew() switch actionArg { + case "interactive", "info": + gb.Interactive(true) + case "noninteractive": + gb.Interactive(false) case "h", "help": log.Print(usage()) case "ls", "list": diff --git a/go.mod b/go.mod index 881b7c8..e486b3c 100644 --- a/go.mod +++ b/go.mod @@ -7,21 +7,23 @@ require ( github.com/c4milo/unpackit v1.0.0 github.com/gookit/color v1.5.4 github.com/schollz/progressbar/v3 v3.13.1 - gotest.tools v2.2.0+incompatible + github.com/stretchr/testify v1.8.4 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dsnet/compress v0.0.1 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/klauspost/compress v1.4.1 // indirect github.com/klauspost/cpuid v1.2.0 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.6.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 08485b1..598070f 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/hooklift/assert v0.1.0 h1:UZzFxx5dSb9aBtvMHTtnPuvFnBvcEhHTPb9+0+jpEjs= @@ -51,7 +49,7 @@ golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= diff --git a/gobrew.go b/gobrew.go index c6cf651..0a82220 100644 --- a/gobrew.go +++ b/gobrew.go @@ -84,6 +84,107 @@ func NewGoBrewDirectory(homeDir string) GoBrew { return gb } +func (gb *GoBrew) Interactive(ask bool) { + currentVersion := gb.CurrentVersion() + currentMajorVersion := ExtractMajorVersion(currentVersion) + + latestVersion := gb.getLatestVersion() + latestMajorVersion := ExtractMajorVersion(latestVersion) + + modVersion := "" + if gb.hasModFile() { + modVersion = gb.getModVersion() + } + + if modVersion == "" { + modVersion = "None" + } + + fmt.Println() + + if currentVersion == "" { + currentVersion = "None" + color.Warnln("GO Installed Version", ".......", currentVersion) + } else { + labels := []string{} + if modVersion != "None" && currentMajorVersion != modVersion { + labels = append(labels, "not same as go.mod") + } + if currentVersion != latestVersion { + labels = append(labels, "not latest") + } + label := "" + if len(labels) > 0 { + label = "(" + strings.Join(labels, ", ") + ")" + } + if label != "" { + label = " " + color.FgRed.Render(label) + } + color.Successln("GO Installed Version", ".......", currentVersion+label) + } + + if modVersion != "None" && latestMajorVersion != modVersion { + label := " " + color.FgYellow.Render("(not latest)") + color.Successln("GO go.mod Version", " .......", modVersion+label) + } else { + color.Successln("GO go.mod Version", " .......", modVersion) + } + + color.Successln("GO Latest Version", " .......", latestVersion) + fmt.Println() + + if currentVersion == "None" { + color.Warnln("GO is not installed.") + c := true + if ask { + c = AskForConfirmation("Do you want to use latest GO version (" + latestVersion + ")?") + } + if c { + gb.Install(latestVersion) + gb.Use(latestVersion) + } + return + } + + if currentMajorVersion != modVersion { + color.Warnf("GO Installed Version (%s) and go.mod Version (%s) are different.\n", currentMajorVersion, modVersion) + c := true + if ask { + c = AskForConfirmation("Do you want to use GO version same as go.mod version (" + modVersion + "@latest)?") + } + if c { + gb.Install(modVersion + "@latest") + gb.Use(modVersion + "@latest") + } + return + } + + if currentVersion != latestVersion { + color.Warnf("GO Installed Version (%s) and GO Latest Version (%s) are different.\n", currentVersion, latestVersion) + c := true + if ask { + c = AskForConfirmation("Do you want to update GO to latest version (" + latestVersion + ")?") + } + if c { + gb.Install(latestVersion) + gb.Use(latestVersion) + } + return + } +} + +func (gb *GoBrew) getLatestVersion() string { + getGolangVersions := gb.getGolangVersions() + // loop through reverse and ignore beta and rc versions to get latest version + for i := len(getGolangVersions) - 1; i >= 0; i-- { + r := regexp.MustCompile("beta.*|rc.*") + matches := r.FindAllString(getGolangVersions[i], -1) + if len(matches) == 0 { + return strings.ReplaceAll(getGolangVersions[i], "go", "") + } + } + return "" +} func (gb *GoBrew) getArch() string { return runtime.GOOS + "-" + runtime.GOARCH } @@ -247,7 +348,7 @@ func (gb *GoBrew) getGroupedVersion(versions []string, print bool) map[string][] gb.print("\t", print) } else { if print { - color.Infop(lookupKey) + color.Successp(lookupKey) } gb.print("\t", print) } @@ -465,6 +566,18 @@ func (gb *GoBrew) judgeVersion(version string) string { return version } +func (gb *GoBrew) hasModFile() bool { + modFilePath := filepath.Join("go.mod") + _, err := os.Stat(modFilePath) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + // read go.mod file and extract version // Do not use go to get the version as go list -m -f '{{.GoVersion}}' // Because go might not be installed @@ -514,7 +627,7 @@ func (gb *GoBrew) Version(currentVersion string) { // Upgrade of GoBrew func (gb *GoBrew) Upgrade(currentVersion string) { - if "v"+currentVersion == gb.getLatestVersion() { + if "v"+currentVersion == gb.getGobrewVersion() { color.Infoln("[INFO] your version is already newest") return } @@ -638,7 +751,7 @@ func (gb *GoBrew) changeSymblinkGo(version string) { utils.CheckError(os.Symlink(versionGoDir, gb.currentGoDir), "==> [Error]: symbolic link failed") } -func (gb *GoBrew) getLatestVersion() string { +func (gb *GoBrew) getGobrewVersion() string { url := "https://api.github.com/repos/kevincobain2000/gobrew/releases/latest" data := doRequest(url) if len(data) == 0 { diff --git a/gobrew_test.go b/gobrew_test.go index 2a3a57e..9039ace 100644 --- a/gobrew_test.go +++ b/gobrew_test.go @@ -4,9 +4,10 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" - "gotest.tools/assert" + "github.com/stretchr/testify/assert" ) func TestNewGobrewHomeDirUsesUserHomeDir(t *testing.T) { @@ -196,7 +197,7 @@ func TestDoNotUpgradeLatestVersion(t *testing.T) { } binaryFile := filepath.Join(binaryDir, baseName) - currentVersion := gb.getLatestVersion() + currentVersion := gb.getGobrewVersion() if currentVersion == "" { t.Skip("could not determine the current version") @@ -208,3 +209,43 @@ func TestDoNotUpgradeLatestVersion(t *testing.T) { t.Errorf("unexpected upgrade of latest version") } } + +func TestInteractive(t *testing.T) { + tempDir := filepath.Join(os.TempDir(), "gobrew-test-interactive") + err := os.MkdirAll(tempDir, os.ModePerm) + if err != nil { + t.Skip("could not create directory for gobrew update:", err) + return + } + + defer func() { + _ = os.RemoveAll(tempDir) + }() + + gb := NewGoBrewDirectory(tempDir) + currentVersion := gb.CurrentVersion() + latestVersion := gb.getLatestVersion() + // modVersion := gb.getModVersion() + assert.Equal(t, "", currentVersion) + assert.NotEqual(t, currentVersion, latestVersion) + + ask := false + gb.Interactive(ask) + + currentVersion = gb.CurrentVersion() + // remove string private from currentVersion (for macOS) due to /private/var symlink issue + currentVersion = strings.Replace(currentVersion, "private", "", -1) + assert.Equal(t, currentVersion, latestVersion) + + gb.Install("1.16.5") // we know, it is not latest + gb.Use("1.16.5") + currentVersion = gb.CurrentVersion() + currentVersion = strings.Replace(currentVersion, "private", "", -1) + assert.Equal(t, "1.16.5", currentVersion) + assert.NotEqual(t, currentVersion, latestVersion) + + gb.Interactive(ask) + currentVersion = gb.CurrentVersion() + currentVersion = strings.Replace(currentVersion, "private", "", -1) + assert.Equal(t, currentVersion, latestVersion) +} diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..4a6eb95 --- /dev/null +++ b/helpers.go @@ -0,0 +1,47 @@ +package gobrew + +import ( + "bufio" + "fmt" + "log" + "os" + "strings" + + "github.com/gookit/color" +) + +func ExtractMajorVersion(version string) string { + parts := strings.Split(version, ".") + if len(parts) < 2 { + return "" + } + // remove rc and beta + parts[1] = strings.Split(parts[1], "rc")[0] + parts[1] = strings.Split(parts[1], "beta")[0] + + // Take the first two parts and join them back with a period to create the new version. + majorVersion := strings.Join(parts[:2], ".") + return majorVersion +} + +func AskForConfirmation(s string) bool { + reader := bufio.NewReader(os.Stdin) + + for { + fmt.Printf("%s ", s) + color.Successf("[y/n]: ") + + response, err := reader.ReadString('\n') + if err != nil { + log.Fatal(err) + } + + response = strings.ToLower(strings.TrimSpace(response)) + + if response == "y" || response == "yes" { + return true + } else if response == "" || response == "n" || response == "no" { + return false + } + } +} diff --git a/helpers_test.go b/helpers_test.go new file mode 100644 index 0000000..85a3fc4 --- /dev/null +++ b/helpers_test.go @@ -0,0 +1,59 @@ +package gobrew + +import ( + "testing" +) + +func TestExtractMajorVersion(t *testing.T) { + type args struct { + version string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "extracts major version", + args: args{ + version: "1.16.5", + }, + want: "1.16", + }, + { + name: "extracts major version if already major version", + args: args{ + version: "1.16", + }, + want: "1.16", + }, + { + name: "extracts major version with extra parts", + args: args{ + version: "", + }, + want: "", + }, + { + name: "extracts major version rc", + args: args{ + version: "1.21rc1", + }, + want: "1.21", + }, + { + name: "extracts major version beta", + args: args{ + version: "1.21beta1", + }, + want: "1.21", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ExtractMajorVersion(tt.args.version); got != tt.want { + t.Errorf("ExtractMajorVersion() = %v, want %v", got, tt.want) + } + }) + } +}