Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add flag for install location (optional) #140

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ tgswitch --chdir terragrunt_dir
tgswitch -c terragrunt_dir
```

### Install to non-default location

By default `tgswitch` will download the Terragrunt binary to the user home directory under this path: `/Users/warrenveerasingam/.terragrunt.versions`

If you want to install the binaries outside of the home directory then you can provide the `-i` or `--install` to install Terragrunt binaries to a non-standard path. Useful if you want to install versions of Terragrunt that can be shared with multiple users.

The Terragrunt binaries will then be placed in the directory `.terragrunt.versions` under the custom install path e.g. `/opt/terragrunt/.terragrunt.versions`

```bash
tgswitch -i /opt/terragrunt/
```

**NOTE**

* The directory passed in `-i`/`--install` must be created before running `tgswitch`

**Automatically switch with bash**

Add the following to the end of your `~/.bashrc` file:
Expand Down
54 changes: 26 additions & 28 deletions lib/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
gruntURL = "https://github.com/gruntwork-io/terragrunt/releases/download/"
installFile = "terragrunt"
installVersion = "terragrunt_"
installPath = "/.terragrunt.versions/"
installDir = ".terragrunt.versions"
recentFile = "RECENT"
)

Expand Down Expand Up @@ -50,24 +50,22 @@ func initialize() {
}

// GetInstallLocation : get location where the terragrunt binary will be installed,
// will create a directory in the home location if it does not exist
func GetInstallLocation() string {
/* get current user */
usr, errCurr := user.Current()
if errCurr != nil {
log.Fatal(errCurr)
}
// will create the installDir if it does not exist
func GetInstallLocation(installPath string) string {
/* set installation location */
installLocation = usr.HomeDir + installPath
installLocation = filepath.Join(installPath, installDir)

/* Create local installation directory if it does not exist */
CreateDirIfNotExist(installLocation)

return installLocation

}

// AddRecent : add to recent file
func AddRecent(requestedVersion string) {
func AddRecent(requestedVersion string, installPath string) {

installLocation = GetInstallLocation()
installLocation = GetInstallLocation(installPath)

semverRegex := regexp.MustCompile(`\d+(\.\d+){2}\z`)

Expand All @@ -83,7 +81,7 @@ func AddRecent(requestedVersion string) {
for _, line := range lines {
if !semverRegex.MatchString(line) {
RemoveFiles(installLocation + recentFile)
CreateRecentFile(requestedVersion)
CreateRecentFile(requestedVersion, installPath)
return
}
}
Expand All @@ -103,14 +101,14 @@ func AddRecent(requestedVersion string) {
}

} else {
CreateRecentFile(requestedVersion)
CreateRecentFile(requestedVersion, installPath)
}
}

// GetRecentVersions : get recent version from file
func GetRecentVersions() ([]string, error) {
func GetRecentVersions(installPath string) ([]string, error) {

installLocation = GetInstallLocation()
installLocation = GetInstallLocation(installPath)

fileExist := CheckFileExist(installLocation + recentFile)
if fileExist {
Expand Down Expand Up @@ -140,10 +138,10 @@ func GetRecentVersions() ([]string, error) {
return nil, nil
}

//CreateRecentFile : create a recent file
func CreateRecentFile(requestedVersion string) {
// CreateRecentFile : create a recent file
func CreateRecentFile(requestedVersion string, installPath string) {

installLocation = GetInstallLocation()
installLocation = GetInstallLocation(installPath)
WriteLines([]string{requestedVersion}, installLocation+recentFile)
}

Expand All @@ -168,17 +166,17 @@ func ValidVersionFormat(version string) bool {
return semverRegex.MatchString(version)
}

//Install : Install the provided version in the argument
func Install(tgversion string, usrBinPath string, mirrorURL string) string {
// Install : Install the provided version in the argument
func Install(tgversion string, usrBinPath string, installPath string, mirrorURL string) string {
/* Check to see if user has permission to the default bin location which is "/usr/local/bin/terragrunt"
* If user does not have permission to default bin location, proceed to create $HOME/bin and install the tgswitch there
* Inform user that they dont have permission to default location, therefore tgswitch was installed in $HOME/bin
* Tell users to add $HOME/bin to their path
*/
binPath := InstallableBinLocation(usrBinPath)

initialize() //initialize path
installLocation = GetInstallLocation() //get installation location - this is where we will put our terragrunt binary file
initialize() //initialize path
installLocation = GetInstallLocation(installPath) //get installation location - this is where we will put our terragrunt binary file

goarch := runtime.GOARCH
goos := runtime.GOOS
Expand All @@ -200,7 +198,7 @@ func Install(tgversion string, usrBinPath string, mirrorURL string) string {
/* set symlink to desired version */
CreateSymlink(installFileVersionPath, binPath)
fmt.Printf("Switched terragrunt to version %q \n", tgversion)
AddRecent(tgversion) //add to recent file for faster lookup
AddRecent(tgversion, installPath) //add to recent file for faster lookup
os.Exit(0)
}

Expand Down Expand Up @@ -239,14 +237,14 @@ func Install(tgversion string, usrBinPath string, mirrorURL string) string {
/* set symlink to desired version */
CreateSymlink(installFileVersionPath, binPath)
fmt.Printf("Switched terragrunt to version %q \n", tgversion)
AddRecent(tgversion) //add to recent file for faster lookup
AddRecent(tgversion, installPath) //add to recent file for faster lookup
os.Exit(0)
return ""
}

//InstallableBinLocation : Checks if terragrunt is installable in the location provided by the user.
//If not, create $HOME/bin. Ask users to add $HOME/bin to $PATH
//Return $HOME/bin as install location
// InstallableBinLocation : Checks if terragrunt is installable in the location provided by the user.
// If not, create $HOME/bin. Ask users to add $HOME/bin to $PATH
// Return $HOME/bin as install location
func InstallableBinLocation(userBinPath string) string {

usr, errCurr := user.Current()
Expand Down Expand Up @@ -294,7 +292,7 @@ func PrintCreateDirStmt(unableDir string, writable string) {
fmt.Printf("RUN `export PATH=$PATH:%s` to append bin to $PATH\n", writable)
}

//ConvertExecutableExt : convert excutable with local OS extension
// ConvertExecutableExt : convert excutable with local OS extension
func ConvertExecutableExt(fpath string) string {
switch runtime.GOOS {
case "windows":
Expand Down
70 changes: 38 additions & 32 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ var version = "0.5.0\n"
func main() {

dir := lib.GetCurrentDirectory()
homedir := lib.GetHomeDirectory()

custBinPath := getopt.StringLong("bin", 'b', lib.ConvertExecutableExt(defaultBin), "Custom binary path. Ex: tgswitch -b "+lib.ConvertExecutableExt("/Users/username/bin/terragrunt"))
installPath := getopt.StringLong("install", 'i', homedir, "Custom install path. Ex: tfswitch -i /Users/username")
versionFlag := getopt.BoolLong("version", 'v', "displays the version of tgswitch")
helpFlag := getopt.BoolLong("help", 'h', "displays help message")
chDirPath := getopt.StringLong("chdir", 'c', dir, "Switch to a different working directory before executing the given command. Ex: tgswitch --chdir terragrunt dir will run tgswitch in the directory")
Expand All @@ -61,8 +64,6 @@ func main() {
os.Exit(1)
}

homedir := lib.GetHomeDirectory()

TOMLConfigFile := filepath.Join(*chDirPath, tomlFilename) //settings for .tgswitch.toml file in current directory (option to specify bin directory)
HomeTOMLConfigFile := filepath.Join(homedir, tomlFilename) //settings for .tgswitch.toml file in home directory (option to specify bin directory)
RCFile := filepath.Join(*chDirPath, rcFilename) //settings for .tgswitchrc file in current directory (backward compatible purpose)
Expand All @@ -84,9 +85,9 @@ func main() {
version := ""
binPath := *custBinPath
if lib.FileExists(TOMLConfigFile) { //read from toml from current directory
version, binPath = GetParamsTOML(binPath, *chDirPath)
version, binPath, *installPath = GetParamsTOML(binPath, *installPath, *chDirPath)
} else { // else read from toml from home directory
version, binPath = GetParamsTOML(binPath, homedir)
version, binPath, *installPath = GetParamsTOML(binPath, *installPath, homedir)
}

/* GIVEN A TOML FILE, */
Expand All @@ -99,7 +100,7 @@ func main() {
exist := lib.VersionExist(requestedVersion, listOfVersions)

if exist {
installLocation := lib.Install(requestedVersion, binPath, terragruntURL)
installLocation := lib.Install(requestedVersion, binPath, *installPath, terragruntURL)
fmt.Println("Install Location:", installLocation)
}
} else {
Expand All @@ -110,26 +111,26 @@ func main() {
case lib.FileExists(RCFile) && len(args) == 0:
lib.ReadingFileMsg(rcFilename)
tgversion := lib.RetrieveFileContents(RCFile)
installVersion(tgversion, &binPath)
installVersion(tgversion, &binPath, installPath)
/* if .terragrunt-version file found (IN ADDITION TO A TOML FILE) */
case lib.FileExists(TGVersionFile) && len(args) == 0:
lib.ReadingFileMsg(TGVersionFile)
tgversion := lib.RetrieveFileContents(TGVersionFile)
installVersion(tgversion, &binPath)
installVersion(tgversion, &binPath, installPath)
/* if terragrunt.hcl file found (IN ADDITION TO A TOML FILE) */
case lib.FileExists(TGHACLFile) && checkVersionDefinedHCL(&TGHACLFile) && len(args) == 0:
installTGHclFile(&TGHACLFile, binPath, terragruntURL)
installTGHclFile(&TGHACLFile, binPath, *installPath, terragruntURL)
/* if terragrunt Version environment variable is set (IN ADDITION TO A TOML FILE)*/
case checkTGEnvExist() && len(args) == 0 && version == "":
tgversion := os.Getenv("TG_VERSION")
fmt.Printf("Terragrunt version environment variable: %s\n", tgversion)
installVersion(tgversion, &binPath)
installVersion(tgversion, &binPath, installPath)
/* if version is specified in the .toml file */
case version != "":
lib.Install(version, binPath, terragruntURL)
lib.Install(version, binPath, *installPath, terragruntURL)
/* show dropdown */
default:
installFromList(&binPath)
installFromList(&binPath, installPath)
}

case len(args) == 1:
Expand All @@ -140,7 +141,7 @@ func main() {
exist := lib.VersionExist(requestedVersion, listOfVersions)

if exist {
installLocation := lib.Install(requestedVersion, *custBinPath, terragruntURL)
installLocation := lib.Install(requestedVersion, *custBinPath, *installPath, terragruntURL)
fmt.Println("Install Location:", installLocation)
}
} else {
Expand All @@ -151,22 +152,22 @@ func main() {
case lib.FileExists(RCFile) && len(args) == 0:
lib.ReadingFileMsg(rcFilename)
tgversion := lib.RetrieveFileContents(RCFile)
installVersion(tgversion, custBinPath)
installVersion(tgversion, custBinPath, installPath)
case lib.FileExists(TGVersionFile) && len(args) == 0:
lib.ReadingFileMsg(TGVersionFile)
tgversion := lib.RetrieveFileContents(TGVersionFile)
installVersion(tgversion, custBinPath)
installVersion(tgversion, custBinPath, installPath)
/* if terragrunt.hcl file found */
case lib.FileExists(TGHACLFile) && checkVersionDefinedHCL(&TGHACLFile) && len(args) == 0:
installTGHclFile(&TGHACLFile, *custBinPath, terragruntURL)
installTGHclFile(&TGHACLFile, *custBinPath, *installPath, terragruntURL)
/* if terragrunt Version environment variable is set*/
case checkTGEnvExist() && len(args) == 0:
tgversion := os.Getenv("TG_VERSION")
fmt.Printf("Terragrunt version environment variable: %s\n", tgversion)
installVersion(tgversion, custBinPath)
installVersion(tgversion, custBinPath, installPath)
/* show dropdown */
default:
installFromList(custBinPath)
installFromList(custBinPath, installPath)
os.Exit(0)
}

Expand All @@ -179,7 +180,7 @@ func usageMessage() {
}

/* parses everything in the toml file, return required version and bin path */
func GetParamsTOML(binPath string, dir string) (string, string) {
func GetParamsTOML(binPath string, installPath string, dir string) (string, string, string) {
path := lib.GetHomeDirectory()
if dir == path {
path = "home directory"
Expand Down Expand Up @@ -207,14 +208,19 @@ func GetParamsTOML(binPath string, dir string) (string, string) {
version = ""
}

return version.(string), binPath
install := viper.Get("install") //attempt to get the install path if it's provided in the toml
if install != nil {
installPath = os.ExpandEnv(install.(string))
}

return version.(string), binPath, installPath
}

/* installFromList : displays & installs tf version */
func installFromList(custBinPath *string) {
func installFromList(custBinPath *string, installPath *string) {

listOfVersions := lib.GetAppList(proxyUrl)
recentVersions, _ := lib.GetRecentVersions() //get recent versions from RECENT file
recentVersions, _ := lib.GetRecentVersions(*installPath) //get recent versions from RECENT file
listOfVersions = append(recentVersions, listOfVersions...) //append recent versions to the top of the list
listOfVersions = lib.RemoveDuplicateVersions(listOfVersions) //remove duplicate version

Expand All @@ -230,23 +236,23 @@ func installFromList(custBinPath *string) {
os.Exit(1)
}

lib.Install(tgversion, *custBinPath, terragruntURL)
lib.Install(tgversion, *custBinPath, *installPath, terragruntURL)
os.Exit(0)
}

// install with provided version as argument
func installVersion(arg string, custBinPath *string) {
func installVersion(arg string, custBinPath *string, installPath *string) {
if lib.ValidVersionFormat(arg) {
requestedVersion := arg

//check to see if the requested version has been downloaded before
installLocation := lib.GetInstallLocation()
installLocation := lib.GetInstallLocation(*installPath)
installFileVersionPath := lib.ConvertExecutableExt(filepath.Join(installLocation, versionPrefix+requestedVersion))
recentDownloadFile := lib.CheckFileExist(installFileVersionPath)
if recentDownloadFile {
lib.ChangeSymlink(installFileVersionPath, *custBinPath)
fmt.Printf("Switched terragrunt to version %q \n", requestedVersion)
lib.AddRecent(requestedVersion) //add to recent file for faster lookup
lib.AddRecent(requestedVersion, *installPath) //add to recent file for faster lookup
os.Exit(0)
}

Expand All @@ -257,7 +263,7 @@ func installVersion(arg string, custBinPath *string) {
exist := lib.VersionExist(requestedVersion, listOfVersions)

if exist {
installLocation := lib.Install(requestedVersion, *custBinPath, terragruntURL)
installLocation := lib.Install(requestedVersion, *custBinPath, *installPath, terragruntURL)
fmt.Println("Install Location:", installLocation)
}

Expand All @@ -274,19 +280,19 @@ func checkTGEnvExist() bool {
}

// install using a version constraint
func installFromConstraint(tgconstraint *string, custBinPath, mirrorURL string) {
func installFromConstraint(tgconstraint *string, custBinPath, installPath, mirrorURL string) {

tgversion, err := lib.GetSemver(tgconstraint, mirrorURL)
if err == nil {
lib.Install(tgversion, custBinPath, mirrorURL)
lib.Install(tgversion, custBinPath, installPath, mirrorURL)
}
fmt.Println(err)
fmt.Println("No version found to match constraint. Follow the README.md instructions for setup. https://github.com/warrensbox/tgswitch/blob/master/README.md")
os.Exit(1)
}

// Install using version constraint from terragrunt file
func installTGHclFile(tgFile *string, custBinPath, mirrorURL string) {
func installTGHclFile(tgFile *string, custBinPath, installPath, mirrorURL string) {
fmt.Printf("Terragrunt file found: %s\n", *tgFile)
parser := hclparse.NewParser()
file, diags := parser.ParseHCLFile(*tgFile) //use hcl parser to parse HCL file
Expand All @@ -296,7 +302,7 @@ func installTGHclFile(tgFile *string, custBinPath, mirrorURL string) {
}
var version terragruntVersionConstraints
gohcl.DecodeBody(file.Body, nil, &version)
installFromConstraint(&version.TerragruntVersionConstraint, custBinPath, mirrorURL)
installFromConstraint(&version.TerragruntVersionConstraint, custBinPath, installPath, mirrorURL)
}

// check if version is defined in hcl file /* lazy-emergency fix - will improve later */
Expand All @@ -312,7 +318,7 @@ func checkVersionDefinedHCL(tgFile *string) bool {
}

// Install using version constraint from terragrunt file
// func installTGHclFile(tgFile *string, custBinPath *string) {
// func installTGHclFile(tgFile *string, custBinPath *string, installPath *string) {
// fmt.Printf("Terragrunt file found: %s\n", *tgFile)
// parser := hclparse.NewParser()
// file, diags := parser.ParseHCLFile(*tgFile) //use hcl parser to parse HCL file
Expand All @@ -323,7 +329,7 @@ func checkVersionDefinedHCL(tgFile *string) bool {
// var version terragruntVersionConstraints
// gohcl.DecodeBody(file.Body, nil, &version)
// //TODO figure out sermver
// //installFromConstraint(&version.TerragruntVersionConstraint, custBinPath)
// //installFromConstraint(&version.TerragruntVersionConstraint, custBinPath, installPath)
// }

type terragruntVersionConstraints struct {
Expand Down