Skip to content

Commit 05e10bc

Browse files
committed
feat: Add env vars for install and bin args
Resolves #562 * Add Env vars for custom binary path and custom install path * Add TOML config key for custom install path for consistency * Improve wording in resolved parameter values debugging output * Add tests for new Env vars and TOML config key * Documentation * Update https://tfswitch.warrensbox.com/usage/commandline/ accordingly * Update https://tfswitch.warrensbox.com/usage/config-files/ accordingly * Add `arch` and `log-level` TOML keys description to https://tfswitch.warrensbox.com/usage/config-files/ * Improve wording in some places
1 parent 9297c25 commit 05e10bc

File tree

9 files changed

+186
-44
lines changed

9 files changed

+186
-44
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ GOARCH ?= $(shell $(GOBINARY) env GOARCH)
1111

1212
$(EXE): version go.mod *.go lib/*.go
1313
mkdir -p "$(BUILDPATH)/"
14-
$(GOBINARY) build -v -ldflags "-X main.version=$(VER)" -o "$(BUILDPATH)/$@" $(PKG)
14+
$(GOBINARY) build -v -ldflags "-X 'main.version=$(VER)'" -o "$(BUILDPATH)/$@" $(PKG)
1515

1616
.PHONY: release
1717
release: $(EXE) darwin linux windows

lib/param_parsing/environment.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,29 @@ package param_parsing
33
import "os"
44

55
func GetParamsFromEnvironment(params Params) Params {
6-
if envArch := os.Getenv("TF_ARCH"); envArch != "" {
7-
params.Arch = envArch
8-
logger.Debugf("Using architecture from environment variable \"TF_ARCH\": %q", envArch)
6+
if envVar := os.Getenv("TF_ARCH"); envVar != "" {
7+
params.Arch = envVar
8+
logger.Debugf("Using architecture from environment variable \"TF_ARCH\": %q", envVar)
99
}
10-
if envVersion := os.Getenv("TF_VERSION"); envVersion != "" {
11-
params.Version = envVersion
12-
logger.Debugf("Using version from environment variable \"TF_VERSION\": %q", envVersion)
10+
if envVar := os.Getenv("TF_VERSION"); envVar != "" {
11+
params.Version = envVar
12+
logger.Debugf("Using version from environment variable \"TF_VERSION\": %q", envVar)
1313
}
14-
if envDefaultVersion := os.Getenv("TF_DEFAULT_VERSION"); envDefaultVersion != "" {
15-
params.DefaultVersion = envDefaultVersion
16-
logger.Debugf("Using default-version from environment variable \"TF_DEFAULT_VERSION\": %q", envDefaultVersion)
14+
if envVar := os.Getenv("TF_DEFAULT_VERSION"); envVar != "" {
15+
params.DefaultVersion = envVar
16+
logger.Debugf("Using default version from environment variable \"TF_DEFAULT_VERSION\": %q", envVar)
1717
}
18-
if envProduct := os.Getenv("TF_PRODUCT"); envProduct != "" {
19-
params.Product = envProduct
20-
logger.Debugf("Using product from environment variable \"TF_PRODUCT\": %q", envProduct)
18+
if envVar := os.Getenv("TF_PRODUCT"); envVar != "" {
19+
params.Product = envVar
20+
logger.Debugf("Using product from environment variable \"TF_PRODUCT\": %q", envVar)
21+
}
22+
if envVar := os.Getenv("TF_BINARY_PATH"); envVar != "" {
23+
params.CustomBinaryPath = envVar
24+
logger.Debugf("Using custom binary path from environment variable \"TF_BINARY_PATH\": %q", envVar)
25+
}
26+
if envVar := os.Getenv("TF_INSTALL_PATH"); envVar != "" {
27+
params.InstallPath = envVar
28+
logger.Debugf("Using custom install path from environment variable \"TF_INSTALL_PATH\": %q", envVar)
2129
}
2230
return params
2331
}

lib/param_parsing/environment_test.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,32 @@ func TestGetParamsFromEnvironment_product_from_env(t *testing.T) {
5454
params = GetParamsFromEnvironment(params)
5555
_ = os.Unsetenv("TF_PRODUCT")
5656
if params.Product != expected {
57-
t.Error("Determined version is not matching. Got " + params.Product + ", expected " + expected)
57+
t.Error("Determined product is not matching. Got " + params.Product + ", expected " + expected)
58+
}
59+
}
60+
61+
func TestGetParamsFromEnvironment_bin_from_env(t *testing.T) {
62+
logger = lib.InitLogger("DEBUG")
63+
var params Params
64+
expected := "custom_binary_path_from_env"
65+
_ = os.Setenv("TF_BINARY_PATH", expected)
66+
params = initParams(params)
67+
params = GetParamsFromEnvironment(params)
68+
_ = os.Unsetenv("TF_BINARY_PATH")
69+
if params.CustomBinaryPath != expected {
70+
t.Errorf("Determined custom binary path is not matching. Got %q, expected %q", params.CustomBinaryPath, expected)
71+
}
72+
}
73+
74+
func TestGetParamsFromEnvironment_install_from_env(t *testing.T) {
75+
logger = lib.InitLogger("DEBUG")
76+
var params Params
77+
expected := "/custom_install_path_from_env"
78+
_ = os.Setenv("TF_INSTALL_PATH", expected)
79+
params = initParams(params)
80+
params = GetParamsFromEnvironment(params)
81+
_ = os.Unsetenv("TF_INSTALL_PATH")
82+
if params.InstallPath != expected {
83+
t.Errorf("Determined custom install path is not matching. Got %q, expected %q", params.InstallPath, expected)
5884
}
5985
}

lib/param_parsing/parameters.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,23 @@ func populateParams(params Params) Params {
163163
}
164164

165165
if isShortRun {
166-
logger.Debugf("Resolved CPU architecture: %q", params.Arch)
167166
if params.DryRun {
168167
logger.Info("[DRY-RUN] No changes will be made")
169168
} else {
170169
logger.Debugf("Resolved dry-run: %t", params.DryRun)
171170
}
171+
172+
logger.Debugf("Resolved CPU architecture: %q", params.Arch)
172173
if params.DefaultVersion != "" {
173174
logger.Debugf("Resolved fallback version: %q", params.DefaultVersion)
174175
}
175-
logger.Debugf("Resolved installation path: %q", filepath.Join(params.InstallPath, lib.InstallDir))
176-
logger.Debugf("Resolved installation target: %q", params.CustomBinaryPath)
177-
logger.Debugf("Resolved installation version: %q", params.Version)
176+
logger.Debugf("Resolved binary path: %q", params.CustomBinaryPath)
177+
logger.Debugf("Resolved install path: %q", filepath.Join(params.InstallPath, lib.InstallDir))
178+
logger.Debugf("Resolved install version: %q", params.Version)
178179
logger.Debugf("Resolved log level: %q", params.LogLevel)
179180
logger.Debugf("Resolved mirror URL: %q", params.MirrorURL)
180181
logger.Debugf("Resolved product name: %q", params.Product)
181-
logger.Debugf("Resolved target directory: %q", params.ChDirPath)
182+
logger.Debugf("Resolved working directory: %q", params.ChDirPath)
182183
}
183184

184185
return params

lib/param_parsing/toml.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ func getParamsTOML(params Params) (Params, error) {
3333
}
3434
if configKey := "bin"; viperParser.Get(configKey) != nil {
3535
params.CustomBinaryPath = os.ExpandEnv(viperParser.GetString(configKey))
36-
logger.Debugf("Installation target (%q) from %q: %q", configKey, tomlPath, params.CustomBinaryPath)
36+
logger.Debugf("Custom binary path (%q) from %q: %q", configKey, tomlPath, params.CustomBinaryPath)
37+
}
38+
if configKey := "install"; viperParser.Get(configKey) != nil {
39+
params.InstallPath = viperParser.GetString(configKey)
40+
logger.Debugf("Custom install path (%q) from %q: %q", configKey, tomlPath, params.InstallPath)
3741
}
3842
if configKey := "log-level"; viperParser.Get(configKey) != nil {
3943
params.LogLevel = viperParser.GetString(configKey)

lib/param_parsing/toml_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ func TestGetParamsTOML_BinaryPath(t *testing.T) {
2525
}
2626
}
2727

28+
func TestGetParamsTOML_InstallPath(t *testing.T) {
29+
expected := "/custom_install_path_from_toml"
30+
params := prepare()
31+
params, err := getParamsTOML(params)
32+
if err != nil {
33+
t.Fatalf("Got error %v", err)
34+
}
35+
if params.InstallPath != expected {
36+
t.Errorf("InstallPath not matching. Got %q, expected %q", params.CustomBinaryPath, expected)
37+
}
38+
}
39+
2840
func TestGetParamsTOML_Version(t *testing.T) {
2941
expected := "1.6.2"
3042
params := prepare()

test-data/integration-tests/test_tfswitchtoml/.tfswitch.toml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
arch = "amd64"
22
bin = "/usr/local/bin/terraform_from_toml"
3+
install = "/custom_install_path_from_toml"
34
default-version = "1.5.4"
45
log-level = "NOTICE"
56
product = "opentofu"

www/docs/usage/commandline.md

+49-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ You can also set environment variables for tfswitch to override some configurati
3232
For example:
3333

3434
```bash
35-
export TF_VERSION=0.14.4
35+
export TF_VERSION="0.14.4"
3636
tfswitch # Will automatically switch to terraform version 0.14.4
3737
```
3838

@@ -43,7 +43,7 @@ tfswitch # Will automatically switch to terraform version 0.14.4
4343
For example:
4444

4545
```bash
46-
export TF_DEFAULT_VERSION=0.14.4
46+
export TF_DEFAULT_VERSION="0.14.4"
4747
tfswitch # Will automatically switch to terraform version 0.14.4
4848
```
4949

@@ -59,13 +59,32 @@ This can either be set to:
5959
For example:
6060

6161
```bash
62-
export TF_PRODUCT=opentofu
62+
export TF_PRODUCT="opentofu"
6363
tfswitch # Will install opentofu instead of terraform
6464
```
6565

66+
### `TF_LOG_LEVEL`
67+
68+
`TF_LOG_LEVEL` environment variable can be set to override default log level.
69+
70+
- Supported log levels:
71+
- `INFO`: includes `PANIC`, `FATAL`, `ERROR`
72+
- `ERROR`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `INFO`
73+
- `NOTICE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`
74+
- `DEBUG`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`
75+
- `TRACE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`, `TRACE`
76+
- Any other log level value falls under default logging level
77+
78+
For example:
79+
80+
```bash
81+
export TF_LOG_LEVEL="DEBUG"
82+
tfswitch # Will output debug logs
83+
```
84+
6685
### `TF_ARCH`
6786

68-
`TF_ARCH` environment variable can be set to override default CPU architecture for downloaded Terraform binary.
87+
`TF_ARCH` environment variable can be set to override default CPU architecture of downloaded binaries.
6988

7089
- This can be set to any string, though incorrect values will result in download failure.
7190
- Suggested values: `amd64`, `arm64`, `386`.
@@ -76,8 +95,32 @@ tfswitch # Will install opentofu instead of terraform
7695
For example:
7796

7897
```bash
79-
export TF_ARCH=amd64
80-
tfswitch # Will install Terraform binary for amd64 architecture
98+
export TF_ARCH="amd64"
99+
tfswitch # Will install binary for amd64 architecture
100+
```
101+
102+
### `TF_BINARY_PATH`
103+
104+
`tfswitch` defaults to install to the `/usr/local/bin/` directory (and falls back to `$HOME/bin/` otherwise). The target filename is resolved automatically based on the `product` parameter.
105+
`TF_BINARY_PATH` environment variable can be set to specify a **full installation path** (directory + file name). If target directory does not exist, `tfswitch` falls back to `$HOME/bin/` directory.
106+
107+
For example:
108+
109+
```bash
110+
export TF_BINARY_PATH="$HOME/bin/terraform" # Path to the file
111+
tfswitch # Will install binary as $HOME/bin/terraform
112+
```
113+
114+
### `TF_INSTALL_PATH`
115+
116+
`tfswitch` defaults to download binaries to the `$HOME/.terraform.versions/` directory.
117+
`TF_INSTALL_PATH`environment variable can be set to specify a parent directory for `.terraform.versions` directory. Current user must have write permissions to the target directory. If the target directory does not exist, `tfswitch` will create it.
118+
119+
For example:
120+
121+
```bash
122+
export TF_INSTALL_PATH="/tmp" # Path to the directory where `.terraform.versions` directory resides
123+
tfswitch # Will download actual binary to /tmp/.terraform.versions/
81124
```
82125

83126
## Install latest version only

www/docs/usage/config-files.md

+65-18
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ tfswitch -c terraform_dir
99

1010
## Use `version.tf` file
1111

12-
If a .tf file with the terraform constraints is included in the current directory, it should automatically download or switch to that terraform version.
13-
For example, the following should automatically switch terraform to the lastest version:
12+
If a `.tf` file with the version constraints is included in the current directory, `tfswitch` should automatically download or switch to that terraform version.
13+
Also please refer to [Order of Terraform version definition precedence](general.md) page for more information on how `tfswitch` determines the version to use.
14+
For example, the following should automatically switch to the latest available version newer than `0.12.8`:
1415

1516
```hcl
1617
terraform {
@@ -30,21 +31,23 @@ terraform {
3031
![tfswitchrc](../static/tfswitch-v6.gif)
3132

3233
1. Create a `.tfswitchrc` file containing the desired version
33-
2. For example, `echo "0.10.5" >> .tfswitchrc` for version 0.10.5 of terraform
34-
3. Run the command `tfswitch` in the same directory as your `.tfswitchrc`
34+
2. For example, `echo "0.10.5" >> .tfswitchrc` for version `0.10.5`
35+
3. Run the command `tfswitch` in the same directory as this `.tfswitchrc` file
3536

3637
*Instead of a `.tfswitchrc` file, a `.terraform-version` file may be used for compatibility with [`tfenv`](https://github.com/tfutils/tfenv#terraform-version-file) and other tools which use it*
3738

3839
## Use `.tfswitch.toml` file (For non-admin users with limited privilege on their computers)
3940

40-
This is similiar to using a `.tfswitchrc` file, but you can specify a custom binary path for your terraform installation
41+
`tfswitch` defaults to install to the `/usr/local/bin/` directory (and falls back to `$HOME/bin/` otherwise). The target filename is resolved automatically based on the `product` attribute ([see below](#setting-product-using-tfswitchtoml-file)). If you do not have write access to `/usr/local/bin/` directory, you can use the `.tfswitch.toml` file to specify a **full installation path** (directory + file name).
42+
This is similar to using a `.tfswitchrc` file, but you specify a custom binary path for the installation:
4143

4244
![toml1](../static/tfswitch-v7.gif)
4345
![toml2](../static/tfswitch-v8.gif)
4446

45-
1. Create a custom binary path. Ex: `mkdir $HOME/bin`
46-
2. Add the path to your PATH. Ex: `export PATH=$PATH:$HOME/bin` (add this to your Bash profile or Zsh profile)
47-
3. Pass `-b` or `--bin` parameter with your custom path to install Terraform. Ex: `tfswitch -b $HOME/bin/terraform 0.10.8`
47+
1. Create a directory for the custom binary path. Ex: `mkdir -p "$HOME/bin/"`
48+
2. Add the path to the directory to your `PATH` environment variable. Ex: `export PATH="$PATH:$HOME/bin"` (add this to your Bash profile or Zsh profile)
49+
3. Pass `-b` or `--bin` parameter with the custom binary path as value (this must be a first level pointer inside the directory from above). Ex: `tfswitch -b "$HOME/bin/terraform" 0.10.8`
50+
- If target directory for custom binary path does not exist, `tfswitch` falls back to `$HOME/bin/` directory
4851
4. Optionally, you can create a `.tfswitch.toml` file in your home directory (`~/.tfswitch.toml`)
4952
5. Your `~/.tfswitch.toml` file should look like this:
5053

@@ -53,28 +56,27 @@ bin = "$HOME/bin/terraform"
5356
version = "0.11.3"
5457
```
5558

56-
6. Run `tfswitch` and it should automatically install the required terraform version in the specified binary path
59+
6. Run `tfswitch` and it should automatically install the required version in the specified binary path
5760

58-
**NOTE**
59-
60-
1. For Linux users that do not have write permission to `/usr/local/bin/`, `tfswitch` will attempt to install terraform at `$HOME/bin`. Run `export PATH=$PATH:$HOME/bin` to append bin to PATH
61-
2. For Windows host, `tfswitch` need to be run under `Administrator` mode, and `$HOME/.tfswitch.toml` with `bin` must be defined (with a valid path) as minimum, below is an example for `$HOME/.tfswitch.toml` on windows
61+
Below is an example for `$HOME/.tfswitch.toml` on Windows:
6262

6363
```toml
6464
bin = "C:\\Users\\<%USRNAME%>\\bin\\terraform.exe"
6565
```
6666

67-
## Setting the default version using `.tfswitch.toml` file
67+
## Setting the default (fallback) version using `.tfswitch.toml` file
6868

69-
The `.tfswitch.toml` file can be configured with a `default-version` attribute to configure tfswitch a particular version, if no other sources of versions are found
69+
If `tfswsitch` is unable to determine the version to use, it errors out.
70+
The `.tfswitch.toml` file can be configured with a `default-version` attribute for `tfswitch` to use a particular version, if no other sources of versions are found
7071

7172
```toml
7273
default-version = "1.5.4"
7374
```
7475

7576
## Setting product using `.tfswitch.toml` file
7677

77-
The `.tfswitch.toml` file can be configured with a `product` attribute to configure tfswitch to use Terraform or OpenTofu, by default:
78+
`tfswitch` defaults to install Terraform binaries.
79+
The `.tfswitch.toml` file can be configured with a `product` attribute for `tfswitch` to use either Terraform or OpenTofu by default:
7880

7981
```toml
8082
product = "opentofu"
@@ -86,10 +88,55 @@ or
8688
product = "terraform"
8789
```
8890

91+
## Setting log level using `.tfswitch.toml` file
92+
93+
`tfswitch` defaults to `INFO` log level.
94+
The `.tfswitch.toml` file can be configured with a `log-level` attribute for `tfswitch` to use non-default logging verbosity:
95+
96+
```toml
97+
log-level = "INFO"
98+
```
99+
100+
- Supported log levels:
101+
- `INFO`: includes `PANIC`, `FATAL`, `ERROR`
102+
- `ERROR`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `INFO`
103+
- `NOTICE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`
104+
- `DEBUG`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`
105+
- `TRACE`: includes `PANIC`, `FATAL`, `ERROR`, `WARN`, `NOTICE`, `INFO`, `DEBUG`, `TRACE`
106+
- Any other log level value falls under default logging level
107+
108+
## Overriding CPU architecture type for the downloaded binary using `.tfswitch.toml` file
109+
110+
CPU architecture of the downloaded binaries defaults to `tfswitch`'s host architecture.
111+
The `.tfswitch.toml` file can be configured with a `arch` attribute for `tfswitch` to download binary of non-default architecture type:
112+
113+
```toml
114+
arch = "arm64"
115+
```
116+
117+
- This can be set to any string, though incorrect values will result in download failure.
118+
- Suggested values: `amd64`, `arm64`, `386`.
119+
- Check available Arch types at:
120+
- [Terraform Downloads](https://releases.hashicorp.com/terraform/)
121+
- [OpenTofu Downloads](https://get.opentofu.org/tofu/)
122+
123+
## Overriding installation directory, where actual binaries are stored, using `.tfswitch.toml` file
124+
125+
`tfswitch` defaults to download binaries to the `$HOME/.terraform.versions/` directory.
126+
The `.tfswitch.toml` file can be configured with a `install` attribute to specify a parent directory for `.terraform.versions` directory.
127+
128+
```toml
129+
install = "/tmp"
130+
```
131+
132+
**NOTE**:
133+
- Current user must have write permissions to the target directory
134+
- If the target directory does not exist, `tfswitch` will create it
135+
89136
## Use `terragrunt.hcl` file
90137

91-
If a terragrunt.hcl file with the terraform constraint is included in the current directory, it should automatically download or switch to that terraform version.
92-
For example, the following should automatically switch terraform to the lastest version 0.13:
138+
If a terragrunt.hcl file with the terraform constraint is included in the current directory, it should automatically download or switch to that terraform version.
139+
For example, the following should automatically switch Terraform to the latest version 0.13:
93140

94141
```hcl
95142
terragrunt_version_constraint = ">= 0.26, < 0.27"

0 commit comments

Comments
 (0)