Skip to content

Commit fc0a718

Browse files
authored
Merge pull request #339 from tfutils/auto_use_required_version
Auto use required version
2 parents 371b76c + ec64cf3 commit fc0a718

24 files changed

Lines changed: 337 additions & 80 deletions

.github/workflows/test.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ jobs:
1818
strategy:
1919
matrix:
2020
os:
21-
- 'macos-11'
2221
- 'macos-latest'
2322
- 'ubuntu-latest'
2423
steps:
2524
- uses: 'actions/checkout@v2'
25+
with:
26+
fetch-depth: 1
27+
- name: 'Install Dependencies'
28+
run: './test/install_deps.sh'
2629
- name: 'Run all tests'
2730
run: './test/run.sh'
2831
shell: 'bash'
@@ -41,6 +44,10 @@ jobs:
4144
- 'windows-2019'
4245
steps:
4346
- uses: 'actions/checkout@v2'
47+
with:
48+
fetch-depth: 1
49+
- name: 'Install Dependencies'
50+
run: './test/install_deps.sh'
4451
- name: 'Run all tests'
4552
run: './test/run.sh'
4653
shell: 'bash'

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ $ which tfenv
8181

8282
Install a specific version of Terraform.
8383

84-
If no parameter is passed, the version to use is resolved automatically via [.terraform-version files](#terraform-version-file) or [TFENV\_TERRAFORM\_VERSION environment variable](#tfenv_terraform_version) (TFENV\_TERRAFORM\_VERSION takes precedence), defaulting to 'latest' if none are found.
84+
If no parameter is passed, the version to use is resolved automatically via [TFENV\_TERRAFORM\_VERSION environment variable](#tfenv_terraform_version), [.terraform-version files](#terraform-version-file), or [required_version in "terraform" section of any .tf or .tf.json file](#min-required), in that order of precedence, i.e. TFENV\_TERRAFORM\_VERSION, then .terraform-version, and then required_version in .tf. The default is 'latest' if none are found.
8585

8686
If a parameter is passed, available options:
8787

bin/terraform

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if [ -z "${TFENV_ROOT:-""}" ]; then
2626
};
2727
TFENV_SHIM=$(readlink_f "${0}")
2828
TFENV_ROOT="${TFENV_SHIM%/*/*}";
29-
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT"
29+
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT";
3030
else
3131
TFENV_ROOT="${TFENV_ROOT%/}";
3232
fi;
@@ -47,7 +47,7 @@ fi;
4747
for dir in libexec bin; do
4848
case ":${PATH}:" in
4949
*:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";;
50-
*)
50+
*)
5151
log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now";
5252
export PATH="${TFENV_ROOT}/${dir}:${PATH}";
5353
;;
@@ -62,4 +62,4 @@ log 'debug' "program=\"${0##*/}\"";
6262

6363
declare tfenv_path="${TFENV_ROOT}/bin/tfenv";
6464

65-
tfenv-exec "$@"
65+
tfenv-exec "$@";

bin/tfenv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if [ -z "${TFENV_ROOT:-""}" ]; then
2626
};
2727
TFENV_SHIM=$(readlink_f "${0}")
2828
TFENV_ROOT="${TFENV_SHIM%/*/*}";
29-
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT"
29+
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT";
3030

3131
else
3232
TFENV_ROOT="${TFENV_ROOT%/}";
@@ -48,7 +48,7 @@ fi;
4848
for dir in libexec bin; do
4949
case ":${PATH}:" in
5050
*:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";;
51-
*)
51+
*)
5252
log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now";
5353
export PATH="${TFENV_ROOT}/${dir}:${PATH}";
5454
;;
@@ -62,7 +62,7 @@ done;
6262
declare arg="${1:-""}";
6363

6464
log 'debug' "Setting TFENV_DIR to ${PWD}";
65-
export TFENV_DIR="${PWD}"
65+
export TFENV_DIR="${PWD}";
6666

