Skip to content

Commit

Permalink
feat: support multiple flags together (#9)
Browse files Browse the repository at this point in the history
* feat: support multiple flags together

* fix: pr template
  • Loading branch information
VassilisPallas authored Oct 29, 2023
1 parent c446b52 commit 10ac1c3
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 87 deletions.
1 change: 0 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Fixes https://github.com/VassilisPallas/gvs/issues

Tests

- [ ] I've added integration tests
- [ ] I've added unit tests

Documentation
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Installing version...

### Install specific version

To install a specific version without using the dropdown, use the `--install-version=value`.
To install a specific version without using the dropdown, use the `--install-version=value` flag.

```sh
$ gvs --install-version=1.21.3
Expand Down Expand Up @@ -190,10 +190,24 @@ Installing version...

Every time you install a new version, gvs keeps the previous installed versions, so you can easily change between them. If you want to delete all the unused versions and keep only the current one, use the `--delete-unused` flag.

In the below example, the versions `1.20` and `1.19` are previously installed, and since they are not used (neither of them is the current version you use), they will be deleted.
In the below example, the versions `1.20` and `1.19` are previously installed, and since they are not used (neither of them is the current version you use), they will be deleted after installing the new version 1.21.2.

```sh
$ gvs --delete-unused
Use the arrow keys to navigate: ↓ ↑ → ←
? Select go version:
1.21.3
▸ 1.21.2
1.21.1
1.21.0
1.20.10

✔ 1.21.2
Downloading...
Compare Checksums...
Unzipping...
Installing version...
1.21.2 version is installed!
Deleting go1.20.
go1.20 is deleted.
Deleting go1.19.
Expand Down
66 changes: 66 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cli

import (
"errors"
"fmt"
"runtime"

"github.com/VassilisPallas/gvs/logger"
"github.com/VassilisPallas/gvs/version"
)

type CLI struct {
versions []*version.ExtendedVersion
versioner version.Versioner
log logger.Logger
}

func (cli CLI) Install(selectedVersion *version.ExtendedVersion) error {
cli.log.Info("selected %s version\n", selectedVersion.Version)
return cli.versioner.Install(selectedVersion, runtime.GOOS, runtime.GOARCH)
}

func (cli CLI) InstallVersion(goVersion string) error {
semver := &version.Semver{}
err := version.ParseSemver(goVersion, semver)
if err != nil {
return err
}

selectedVersion := cli.versioner.FindVersionBasedOnSemverName(cli.versions, semver)
if selectedVersion != nil {
return fmt.Errorf("%s is not a valid version", semver.GetVersion())
}

return cli.Install(selectedVersion)
}

func (cli CLI) InstallLatestVersion() error {
selectedIndex := cli.versioner.GetLatestVersion(cli.versions)
if selectedIndex == -1 {
return errors.New("latest version not found")
}

selectedVersion := cli.versions[selectedIndex]

return cli.Install(selectedVersion)
}

func (cli CLI) DeleteUnusedVersions() error {
deleted_count, err := cli.versioner.DeleteUnusedVersions(cli.versions)
if err != nil {
return err
}

if deleted_count > 0 {
cli.log.PrintMessage("All the unused version are deleted!")
} else {
cli.log.PrintMessage("Nothing to delete")
}

return nil
}

func New(versions []*version.ExtendedVersion, versioner version.Versioner, log logger.Logger) CLI {
return CLI{versions: versions, versioner: versioner, log: log}
}
103 changes: 26 additions & 77 deletions cmd/gvs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package main
import (
"net/http"
"os"
"runtime"
"time"

"github.com/VassilisPallas/gvs/api_client"
"github.com/VassilisPallas/gvs/cli"
"github.com/VassilisPallas/gvs/clock"
cf "github.com/VassilisPallas/gvs/config"
"github.com/VassilisPallas/gvs/files"
Expand All @@ -16,7 +16,6 @@ import (
"github.com/VassilisPallas/gvs/pkg/unzip"
"github.com/VassilisPallas/gvs/version"
"github.com/manifoldco/promptui"
"golang.org/x/mod/modfile"
)

var (
Expand All @@ -30,12 +29,12 @@ var (

func parseFlags() {
set := flags.FlagSet{}
set.FlagBool(&showAllVersions, "show-all", false, "Show both stable and unstable versions.")
set.FlagBool(&installLatest, "install-latest", false, "Install latest stable version.")
set.FlagBool(&deleteUnused, "delete-unused", false, "Delete all unused versions that were installed before.")
set.FlagBool(&refreshVersions, "refresh-versions", false, "Fetch again go versions in case the cached ones are stale.")
set.FlagStr(&specificVersion, "install-version", "", "Pass the version you want to install instead of selecting from the dropdown. If you do not specify the minor or the patch version, the latest one will be selected.")
set.FlagBool(&fromModFile, "from-mod", false, "Install the version that will be found on the go.mod file. The go.mod file should be on the same path you run gvs. If the version in the go.mod file do not specify the minor or the patch version, the latest one will be selected.")
set.FlagBool(&showAllVersions, "show-all", 'a', false, "Show both stable and unstable versions.")
set.FlagBool(&installLatest, "install-latest", 'l', false, "Install latest stable version.")
set.FlagBool(&deleteUnused, "delete-unused", 'd', false, "Delete all unused versions that were installed before.")
set.FlagBool(&refreshVersions, "refresh-versions", 'r', false, "Fetch again go versions in case the cached ones are stale.")
set.FlagStr(&specificVersion, "install-version", 'v', "", "Pass the version you want to install instead of selecting from the dropdown. If you do not specify the minor or the patch version, the latest one will be selected.")
set.FlagBool(&fromModFile, "from-mod", 'm', false, "Install the version that will be found on the go.mod file. The go.mod file should be on the same path you run gvs. If the version in the go.mod file do not specify the minor or the patch version, the latest one will be selected.")

set.Parse()
}
Expand Down Expand Up @@ -79,40 +78,20 @@ func main() {
return
}

cli := cli.New(versions, versioner, log)

switch {
case fromModFile:
log.Info("install version from go.mod file option selected")

buf, err := fs.ReadFile("./go.mod")
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}

f, err := modfile.Parse("go.mod", buf, nil)
version, err := fileHelpers.ReadVersionFromMod()
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}

semver := &version.Semver{}
err = version.ParseSemver(f.Go.Version, semver)
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}

selectedVersion := versioner.FindVersionBasedOnSemverName(versions, semver)
if selectedVersion == nil {
log.PrintError("%s is not a valid version.", semver.GetVersion())
os.Exit(1)
return
}

err = versioner.Install(selectedVersion, runtime.GOOS, runtime.GOARCH)
err = cli.InstallVersion(version)
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
Expand All @@ -121,56 +100,16 @@ func main() {
case specificVersion != "":
log.Info("install specific version option selected")

semver := &version.Semver{}
err := version.ParseSemver(specificVersion, semver)
err := cli.InstallVersion(specificVersion)
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}

selectedVersion := versioner.FindVersionBasedOnSemverName(versions, semver)
if selectedVersion == nil {
log.PrintError("%s is not a valid version.", semver.GetVersion())
os.Exit(1)
return
}

err = versioner.Install(selectedVersion, runtime.GOOS, runtime.GOARCH)
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}
case deleteUnused:
log.Info("deleteUnused option selected")

deleted_count, err := versioner.DeleteUnusedVersions(versions)
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}

