diff --git a/lib/install.go b/lib/install.go index b674bd4..3f4658a 100644 --- a/lib/install.go +++ b/lib/install.go @@ -8,6 +8,7 @@ import ( "regexp" "runtime" + "github.com/warrensbox/terraform-switcher/lib" "github.com/warrensbox/tgswitch/modal" ) @@ -47,17 +48,34 @@ func init() { for path := next(); len(path) > 0; path = next() { installedBinPath = path } + + /* remove current symlink if exist*/ + symlinkExist := CheckSymlink(installedBinPath) + + if symlinkExist { + RemoveSymlink(installedBinPath) + } /* Create local installation directory if it does not exist */ CreateDirIfNotExist(installLocation) } //Install : Install the provided version in the argument -func Install(url string, appversion string, assests []modal.Repo, userBinPath * string) string { +func Install(url string, appversion string, assests []modal.Repo, userBinPath *string) string { + /* If user provided bin path use user one instead of default */ if userBinPath != nil { installedBinPath = *userBinPath } + pathDir := lib.Path(installedBinPath) //get path directory from binary path + binDirExist := lib.CheckDirExist(pathDir) //check bin path exist + + if !binDirExist { + fmt.Printf("Binary path does not exist: %s\n", pathDir) + fmt.Printf("Please create binary path: %s for terragrunt installation\n", pathDir) + os.Exit(1) + } + /* check if selected version already downloaded */ fileExist := CheckFileExist(installLocation + installVersion + appversion) @@ -195,3 +213,20 @@ func GetRecentVersions() ([]string, error) { func CreateRecentFile(requestedVersion string) { WriteLines([]string{requestedVersion}, installLocation+recentFile) } + +// ValidVersionFormat : returns valid version format +/* For example: 0.1.2 = valid +// For example: 0.1.2-beta1 = valid +// For example: 0.1.2-alpha = valid +// For example: a.1.2 = invalid +// For example: 0.1. 2 = invalid +*/ +func ValidVersionFormat(version string) bool { + + // Getting versions from body; should return match /X.X.X-@/ where X is a number,@ is a word character between a-z or A-Z + // Follow https://semver.org/spec/v1.0.0-beta.html + // Check regular expression at https://rubular.com/r/ju3PxbaSBALpJB + semverRegex := regexp.MustCompile(`^(\d+\.\d+\.\d+)(-[a-zA-z]+\d*)?$`) + + return semverRegex.MatchString(version) +} diff --git a/lib/list_versions_test.go b/lib/list_versions_test.go index 9647642..59646a8 100644 --- a/lib/list_versions_test.go +++ b/lib/list_versions_test.go @@ -24,3 +24,100 @@ func TestRemoveDuplicateVersions(t *testing.T) { t.Log("Write versions exist (expected)") } } + +//TestValidVersionFormat : test if func returns valid version format +// more regex testing at https://rubular.com/r/UvWXui7EU2icSb +func TestValidVersionFormat(t *testing.T) { + + var version string + version = "0.11.8" + + valid := lib.ValidVersionFormat(version) + + if valid == true { + t.Logf("Valid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "1.11.9" + + valid = lib.ValidVersionFormat(version) + + if valid == true { + t.Logf("Valid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "1.11.a" + + valid = lib.ValidVersionFormat(version) + + if valid == false { + t.Logf("Invalid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "22323" + + valid = lib.ValidVersionFormat(version) + + if valid == false { + t.Logf("Invalid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "@^&*!)!" + + valid = lib.ValidVersionFormat(version) + + if valid == false { + t.Logf("Invalid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "1.11.9-beta1" + + valid = lib.ValidVersionFormat(version) + + if valid == true { + t.Logf("Valid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "0.12.0-rc2" + + valid = lib.ValidVersionFormat(version) + + if valid == true { + t.Logf("Valid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "1.11.4-boom" + + valid = lib.ValidVersionFormat(version) + + if valid == true { + t.Logf("Valid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + + version = "1.11.4-1" + + valid = lib.ValidVersionFormat(version) + + if valid == false { + t.Logf("Invalid version format : %s (expected)", version) + } else { + log.Fatalf("Failed to verify version format: %s\n", version) + } + +} diff --git a/lib/symlink.go b/lib/symlink.go index 7c8c3dc..1f287f1 100644 --- a/lib/symlink.go +++ b/lib/symlink.go @@ -1,19 +1,24 @@ package lib import ( - "fmt" "log" "os" ) +//CreateSymlink : create symlink //CreateSymlink : create symlink func CreateSymlink(cwd string, dir string) { err := os.Symlink(cwd, dir) if err != nil { - fmt.Println(err) - log.Fatalf("Unable to create symlink. You must have SUDO privileges %v \n", err) - panic(err) + log.Fatalf(` + Unable to create new symlink. + Maybe symlink already exist. Try removing existing symlink manually. + Try running "unlink" to remove existing symlink. + If error persist, you may not have the permission to create a symlink at %s. + Error: %s + `, dir, dir, err) + os.Exit(1) } } @@ -22,15 +27,25 @@ func RemoveSymlink(symlinkPath string) { _, err := os.Lstat(symlinkPath) if err != nil { - fmt.Println(err) - log.Fatalf("Unable to find symlink. You must have SUDO privileges - %v \n", err) - panic(err) + log.Fatalf(` + Unable to remove symlink. + Maybe symlink already exist. Try removing existing symlink manually. + Try running "unlink" to remove existing symlink. + If error persist, you may not have the permission to create a symlink at %s. + Error: %s + `, symlinkPath, symlinkPath, err) + os.Exit(1) } else { errRemove := os.Remove(symlinkPath) if errRemove != nil { - fmt.Println(errRemove) - log.Fatalf("Unable to remove symlink. You must have SUDO privileges - %v \n", errRemove) - panic(errRemove) + log.Fatalf(` + Unable to remove symlink. + Maybe symlink already exist. Try removing existing symlink manually. + Try running "unlink" to remove existing symlink. + If error persist, you may not have the permission to create a symlink at %s. + Error: %s + `, symlinkPath, symlinkPath, errRemove) + os.Exit(1) } } } @@ -38,18 +53,12 @@ func RemoveSymlink(symlinkPath string) { // CheckSymlink : check file is symlink func CheckSymlink(symlinkPath string) bool { - //symlink := false - //fmt.Println("Checking symlink") - fi, err := os.Lstat(symlinkPath) if err != nil { - fmt.Println(err) - // symlink = false return false } if fi.Mode()&os.ModeSymlink != 0 { - //symlink = true return true } diff --git a/main.go b/main.go index e6a3381..3b17220 100644 --- a/main.go +++ b/main.go @@ -17,9 +17,11 @@ package main import ( "fmt" + "io/ioutil" "log" "os" "regexp" + "strings" "github.com/manifoldco/promptui" "github.com/pborman/getopt" @@ -29,9 +31,11 @@ import ( const ( terragruntURL = "https://api.github.com/repos/gruntwork-io/terragrunt/releases?" + defaultBin = "/usr/local/bin/terragrunt" //default bin installation dir + rcFilename = ".tgswitchrc" ) -var version = "0.1.0\n" +var version = "0.2.0\n" var CLIENT_ID = "xxx" var CLIENT_SECRET = "xxx" @@ -43,7 +47,7 @@ func main() { client.ClientID = CLIENT_ID client.ClientSecret = CLIENT_SECRET - userBinPath := getopt.StringLong("bin", 'b', "Custom binary path. For example: /Users/username/bin/terragrunt") + custBinPath := getopt.StringLong("bin", 'b', defaultBin, "Custom binary path. For example: /Users/username/bin/terragrunt") versionFlag := getopt.BoolLong("version", 'v', "displays the version of tgswitch") helpFlag := getopt.BoolLong("help", 'h', "displays help message") _ = versionFlag @@ -51,13 +55,39 @@ func main() { getopt.Parse() args := getopt.Args() + dir, err := os.Getwd() + if err != nil { + log.Printf("Failed to get current directory %v\n", err) + os.Exit(1) + } + + rcfile := dir + fmt.Sprintf("/%s", rcFilename) //settings for .tfswitchrc file in current directory + if *versionFlag { fmt.Printf("\nVersion: %v\n", version) } else if *helpFlag { usageMessage() } else { - if len(args) == 1 { + if _, err := os.Stat(rcfile); err == nil && len(args) == 0 { //if there is a .tfswitchrc file, and no commmand line arguments + fmt.Printf("Reading required terraform version %s \n", rcFilename) + + fileContents, err := ioutil.ReadFile(rcfile) + if err != nil { + fmt.Printf("Failed to read %s file. Follow the README.md instructions for setup. https://github.com/warrensbox/terraform-switcher/blob/master/README.md\n", rcFilename) + fmt.Printf("Error: %s\n", err) + os.Exit(1) + } + tfversion := strings.TrimSuffix(string(fileContents), "\n") + _, assets := lib.GetAppList(terragruntURL, &client) + + if lib.ValidVersionFormat(tfversion) { //check if version is correct + lib.Install(terragruntURL, string(tfversion), assets, custBinPath) + } else { + fmt.Println("Invalid terraform version format. Format should be #.#.# or #.#.#-@# where # is numbers and @ is word characters. For example, 0.11.7 and 0.11.9-beta1 are valid versions") + os.Exit(1) + } + } else if len(args) == 1 { semverRegex := regexp.MustCompile(`\A\d+(\.\d+){2}\z`) if semverRegex.MatchString(args[0]) { @@ -68,7 +98,7 @@ func main() { exist := lib.VersionExist(requestedVersion, tflist) if exist { - installLocation := lib.Install(terragruntURL, requestedVersion, assets, userBinPath) + installLocation := lib.Install(terragruntURL, requestedVersion, assets, custBinPath) lib.AddRecent(requestedVersion, installLocation) //add to recent file for faster lookup } else { fmt.Println("Not a valid terragrunt version") @@ -100,7 +130,7 @@ func main() { os.Exit(1) } - installLocation := lib.Install(terragruntURL, tgversion, assets, userBinPath) + installLocation := lib.Install(terragruntURL, tgversion, assets, custBinPath) lib.AddRecent(tgversion, installLocation) //add to recent file for faster lookup os.Exit(0) } else {