6767
abort() {
6868
log 'debug' 'Aborting...';

lib/bashlog.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ function log() {
6565
local severities_ALERT=1; # Unused
6666
local severities_EMERG=0; # Unused
6767

68-
local severity_var="severities_${upper}"
69-
local severity="${!severity_var:-3}"
68+
local severity_var="severities_${upper}";
69+
local severity="${!severity_var:-3}";
7070

7171
if [ "${debug_level}" -gt 0 ] || [ "${severity}" -lt 7 ]; then
7272

@@ -111,7 +111,7 @@ function log() {
111111
local colours_DEFAULT='\033[0m' # Default
112112

113113
local norm="${colours_DEFAULT}";
114-
local colour_var="colours_${upper}"
114+
local colour_var="colours_${upper}";
115115
local colour="${!colour_var:-\033[31m}";
116116

117117
local std_line;

lib/helpers.sh

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ if [ -z "${TFENV_ROOT:-""}" ]; then
1818
};
1919
TFENV_SHIM=$(readlink_f "${0}")
2020
TFENV_ROOT="${TFENV_SHIM%/*/*}";
21-
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT"
21+
[ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT";
2222
else
2323
TFENV_ROOT="${TFENV_ROOT%/}";
2424
fi;
@@ -44,23 +44,24 @@ fi;
4444

4545
function load_bashlog () {
4646
source "${TFENV_ROOT}/lib/bashlog.sh";
47-
}
47+
};
4848
export -f load_bashlog;
49+
4950
if [ "${TFENV_DEBUG:-0}" -gt 0 ] ; then
5051
# our shim below cannot be used when debugging is enabled
51-
load_bashlog
52+
load_bashlog;
5253
else
5354
# Shim that understands to no-op for debug messages, and defers to
5455
# full bashlog for everything else.
5556
function log () {
5657
if [ "$1" != 'debug' ] ; then
5758
# Loading full bashlog will overwrite the `log` function
58-
load_bashlog
59-
log "$@"
60-
fi
61-
}
59+
load_bashlog;
60+
log "$@";
61+
fi;
62+
};
6263
export -f log;
63-
fi
64+
fi;
6465

6566
resolve_version () {
6667
declare version_requested version regex min_required version_file;
@@ -75,19 +76,41 @@ resolve_version () {
7576
log 'debug' "Version File (${version_file}) is not the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
7677
version_requested="$(cat "${version_file}")" \
7778
|| log 'error' "Failed to open ${version_file}";
79+
fi
7880

79-
elif [ -f "${version_file}" ]; then
81+
if [ -z "${version_requested:-""}" ]; then
82+
log 'debug' 'Trying to set version from "required_version" under "terraform" section';
83+
versions="$( echo $(cat {*.tf,*.tf.json} 2>/dev/null | grep -h required_version) | grep -o '\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";
84+
if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
85+
found_min_required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}";
86+
if [[ "${found_min_required}" =~ ^!=.+ ]]; then
87+
log 'debug' "required_version is a negation - we cannot guess the desired one, skipping.";
88+
else
89+
found_min_required="$(echo "$found_min_required")";
90+
91+
# Probably not an advisable way to choose a terraform version,
92+
# but this is the way this functionality works in terraform:
93+
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
94+
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
95+
found_min_required="${found_min_required}.0";
96+
done;
97+
version_requested="${found_min_required}";
98+
fi;
99+
fi;
100+
fi;
101+
102+
if [ -z "${version_requested}" -a -f "${version_file}" ]; then
80103
log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
81104
version_requested="$(cat "${version_file}")" \
82105
|| log 'error' "Failed to open ${version_file}";
83106

84-
# Absolute fallback
85107
if [ -z "${version_requested}" ]; then
86108
log 'debug' 'Version file had no content. Falling back to "latest"';
87109
version_requested='latest';
88110
fi;
89111

90-
else
112+
# Absolute fallback
113+
elif [ -z "${version_requested}" ]; then
91114
log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version) but it doesn't exist";
92115
log 'info' 'No version requested on the command line or in the version file search path. Installing "latest"';
93116
version_requested='latest';
@@ -123,7 +146,7 @@ resolve_version () {
123146
regex="^${version_requested}$";
124147
log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}";
125148
fi;
126-
}
149+
};
127150

128151
# Curl wrapper to switch TLS option for each OS
129152
function curlw () {
@@ -141,13 +164,13 @@ function curlw () {
141164
fi;
142165

143166
curl ${TLS_OPT} ${NETRC_OPT} "$@";
144-
}
167+
};
145168
export -f curlw;
146169

147170
check_active_version() {
148171
local v="${1}";
149172
[ -n "$(${TFENV_ROOT}/bin/terraform version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ];
150-
}
173+
};
151174
export -f check_active_version;
152175