if deleted_count > 0 {
log.PrintMessage("All the unused version are deleted!")
} else {
log.PrintMessage("Nothing to delete")
}
case installLatest:
log.Info("install latest option selected")

selectedIndex := versioner.GetLatestVersion(versions)
if selectedIndex == -1 {
log.PrintError("latest version not found")
os.Exit(1)
return
}

selectedVersion := versions[selectedIndex]

log.Info("selected %s version", selectedVersion.Version)
err := versioner.Install(selectedVersion, runtime.GOOS, runtime.GOARCH)
err := cli.InstallLatestVersion()
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
Expand Down Expand Up @@ -200,10 +139,20 @@ func main() {
return
}

selectedVersion := promptVersions[selectedIndex]
err = cli.Install(promptVersions[selectedIndex])
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
return
}
}

// execute this command at the end, after any other selected flag
// to delete the unused version if needed.
if deleteUnused {
log.Info("deleteUnused option selected")

log.Info("selected %s version\n", selectedVersion.Version)
err := versioner.Install(selectedVersion, runtime.GOOS, runtime.GOARCH)
err := cli.DeleteUnusedVersions()
if err != nil {
log.PrintError(err.Error())
os.Exit(1)
Expand Down
17 changes: 17 additions & 0 deletions files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/VassilisPallas/gvs/clock"
"github.com/VassilisPallas/gvs/logger"
"github.com/VassilisPallas/gvs/pkg/unzip"
"golang.org/x/mod/modfile"
)

// FileHelpers is the interface that wraps the basic methods for working with files.
Expand Down Expand Up @@ -79,6 +80,8 @@ type FileHelpers interface {
// GetLatestCreatedGoVersionDirectory must return the name of the latest modified directory and
// a non-null error if the operation is successful.
GetLatestCreatedGoVersionDirectory() (string, error)

ReadVersionFromMod() (string, error)
}

// Helper is the struct that implements the FileHelpers interface
Expand Down Expand Up @@ -371,6 +374,20 @@ func (h Helper) GetLatestCreatedGoVersionDirectory() (string, error) {
return dirName, nil
}

func (h Helper) ReadVersionFromMod() (string, error) {
buf, err := h.fileSystem.ReadFile("./go.mod")
if err != nil {
return "", err
}

f, err := modfile.Parse("go.mod", buf, nil)
if err != nil {
return "", err
}

return f.Go.Version, nil
}

// New returns a *Helper instance that implements the FileHelpers interface.
// Each call to New returns a distinct *Helper instance even if the parameters are identical.
func New(fs FS, clock clock.Clock, unzipper unzip.Unzipper, log *logger.Log) *Helper {
Expand Down
74 changes: 74 additions & 0 deletions files/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,77 @@ func TestGetLatestCreatedGoVersionDirectory(t *testing.T) {
})
}
}

