Skip to content

Commit 05ad223

Browse files
committed
Feature: Add flag for install location (optional)
Add the ability to pass -i or --install to change the default install location for the Terraform binaries
1 parent 9059ac6 commit 05ad223

File tree

10 files changed

+119
-91
lines changed

10 files changed

+119
-91
lines changed

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,36 @@ The installation is minimal and easy.
1313
Once installed, simply select the version you require from the dropdown and start using Terraform.
1414

1515
## Documentation
16+
1617
Click [here](https://tfswitch.warrensbox.com) for our extended documentation.
1718

1819
## NOTE
20+
1921
Going forward we will change the version identifier of `tfswitch` to align with the common go package versioning.
2022
Please be advised to change any automated implementation you might have that is relying on the `tfswitch` version string.
2123
**Old version string:** `0.1.2412`
2224
**New version string:** `v1.0.0` Note the `v` that is preceding all version numbers.
2325

2426
## Installation
25-
`tfswitch` is available as a binary and on various package managers (eg. Homebrew).
27+
28+
`tfswitch` is available as a binary and on various package managers (eg. Homebrew).
2629

2730
## Windows
31+
2832
Download and extract the Windows version of `tfswitch` that is compatible with your system.
2933
We are building binaries for 386, amd64, arm6 and arm7 CPU structure.
3034
See the [release page](https://github.com/warrensbox/terraform-switcher/releases/latest) for your download.
3135

3236
## Homebrew
37+
3338
For macOS or various Linux distributions, Homebrew offers the simplest installation process. <a href="https://brew.sh/" target="_blank">If you do not have homebrew installed, click here</a>.
3439

3540
```ruby
3641
brew install warrensbox/tap/tfswitch
3742
```
3843

3944
## Linux
45+
4046
Installation for Linux operating systems.
4147

4248
```sh
@@ -59,34 +65,41 @@ Alternatively, you can install the binary from the source <a href="https://githu
5965

6066
See [our installation documentation](https://tfswitch.warrensbox.com/Install) for more details.
6167

62-
> [!IMPORTANT]
68+
> [!IMPORTANT]
6369
> The version identifier of `tfswitch` has changed to align with the common `go` package versioning.
6470
>
6571
> Version numbers will now be prefixed with a `v` - eg. `v1.0.3`.
6672
>
67-
> Please change any automated implementations relying on the `tfswitch` version string.
73+
> Please change any automated implementations relying on the `tfswitch` version string.
6874
>
6975
> **Old version string:** `0.1.2412`
7076
> **New version string:** `v1.0.3`
7177
7278
[Having trouble installing](https://tfswitch.warrensbox.com/Troubleshoot/)
7379

7480
## Quick Start
81+
7582
### Dropdown Menu
83+
7684
Execute `tfswitch` and select the desired Terraform version via the dropdown menu.
85+
7786
### Version on command line
87+
7888
Use `tfswitch 1.7.0` to install Terraform version 1.7.0. Replace the version number as required.
7989

8090
More [usage guide here](https://tfswitch.warrensbox.com/Quick-Start/)
8191

8292
## How to contribute
83-
An open source project becomes meaningful when people collaborate to improve the code.
93+
94+
An open source project becomes meaningful when people collaborate to improve the code.
8495
Feel free to look at the code, critique and make suggestions. Let's make `tfswitch` better!
8596

8697
See step-by-step instructions on how to contribute here: [Contribute](https://tfswitch.warrensbox.com/How-to-Contribute/)
8798

8899
## Additional Info
100+
89101
See how to *upgrade*, *uninstall*, *troubleshoot* here: [More info](https://tfswitch.warrensbox.com/Upgrade-or-Uninstall/)
90102

91103
## Issues
92-
Please open *issues* here: [New Issue](https://github.com/warrensbox/terraform-switcher/issues)
104+
105+
Please open *issues* here: [New Issue](https://github.com/warrensbox/terraform-switcher/issues)

lib/common.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import (
77

88
func checkFileExist(file string) bool {
99
_, err := os.Stat(file)
10-
if err != nil {
11-
return false
12-
}
13-
return true
10+
return err == nil
1411
}
1512

1613
func createFile(path string) {

lib/defaults.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package lib
22

33
import (
4-
"os"
54
"runtime"
6-
7-
"github.com/mitchellh/go-homedir"
85
)
96

107
var (
@@ -21,11 +18,7 @@ const (
2118
func GetDefaultBin() string {
2219
var defaultBin = "/usr/local/bin/terraform"
2320
if runtime.GOOS == "windows" {
24-
home, err := homedir.Dir()
25-
if err != nil {
26-
logger.Fatal("Could not detect home directory.")
27-
os.Exit(1)
28-
}
21+
home := GetHomeDirectory()
2922
defaultBin = home + "/bin/terraform.exe"
3023
}
3124
return defaultBin
@@ -35,7 +28,7 @@ const (
3528
DefaultMirror = "https://releases.hashicorp.com/terraform"
3629
DefaultLatest = ""
3730
installFile = "terraform"
38-
installPath = ".terraform.versions"
31+
installDir = ".terraform.versions"
3932
recentFile = "RECENT"
4033
tfDarwinArm64StartVersion = "1.0.2"
4134
VersionPrefix = "terraform_"

lib/files.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"path/filepath"
1111
"strings"
1212
"sync"
13+
14+
"github.com/mitchellh/go-homedir"
1315
)
1416

1517
// RenameFile : rename file name
@@ -38,10 +40,7 @@ func RemoveFiles(src string) {
3840
// CheckFileExist : check if file exist in directory
3941
func CheckFileExist(file string) bool {
4042
_, err := os.Stat(file)
41-
if err != nil {
42-
return false
43-
}
44-
return true
43+
return err == nil
4544
}
4645

4746
// Unzip will decompress a zip archive, moving all files and folders
@@ -205,6 +204,16 @@ func GetCurrentDirectory() string {
205204
return dir
206205
}
207206

207+
// GetHomeDirectory : return the user's home directory
208+
func GetHomeDirectory() string {
209+
210+
homedir, err := homedir.Dir()
211+
if err != nil {
212+
logger.Fatalf("Failed to get user's home directory %v", err)
213+
}
214+
return homedir
215+
}
216+
208217
func unzipFile(f *zip.File, destination string, wg *sync.WaitGroup) error {
209218
defer wg.Done()
210219
// 1. Check if file paths are not vulnerable to Zip Slip

lib/install.go

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package lib
22

33
import (
44
"fmt"
5-
"github.com/manifoldco/promptui"
65
"os"
76
"path/filepath"
87
"runtime"
98
"strings"
109

10+
"github.com/manifoldco/promptui"
11+
1112
"github.com/hashicorp/go-version"
12-
"github.com/mitchellh/go-homedir"
1313
)
1414

1515
var (
@@ -38,36 +38,27 @@ func initialize(binPath string) {
3838
}
3939

4040
// getInstallLocation : get location where the terraform binary will be installed,
41-
// will create a directory in the home location if it does not exist
42-
func getInstallLocation() string {
43-
/* get current user */
44-
homeDir, errCurr := homedir.Dir()
45-
if errCurr != nil {
46-
logger.Fatal(errCurr)
47-
os.Exit(1)
48-
}
49-
50-
userCommon := homeDir
51-
41+
// will create the installDir if it does not exist
42+
func getInstallLocation(installPath string) string {
5243
/* set installation location */
53-
installLocation = filepath.Join(userCommon, installPath)
44+
installLocation = filepath.Join(installPath, installDir)
5445

5546
/* Create local installation directory if it does not exist */
5647
createDirIfNotExist(installLocation)
5748
return installLocation
5849
}
5950

6051
// install : install the provided version in the argument
61-
func install(tfversion string, binPath string, mirrorURL string) {
52+
func install(tfversion string, binPath string, installPath string, mirrorURL string) {
6253
/* Check to see if user has permission to the default bin location which is "/usr/local/bin/terraform"
6354
* If user does not have permission to default bin location, proceed to create $HOME/bin and install the tfswitch there
6455
* Inform user that they don't have permission to default location, therefore tfswitch was installed in $HOME/bin
6556
* Tell users to add $HOME/bin to their path
6657
*/
6758
binPath = installableBinLocation(binPath)
6859

69-
initialize(binPath) //initialize path
70-
installLocation = getInstallLocation() //get installation location - this is where we will put our terraform binary file
60+
initialize(binPath) //initialize path
61+
installLocation = getInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
7162

7263
goarch := runtime.GOARCH
7364
goos := runtime.GOOS
@@ -96,7 +87,7 @@ func install(tfversion string, binPath string, mirrorURL string) {
9687
/* set symlink to desired version */
9788
CreateSymlink(installFileVersionPath, binPath)
9889
logger.Infof("Switched terraform to version %q", tfversion)
99-
addRecent(tfversion) //add to recent file for faster lookup
90+
addRecent(tfversion, installPath) //add to recent file for faster lookup
10091
os.Exit(0)
10192
}
10293

@@ -138,14 +129,14 @@ func install(tfversion string, binPath string, mirrorURL string) {
138129
/* set symlink to desired version */
139130
CreateSymlink(installFileVersionPath, binPath)
140131
logger.Infof("Switched terraform to version %q", tfversion)
141-
addRecent(tfversion) //add to recent file for faster lookup
132+
addRecent(tfversion, installPath) //add to recent file for faster lookup
142133
os.Exit(0)
143134
}
144135

145136
// addRecent : add to recent file
146-
func addRecent(requestedVersion string) {
137+
func addRecent(requestedVersion string, installPath string) {
147138

148-
installLocation = getInstallLocation() //get installation location - this is where we will put our terraform binary file
139+
installLocation = getInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
149140
versionFile := filepath.Join(installLocation, recentFile)
150141

151142
fileExist := CheckFileExist(versionFile)
@@ -161,7 +152,7 @@ func addRecent(requestedVersion string) {
161152
if !validVersionFormat(line) {
162153
logger.Infof("File %q is dirty (recreating cache file)", versionFile)
163154
RemoveFiles(versionFile)
164-
CreateRecentFile(requestedVersion)
155+
CreateRecentFile(requestedVersion, installPath)
165156
return
166157
}
167158
}
@@ -181,14 +172,14 @@ func addRecent(requestedVersion string) {
181172
}
182173

183174
} else {
184-
CreateRecentFile(requestedVersion)
175+
CreateRecentFile(requestedVersion, installPath)
185176
}
186177
}
187178

188179
// getRecentVersions : get recent version from file
189-
func getRecentVersions() ([]string, error) {
180+
func getRecentVersions(installPath string) ([]string, error) {
190181

191-
installLocation = getInstallLocation() //get installation location - this is where we will put our terraform binary file
182+
installLocation = getInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
192183
versionFile := filepath.Join(installLocation, recentFile)
193184

194185
fileExist := CheckFileExist(versionFile)
@@ -225,8 +216,8 @@ func getRecentVersions() ([]string, error) {
225216
}
226217

227218
// CreateRecentFile : create RECENT file
228-
func CreateRecentFile(requestedVersion string) {
229-
installLocation = getInstallLocation() //get installation location - this is where we will put our terraform binary file
219+
func CreateRecentFile(requestedVersion string, installPath string) {
220+
installLocation = getInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
230221
_ = WriteLines([]string{requestedVersion}, filepath.Join(installLocation, recentFile))
231222
}
232223

@@ -247,16 +238,11 @@ func ConvertExecutableExt(fpath string) string {
247238
// If not, create $HOME/bin. Ask users to add $HOME/bin to $PATH and return $HOME/bin as install location
248239
func installableBinLocation(userBinPath string) string {
249240

250-
homedir, errCurr := homedir.Dir()
251-
if errCurr != nil {
252-
logger.Fatal(errCurr)
253-
os.Exit(1)
254-
}
255-
241+
homedir := GetHomeDirectory() //get user's home directory
256242
binDir := Path(userBinPath) //get path directory from binary path
257243
binPathExist := CheckDirExist(binDir) //the default is /usr/local/bin but users can provide custom bin locations
258244

259-
if binPathExist == true { //if bin path exist - check if we can write to it
245+
if binPathExist { //if bin path exist - check if we can write to it
260246

261247
binPathWritable := false //assume bin path is not writable
262248
if runtime.GOOS != "windows" {
@@ -288,38 +274,38 @@ func installableBinLocation(userBinPath string) string {
288274
}
289275

290276
// InstallLatestVersion install latest stable tf version
291-
func InstallLatestVersion(customBinaryPath, mirrorURL string) {
277+
func InstallLatestVersion(customBinaryPath, installPath string, mirrorURL string) {
292278
tfversion, _ := getTFLatest(mirrorURL)
293-
install(tfversion, customBinaryPath, mirrorURL)
279+
install(tfversion, customBinaryPath, installPath, mirrorURL)
294280
}
295281

296282
// InstallLatestImplicitVersion install latest - argument (version) must be provided
297-
func InstallLatestImplicitVersion(requestedVersion, customBinaryPath, mirrorURL string, preRelease bool) {
283+
func InstallLatestImplicitVersion(requestedVersion, customBinaryPath, installPath string, mirrorURL string, preRelease bool) {
298284
_, err := version.NewConstraint(requestedVersion)
299285
if err != nil {
300286
logger.Errorf("Error parsing constraint %q: %v", requestedVersion, err)
301287
}
302288
tfversion, err := getTFLatestImplicit(mirrorURL, preRelease, requestedVersion)
303289
if err == nil && tfversion != "" {
304-
install(tfversion, customBinaryPath, mirrorURL)
290+
install(tfversion, customBinaryPath, installPath, mirrorURL)
305291
}
306292
logger.Errorf("Error parsing constraint %q: %v", requestedVersion, err)
307293
PrintInvalidMinorTFVersion()
308294
}
309295

310296
// InstallVersion install with provided version as argument
311-
func InstallVersion(arg, customBinaryPath, mirrorURL string) {
297+
func InstallVersion(arg, customBinaryPath, installPath string, mirrorURL string) {
312298
if validVersionFormat(arg) {
313299
requestedVersion := arg
314300

315301
//check to see if the requested version has been downloaded before
316-
installLocation := getInstallLocation()
302+
installLocation := getInstallLocation(installPath)
317303
installFileVersionPath := ConvertExecutableExt(filepath.Join(installLocation, VersionPrefix+requestedVersion))
318304
recentDownloadFile := CheckFileExist(installFileVersionPath)
319305
if recentDownloadFile {
320306
ChangeSymlink(installFileVersionPath, customBinaryPath)
321307
logger.Infof("Switched terraform to version %q", requestedVersion)
322-
addRecent(requestedVersion) //add to recent file for faster lookup
308+
addRecent(requestedVersion, installPath) //add to recent file for faster lookup
323309
os.Exit(0)
324310
}
325311

@@ -329,7 +315,7 @@ func InstallVersion(arg, customBinaryPath, mirrorURL string) {
329315
exist := versionExist(requestedVersion, tflist) // Check if version exists before downloading it
330316

331317
if exist {
332-
install(requestedVersion, customBinaryPath, mirrorURL)
318+
install(requestedVersion, customBinaryPath, installPath, mirrorURL)
333319
} else {
334320
logger.Fatal("The provided terraform version does not exist.\n Try `tfswitch -l` to see all available versions")
335321
os.Exit(1)
@@ -346,11 +332,11 @@ func InstallVersion(arg, customBinaryPath, mirrorURL string) {
346332
// InstallOption displays & installs tf version
347333
/* listAll = true - all versions including beta and rc will be displayed */
348334
/* listAll = false - only official stable release are displayed */
349-
func InstallOption(listAll bool, customBinaryPath, mirrorURL string) {
350-
tflist, _ := getTFList(mirrorURL, listAll) // Get list of versions
351-
recentVersions, _ := getRecentVersions() // Get recent versions from RECENT file
352-
tflist = append(recentVersions, tflist...) // Append recent versions to the top of the list
353-
tflist = removeDuplicateVersions(tflist) // Remove duplicate version
335+
func InstallOption(listAll bool, customBinaryPath, installPath string, mirrorURL string) {
336+
tflist, _ := getTFList(mirrorURL, listAll) // Get list of versions
337+
recentVersions, _ := getRecentVersions(installPath) // Get recent versions from RECENT file
338+
tflist = append(recentVersions, tflist...) // Append recent versions to the top of the list
339+
tflist = removeDuplicateVersions(tflist) // Remove duplicate version
354340

355341
if len(tflist) == 0 {
356342
logger.Fatalf("Terraform version list is empty: %s", mirrorURL)
@@ -375,6 +361,6 @@ func InstallOption(listAll bool, customBinaryPath, mirrorURL string) {
375361
}
376362
}
377363

378-
install(tfversion, customBinaryPath, mirrorURL)
364+
install(tfversion, customBinaryPath, installPath, mirrorURL)
379365
os.Exit(0)
380366
}

0 commit comments

Comments
 (0)