153176
check_installed_version() {
@@ -173,6 +196,8 @@ cleanup() {
173196
rm -rf ./.terraform-version;
174197
log 'debug' "Deleting ${pwd}/min_required.tf";
175198
rm -rf ./min_required.tf;
199+
log 'debug' "Deleting ${pwd}/required_version.tf";
200+
rm -rf ./required_version.tf;
176201
};
177202
export -f cleanup;
178203

@@ -182,6 +207,18 @@ function error_and_proceed() {
182207
};
183208
export -f error_and_proceed;
184209

210+
function check_dependencies() {
211+
if [[ $(uname) == 'Darwin' ]] && [ $(which brew) ]; then
212+
if ! [ $(which ggrep) ]; then
213+
log 'error' 'A metaphysical dichotomy has caused this unit to overload and shut down. GNU Grep is a requirement and your Mac does not have it. Consider "brew install grep"';
214+
fi;
215+
216+
shopt -s expand_aliases;
217+
alias grep=ggrep;
218+
fi;
219+
};
220+
export -f check_dependencies;
221+
185222
source "$TFENV_ROOT/lib/tfenv-exec.sh";
186223
source "$TFENV_ROOT/lib/tfenv-version-file.sh";
187224
source "$TFENV_ROOT/lib/tfenv-version-name.sh";

lib/tfenv-exec.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ function tfenv-exec() {
3333
|| log 'error' "Failed to execute: ${TF_BIN_PATH} $*";
3434

3535
return 0;
36-
}
36+
};
3737
export -f tfenv-exec;

lib/tfenv-version-file.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ find_local_version_file() {
1414
done;
1515
log 'debug' "No version file found in ${1}";
1616
return 1;
17-
}
17+
};
1818
export -f find_local_version_file;
1919

2020
function tfenv-version-file() {
@@ -24,5 +24,5 @@ function tfenv-version-file() {
2424
echo "${TFENV_CONFIG_DIR}/version";
2525
fi;
2626
fi;
27-
}
27+
};
2828
export -f tfenv-version-file;

lib/tfenv-version-name.sh

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,36 @@ function tfenv-version-name() {
44
&& log 'debug' "TFENV_VERSION_FILE retrieved from tfenv-version-file: ${TFENV_VERSION_FILE}" \
55
|| log 'error' 'Failed to retrieve TFENV_VERSION_FILE from tfenv-version-file';
66

7-
TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \
8-
&& log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}";
7+
if [ "${TFENV_VERSION_FILE}" = "${TFENV_CONFIG_DIR}/version" ]; then
8+
log 'debug' 'Tryng to set version from "required_version" under "terraform" section';
99

10-
TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}";
10+
versions="$( echo $(cat {*.tf,*.tf.json} 2>/dev/null | grep -h required_version) | grep -o '\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";
11+
if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
12+
found_min_required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}";
13+
if [[ "${found_min_required}" =~ ^!=.+ ]]; then
14+
log 'debug' "required_version is a negation - we cannot guess the desired one, skipping.";
15+
else
16+
found_min_required="$(echo "$found_min_required")";
17+
18+
# Probably not an advisable way to choose a terraform version,
19+
# but this is the way this functionality works in terraform:
20+
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
21+
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
22+
found_min_required="${found_min_required}.0";
23+
done;
24+
TFENV_VERSION="${found_min_required}";
25+
fi;
26+
fi;
27+
28+
TFENV_VERSION_SOURCE='terraform{required_version}';
29+
fi;
30+
31+
if [[ -z "${TFENV_VERSION:-""}" ]]; then
32+
TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \
33+
&& log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}";
34+
35+
TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}";
36+
fi;
1137
else
1238
TFENV_VERSION="${TFENV_TERRAFORM_VERSION}" \
1339
&& log 'debug' "TFENV_VERSION specified in TFENV_TERRAFORM_VERSION: ${TFENV_VERSION}";
@@ -63,7 +89,7 @@ function tfenv-version-name() {
6389

6490
# Accept a v-prefixed version, but strip the v.
6591
if [[ "${TFENV_VERSION}" =~ ^v.*$ ]]; then
66-
log 'debug' "Version Requested is prefixed with a v. Stripping the v."
92+
log 'debug' "Version Requested is prefixed with a v. Stripping the v.";
6793
TFENV_VERSION="${TFENV_VERSION#v*}";
6894
fi;
6995
fi;
@@ -77,5 +103,6 @@ function tfenv-version-name() {
77103
fi;
78104

79105
echo "${TFENV_VERSION}";
80-
}
106+
};
81107
export -f tfenv-version-name;
108+

libexec/tfenv---version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fi;
5757
for dir in libexec bin; do
5858
case ":${PATH}:" in
5959
*:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";;
60-
*)
60+
*)
6161
log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now";
6262
export PATH="${TFENV_ROOT}/${dir}:${PATH}";
6363
;;

0 commit comments

Comments
 (0)