From 146e77f82dda4bc2d916dac26f4d7046f4f62a00 Mon Sep 17 00:00:00 2001 From: Johannes Brunswicker Date: Thu, 14 Mar 2024 13:59:37 +0100 Subject: [PATCH] - extracted defaultBin constant to an extra package as it is reused in main and lib. - added detection of home directory in case we are using windows. - updated tests accordingly. --- .github/workflows/go.yml | 2 +- lib/defaults.go | 20 ++++++++++++++ lib/download_test.go | 24 ++++++++++------ lib/install.go | 26 ++++++++--------- lib/symlink_test.go | 60 ++++++++++++++++++++++++++++------------ main.go | 11 ++++---- 6 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 lib/defaults.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a7439696..1a462c07 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,7 +11,7 @@ on: jobs: - example_matrix: + integration_tests: strategy: matrix: os: [ubuntu-latest, windows-latest] diff --git a/lib/defaults.go b/lib/defaults.go new file mode 100644 index 00000000..81ffd303 --- /dev/null +++ b/lib/defaults.go @@ -0,0 +1,20 @@ +package lib + +import ( + "github.com/mitchellh/go-homedir" + "log" + "runtime" +) + +// GetDefaultBin Get default binary path +func GetDefaultBin() string { + var defaultBin = "/usr/local/bin/terraform" + if runtime.GOOS == "windows" { + home, err := homedir.Dir() + if err != nil { + log.Fatalf(`Could not detect home directory.`) + } + defaultBin = home + `/bin/terraform.exe` + } + return defaultBin +} diff --git a/lib/download_test.go b/lib/download_test.go index 22d3358c..79a253ed 100644 --- a/lib/download_test.go +++ b/lib/download_test.go @@ -2,11 +2,12 @@ package lib_test import ( "fmt" + "github.com/mitchellh/go-homedir" "log" "net/url" "os" - "os/user" "path/filepath" + "runtime" "testing" "github.com/warrensbox/terraform-switcher/lib" @@ -21,14 +22,19 @@ func TestDownloadFromURL_FileNameMatch(t *testing.T) { installPath := fmt.Sprintf(tempDir + string(os.PathSeparator) + ".terraform.versions_test") macOS := "_darwin_amd64.zip" - // get current user - usr, errCurr := user.Current() - if errCurr != nil { - log.Fatal(errCurr) + home, err := homedir.Dir() + if err != nil { + log.Fatalf(`Could not detect home directory.`) } - fmt.Printf("Current user: %v \n", usr.HomeDir) - installLocation := filepath.Join(usr.HomeDir, installPath) + fmt.Printf("Current user homedir: %v \n", home) + var installLocation = "" + if runtime.GOOS != "windows" { + installLocation = filepath.Join(home, installPath) + } else { + installLocation = installPath + } + fmt.Printf("Install Location: %v \n", installLocation) // create /.terraform.versions_test/ directory to store code if _, err := os.Stat(installLocation); os.IsNotExist(err) { @@ -44,7 +50,7 @@ func TestDownloadFromURL_FileNameMatch(t *testing.T) { lowestVersion := "0.11.0" url := hashiURL + lowestVersion + "/" + installVersion + lowestVersion + macOS - expectedFile := filepath.Join(usr.HomeDir, installPath, installVersion+lowestVersion+macOS) + expectedFile := filepath.Join(installLocation, installVersion+lowestVersion+macOS) installedFile, errDownload := lib.DownloadFromURL(installLocation, url) if errDownload != nil { @@ -63,7 +69,7 @@ func TestDownloadFromURL_FileNameMatch(t *testing.T) { } //check file name is what is expected - _, err := os.Stat(expectedFile) + _, err = os.Stat(expectedFile) if err != nil { t.Logf("Expected file does not exist %v", expectedFile) } diff --git a/lib/install.go b/lib/install.go index be4ca8fa..2443ce95 100644 --- a/lib/install.go +++ b/lib/install.go @@ -17,7 +17,6 @@ const ( versionPrefix = "terraform_" installPath = ".terraform.versions" recentFile = "RECENT" - defaultBin = "/usr/local/bin/terraform" //default bin installation dir tfDarwinArm64StartVersion = "1.0.2" ) @@ -29,16 +28,17 @@ var ( func initialize() { /* Step 1 */ - /* initilize default binary path for terraform */ + /* initialize default binary path for terraform */ /* assumes that terraform is installed here */ - /* we will find the terraform path instalation later and replace this variable with the correct installed bin path */ - installedBinPath := "/usr/local/bin/terraform" + /* we will find the terraform path installation later and replace this variable with the correct installed bin path */ + //installedBinPath := "/usr/local/bin/terraform" + installedBinPath := GetDefaultBin() /* find terraform binary location if terraform is already installed*/ cmd := NewCommand("terraform") next := cmd.Find() - /* overrride installation default binary path if terraform is already installed */ + /* override installation default binary path if terraform is already installed */ /* find the last bin path */ for path := next(); len(path) > 0; path = next() { installedBinPath = path @@ -75,7 +75,7 @@ func GetInstallLocation() string { } -//Install : Install the provided version in the argument +// Install : Install the provided version in the argument func Install(tfversion string, binPath string, mirrorURL string) { // if !ValidVersionFormat(tfversion) { @@ -85,7 +85,7 @@ func Install(tfversion string, binPath string, mirrorURL string) { /* Check to see if user has permission to the default bin location which is "/usr/local/bin/terraform" * If user does not have permission to default bin location, proceed to create $HOME/bin and install the tfswitch there - * Inform user that they dont have permission to default location, therefore tfswitch was installed in $HOME/bin + * Inform user that they don't have permission to default location, therefore tfswitch was installed in $HOME/bin * Tell users to add $HOME/bin to their path */ binPath = InstallableBinLocation(binPath) @@ -232,7 +232,7 @@ func GetRecentVersions() ([]string, error) { for _, line := range lines { /* checks if versions in the recent file are valid. - If any version is invalid, it will be consider dirty + If any version is invalid, it will be considered dirty and the recent file will be removed */ if !ValidVersionFormat(line) { @@ -252,7 +252,7 @@ func GetRecentVersions() ([]string, error) { return nil, nil } -//CreateRecentFile : create a recent file +// CreateRecentFile : create a recent file func CreateRecentFile(requestedVersion string) { installLocation = GetInstallLocation() //get installation location - this is where we will put our terraform binary file @@ -260,7 +260,7 @@ func CreateRecentFile(requestedVersion string) { WriteLines([]string{requestedVersion}, filepath.Join(installLocation, recentFile)) } -//ConvertExecutableExt : convert excutable with local OS extension +// ConvertExecutableExt : convert excutable with local OS extension func ConvertExecutableExt(fpath string) string { switch runtime.GOOS { case "windows": @@ -273,8 +273,8 @@ func ConvertExecutableExt(fpath string) string { } } -//InstallableBinLocation : Checks if terraform is installable in the location provided by the user. -//If not, create $HOME/bin. Ask users to add $HOME/bin to $PATH and return $HOME/bin as install location +// InstallableBinLocation : Checks if terraform is installable in the location provided by the user. +// If not, create $HOME/bin. Ask users to add $HOME/bin to $PATH and return $HOME/bin as install location func InstallableBinLocation(userBinPath string) string { usr, errCurr := user.Current() @@ -285,7 +285,7 @@ func InstallableBinLocation(userBinPath string) string { binDir := Path(userBinPath) //get path directory from binary path binPathExist := CheckDirExist(binDir) //the default is /usr/local/bin but users can provide custom bin locations - if binPathExist == true { //if bin path exist - check if we can write to to it + if binPathExist == true { //if bin path exist - check if we can write to it binPathWritable := false //assume bin path is not writable if runtime.GOOS != "windows" { diff --git a/lib/symlink_test.go b/lib/symlink_test.go index 08d4f636..15191644 100644 --- a/lib/symlink_test.go +++ b/lib/symlink_test.go @@ -1,10 +1,12 @@ package lib_test import ( + "github.com/mitchellh/go-homedir" "log" "os" "os/user" "path/filepath" + "runtime" "testing" "github.com/warrensbox/terraform-switcher/lib" @@ -14,38 +16,60 @@ import ( // create symlink, check if symlink exist, remove symlink func TestCreateSymlink(t *testing.T) { + testSymlinkDest := "/test-tfswitcher-dest" testSymlinkSrc := "/test-tfswitcher-src" + if runtime.GOOS == "windows" { + testSymlinkSrc = "/test-tfswitcher-src.exe" + } - testSymlinkDest := "/test-tfswitcher-dest" + home, err := homedir.Dir() + if err != nil { + log.Fatalf(`Could not detect home directory.`) + } + symlinkPathSrc := filepath.Join(home, testSymlinkSrc) + symlinkPathDest := filepath.Join(home, testSymlinkDest) - usr, errCurr := user.Current() - if errCurr != nil { - log.Fatal(errCurr) + // Create file for test as windows does not like no source + create, err := os.Create(symlinkPathDest) + if err != nil { + log.Fatalf(`Could not create test dest file for symlink at %v`, symlinkPathDest) } - symlinkPathSrc := filepath.Join(usr.HomeDir, testSymlinkSrc) - symlinkPathDest := filepath.Join(usr.HomeDir, testSymlinkDest) + defer create.Close() - ln, _ := os.Readlink(symlinkPathSrc) + if runtime.GOOS != "windows" { + ln, _ := os.Readlink(symlinkPathSrc) - if ln != symlinkPathDest { - t.Logf("Symlink does not exist %v [expected]", ln) - } else { - t.Logf("Symlink exist %v [expected]", ln) - os.Remove(symlinkPathSrc) - t.Logf("Removed existing symlink for testing purposes") + if ln != symlinkPathDest { + t.Logf("Symlink does not exist %v [expected]", ln) + } else { + t.Logf("Symlink exist %v [expected]", ln) + os.Remove(symlinkPathSrc) + t.Logf("Removed existing symlink for testing purposes") + } } lib.CreateSymlink(symlinkPathDest, symlinkPathSrc) - lnCheck, _ := os.Readlink(symlinkPathSrc) - if lnCheck == symlinkPathDest { - t.Logf("Symlink exist %v [expected]", lnCheck) + if runtime.GOOS == "windows" { + _, err := os.Stat(symlinkPathSrc + ".exe") + if err != nil { + t.Logf("Could not stat file copy at %v. [unexpected]", symlinkPathSrc) + t.Error("File copy was not created.") + } else { + t.Logf("File copy exists at %v [expected]", symlinkPathSrc) + } } else { - t.Logf("Symlink does not exist %v [unexpected]", lnCheck) - t.Error("Symlink was not created") + lnCheck, _ := os.Readlink(symlinkPathSrc) + if lnCheck == symlinkPathDest { + t.Logf("Symlink exist %v [expected]", lnCheck) + } else { + t.Logf("Symlink does not exist %v [unexpected]", lnCheck) + t.Error("Symlink was not created") + } } os.Remove(symlinkPathSrc) + os.Remove(symlinkPathDest) } // TestRemoveSymlink : check if symlink exist-create if does not exist, diff --git a/main.go b/main.go index 0d96c530..abc6590f 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,7 @@ package main /*** OPERATION WORKFLOW ***/ /* -* 1- Create /usr/local/terraform directory if does not exist +* 1- Create /usr/local/terraform directory if it does not exist * 2- Download zip file from url to /usr/local/terraform * 3- Unzip the file to /usr/local/terraform * 4- Rename the file from `terraform` to `terraform_version` @@ -39,7 +39,6 @@ import ( const ( defaultMirror = "https://releases.hashicorp.com/terraform" - defaultBin = "/usr/local/bin/terraform" //default bin installation dir defaultLatest = "" tfvFilename = ".terraform-version" rcFilename = ".tfswitchrc" @@ -52,7 +51,7 @@ var version = "0.12.0\n" func main() { dir := lib.GetCurrentDirectory() - custBinPath := getopt.StringLong("bin", 'b', lib.ConvertExecutableExt(defaultBin), "Custom binary path. Ex: tfswitch -b "+lib.ConvertExecutableExt("/Users/username/bin/terraform")) + custBinPath := getopt.StringLong("bin", 'b', lib.ConvertExecutableExt(lib.GetDefaultBin()), "Custom binary path. Ex: tfswitch -b "+lib.ConvertExecutableExt("/Users/username/bin/terraform")) listAllFlag := getopt.BoolLong("list-all", 'l', "List all versions of terraform - including beta and rc") latestPre := getopt.StringLong("latest-pre", 'p', defaultLatest, "Latest pre-release implicit version. Ex: tfswitch --latest-pre 0.13 downloads 0.13.0-rc1 (latest)") showLatestPre := getopt.StringLong("show-latest-pre", 'P', defaultLatest, "Show latest pre-release implicit version. Ex: tfswitch --show-latest-pre 0.13 prints 0.13.0-rc1 (latest)") @@ -309,7 +308,7 @@ func installVersion(arg string, custBinPath *string, mirrorURL *string) { } } -//retrive file content of regular file +// retrive file content of regular file func retrieveFileContents(file string) string { fileContents, err := ioutil.ReadFile(file) if err != nil { @@ -376,8 +375,8 @@ func getParamsTOML(binPath string, dir string) (string, string) { os.Exit(1) // exit immediately if config file provided but it is unable to read it } - bin := viper.Get("bin") // read custom binary location - if binPath == lib.ConvertExecutableExt(defaultBin) && bin != nil { // if the bin path is the same as the default binary path and if the custom binary is provided in the toml file (use it) + bin := viper.Get("bin") // read custom binary location + if binPath == lib.ConvertExecutableExt(lib.GetDefaultBin()) && bin != nil { // if the bin path is the same as the default binary path and if the custom binary is provided in the toml file (use it) binPath = os.ExpandEnv(bin.(string)) } //fmt.Println(binPath) //uncomment this to debug