func TestReadVersionFromMod(t *testing.T) {
var file_bytes = []byte(`
module module_name
go 1.20
require (
foo/bar v1.2.3
)
`)

testCases := []struct {
testTitle string
modFileBytes []byte
modeFileError error
expectedError error
expectedVersion string
}{
{
testTitle: "should return an error when reading the go.mod file returns an error",
modFileBytes: nil,
modeFileError: errors.New("some error while reading th go.mod file"),
expectedError: errors.New("some error while reading th go.mod file"),
expectedVersion: "",
},
{
testTitle: "should return an error when parsing the go.mod file returns an error",
modFileBytes: []byte(`wrong`),
modeFileError: nil,
expectedError: errors.New("go.mod:1: unknown directive: wrong"),
expectedVersion: "",
},
{
testTitle: "should return the version from go.mod",
modFileBytes: file_bytes,
modeFileError: nil,
expectedError: nil,
expectedVersion: "1.20",
},
}

for _, tc := range testCases {
t.Run(tc.testTitle, func(t *testing.T) {
fs := testutils.FakeFileSystem{
HomeDir: "/tmp",
ReadFileBytes: tc.modFileBytes,
ReadFileError: tc.modeFileError,
}

clock := testutils.FakeClock{
UseRealIsBefore: true,
UseRealIsAfter: true,
}
fileHelper := createFileHelper(&testutils.FakeStdout{}, nil, fs, testutils.FakeUnzipper{}, clock)
goVersion, err := fileHelper.ReadVersionFromMod()

if tc.expectedError == nil && err != nil {
t.Errorf("error should be nil, instead got %q", err.Error())
return
}

if tc.expectedError != nil && err.Error() != tc.expectedError.Error() {
t.Errorf("error should be %q, instead got %q", tc.expectedError.Error(), err.Error())
return
}

if goVersion != tc.expectedVersion {
t.Errorf("the version should be %q, instead got %q", tc.expectedVersion, goVersion)
return
}
})
}
}
Loading

0 comments on commit 10ac1c3

Please sign in to comment.