Skip to content

Commit

Permalink
Fix the problems introduced with minrequired changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Zordrak committed Jul 8, 2022
1 parent fc0a718 commit 0a3c2ca
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 331 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ $ which tfenv

Install a specific version of Terraform.

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.
If no parameter is passed, the version to use is resolved automatically via [TFENV\_TERRAFORM\_VERSION environment variable](#tfenv_terraform_version) or [.terraform-version files](#terraform-version-file), in that order of precedence, i.e. TFENV\_TERRAFORM\_VERSION, then .terraform-version. The default is 'latest' if none are found.

If a parameter is passed, available options:

Expand Down
96 changes: 5 additions & 91 deletions lib/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,91 +63,6 @@ else
export -f log;
fi;

resolve_version () {
declare version_requested version regex min_required version_file;

declare arg="${1:-""}";

if [ -z "${arg}" -a -z "${TFENV_TERRAFORM_VERSION:-""}" ]; then
version_file="$(tfenv-version-file)";
log 'debug' "Version File: ${version_file}";

if [ "${version_file}" != "${TFENV_CONFIG_DIR}/version" ]; then
log 'debug' "Version File (${version_file}) is not the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
version_requested="$(cat "${version_file}")" \
|| log 'error' "Failed to open ${version_file}";
fi

if [ -z "${version_requested:-""}" ]; then
log 'debug' 'Trying to set version from "required_version" under "terraform" section';
versions="$( echo $(cat {*.tf,*.tf.json} 2>/dev/null | grep -h required_version) | grep -o '\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";
if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
found_min_required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}";
if [[ "${found_min_required}" =~ ^!=.+ ]]; then
log 'debug' "required_version is a negation - we cannot guess the desired one, skipping.";
else
found_min_required="$(echo "$found_min_required")";

# Probably not an advisable way to choose a terraform version,
# but this is the way this functionality works in terraform:
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
found_min_required="${found_min_required}.0";
done;
version_requested="${found_min_required}";
fi;
fi;
fi;

if [ -z "${version_requested}" -a -f "${version_file}" ]; then
log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
version_requested="$(cat "${version_file}")" \
|| log 'error' "Failed to open ${version_file}";

if [ -z "${version_requested}" ]; then
log 'debug' 'Version file had no content. Falling back to "latest"';
version_requested='latest';
fi;

# Absolute fallback
elif [ -z "${version_requested}" ]; then
log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version) but it doesn't exist";
log 'info' 'No version requested on the command line or in the version file search path. Installing "latest"';
version_requested='latest';
fi;
elif [ -n "${TFENV_TERRAFORM_VERSION:-""}" ]; then
version_requested="${TFENV_TERRAFORM_VERSION}";
log 'debug' "TFENV_TERRAFORM_VERSION is set: ${TFENV_TERRAFORM_VERSION}";
else
version_requested="${arg}";
fi;

log 'debug' "Version Requested: ${version_requested}";

if [[ "${version_requested}" =~ ^min-required$ ]]; then
log 'info' 'Detecting minimum required version...';
min_required="$(tfenv-min-required)" \
|| log 'error' 'tfenv-min-required failed';

log 'info' "Minimum required version detected: ${min_required}";
version_requested="${min_required}";
fi;

if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
version="${version_requested%%\:*}";
regex="${version_requested##*\:}";
log 'debug' "Version uses latest keyword with regex: ${regex}";
elif [[ "${version_requested}" =~ ^latest$ ]]; then
version="${version_requested}";
regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$";
log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}";
else
version="${version_requested}";
regex="^${version_requested}$";
log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}";
fi;
};

# Curl wrapper to switch TLS option for each OS
function curlw () {
local TLS_OPT="--tlsv1.2";
Expand All @@ -167,27 +82,27 @@ function curlw () {
};
export -f curlw;

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

check_installed_version() {
function check_installed_version() {
local v="${1}";
local bin="${TFENV_CONFIG_DIR}/versions/${v}/terraform";
[ -n "$(${bin} version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ];
};
export -f check_installed_version;

check_default_version() {
function check_default_version() {
local v="${1}";
local def="$(cat "${TFENV_CONFIG_DIR}/version")";
[ "${def}" == "${v}" ];
};
export -f check_default_version;

cleanup() {
function cleanup() {
log 'info' 'Performing cleanup';
local pwd="$(pwd)";
log 'debug' "Deleting ${pwd}/versions";
Expand All @@ -196,8 +111,6 @@ cleanup() {
rm -rf ./.terraform-version;
log 'debug' "Deleting ${pwd}/min_required.tf";
rm -rf ./min_required.tf;
log 'debug' "Deleting ${pwd}/required_version.tf";
rm -rf ./required_version.tf;
};
export -f cleanup;

Expand All @@ -220,6 +133,7 @@ function check_dependencies() {
export -f check_dependencies;

source "$TFENV_ROOT/lib/tfenv-exec.sh";
source "$TFENV_ROOT/lib/tfenv-min-required.sh";
source "$TFENV_ROOT/lib/tfenv-version-file.sh";
source "$TFENV_ROOT/lib/tfenv-version-name.sh";

Expand Down
4 changes: 4 additions & 0 deletions lib/tfenv-exec.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#!/usr/bin/env bash

set -uo pipefail;

function tfenv-exec() {
log 'debug' 'Getting version from tfenv-version-name';
TFENV_VERSION="$(tfenv-version-name)" \
Expand Down
33 changes: 33 additions & 0 deletions lib/tfenv-min-required.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash

set -uo pipefail;

function tfenv-min-required() {
local path="${1:-.}";

local versions="$( echo $(cat ${path}/{*.tf,*.tf.json} 2>/dev/null | grep -Eh '^\s*[^#]*\s*required_version') | grep -o '[~=!<>]\{0,2\}\s*\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";

if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*)([0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
qualifier="${BASH_REMATCH[1]}";
found_min_required="${BASH_REMATCH[2]}${BASH_REMATCH[3]}";
if [[ "${qualifier}" =~ ^!= ]]; then
log 'debug' "required_version is a negation - we cannot guess the desired one, skipping.";
else
local min_required_file="$(grep -Hn required_version ${path}/{*.tf,*.tf.json} 2>/dev/null | xargs)";

# Probably not an advisable way to choose a terraform version,
# but this is the way this functionality works in terraform:
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
found_min_required="${found_min_required}.0";
done;

log 'debug' "Determined min-required to be '${found_min_required}' (${min_required_file})";
echo -en "${found_min_required}";
return;
fi;
fi;

log 'debug' 'Appropriate required_version not found, skipping min-required';
};
export -f tfenv-min-required;
12 changes: 11 additions & 1 deletion lib/tfenv-version-file.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
find_local_version_file() {
#!/usr/bin/env bash

set -uo pipefail;

function find_local_version_file() {
log 'debug' "Looking for a version file in ${1}";

local root="${1}";

while ! [[ "${root}" =~ ^//[^/]*$ ]]; do

if [ -e "${root}/.terraform-version" ]; then
log 'debug' "Found at ${root}/.terraform-version";
echo "${root}/.terraform-version";
return 0;
else
log 'debug' "Not found at ${root}/.terraform-version";
fi;

[ -n "${root}" ] || break;
root="${root%/*}";

done;

log 'debug' "No version file found in ${1}";
return 1;
};
Expand Down
65 changes: 31 additions & 34 deletions lib/tfenv-version-name.sh
Original file line number Diff line number Diff line change
@@ -1,52 +1,45 @@
#!/usr/bin/env bash

set -uo pipefail;

function tfenv-version-name() {
if [[ -z "${TFENV_TERRAFORM_VERSION:-""}" ]]; then
log 'debug' 'We are not hardcoded by a TFENV_TERRAFORM_VERSION environment variable';

TFENV_VERSION_FILE="$(tfenv-version-file)" \
&& log 'debug' "TFENV_VERSION_FILE retrieved from tfenv-version-file: ${TFENV_VERSION_FILE}" \
|| log 'error' 'Failed to retrieve TFENV_VERSION_FILE from tfenv-version-file';

if [ "${TFENV_VERSION_FILE}" = "${TFENV_CONFIG_DIR}/version" ]; then
log 'debug' 'Tryng to set version from "required_version" under "terraform" section';

versions="$( echo $(cat {*.tf,*.tf.json} 2>/dev/null | grep -h required_version) | grep -o '\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";
if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
found_min_required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}";
if [[ "${found_min_required}" =~ ^!=.+ ]]; then
log 'debug' "required_version is a negation - we cannot guess the desired one, skipping.";
else
found_min_required="$(echo "$found_min_required")";

# Probably not an advisable way to choose a terraform version,
# but this is the way this functionality works in terraform:
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
found_min_required="${found_min_required}.0";
done;
TFENV_VERSION="${found_min_required}";
fi;
fi;
TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \
&& log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}";

TFENV_VERSION_SOURCE='terraform{required_version}';
fi;

if [[ -z "${TFENV_VERSION:-""}" ]]; then
TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \
&& log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}";
TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}";

TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}";
fi;
else
TFENV_VERSION="${TFENV_TERRAFORM_VERSION}" \
&& log 'debug' "TFENV_VERSION specified in TFENV_TERRAFORM_VERSION: ${TFENV_VERSION}";
&& log 'debug' "TFENV_VERSION specified in TFENV_TERRAFORM_VERSION environment variable: ${TFENV_VERSION}";

TFENV_VERSION_SOURCE='TFENV_TERRAFORM_VERSION';
fi;

local auto_install="${TFENV_AUTO_INSTALL:-true}";

if [[ "${TFENV_VERSION}" == "min-required" ]]; then
log 'debug' 'TFENV_VERSION uses min-required keyword, looking for a required_version in the code';

local potential_min_required="$(tfenv-min-required)";
if [[ -n "${potential_min_required}" ]]; then
log 'debug' "'min-required' converted to '${potential_min_required}'";
TFENV_VERSION="${potential_min_required}" \
TFENV_VERSION_SOURCE='terraform{required_version}';
else
log 'error' 'Specifically asked for min-required via terraform{required_version}, but none found';
fi;
fi;

if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then
log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}";

[ -d "${TFENV_CONFIG_DIR}/versions" ] \
|| log 'error' 'No versions of terraform installed. Please install one with: tfenv install';

if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then
regex="${TFENV_VERSION##*\:}";
log 'debug' "'latest' keyword uses regex: ${regex}";
Expand All @@ -62,10 +55,14 @@ function tfenv-version-name() {
| sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \
| grep -e "${regex}" \
| head -n 1)";

log 'debug' "Resolved ${TFENV_VERSION} to locally installed version: ${local_version}";
elif [[ "${auto_install}" != "true" ]]; then
log 'error' 'No versions of terraform installed and TFENV_AUTO_INSTALL is not true. Please install a version of terraform before it can be selected as latest';
fi;

if [[ "${TFENV_AUTO_INSTALL:-true}" == "true" ]]; then
log 'debug' "Trying to find the remote version using the regex: ${regex}";
if [[ "${auto_install}" == "true" ]]; then
log 'debug' "Using latest keyword and auto_install means the current version is whatever is latest in the remote. Trying to find the remote version using the regex: ${regex}";
remote_version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)";
if [[ -n "${remote_version}" ]]; then
if [[ "${local_version}" != "${remote_version}" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion libexec/tfenv-init
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash

[ -n "${TFENV_DEBUG}" ] && set -x:
[ -n "${TFENV_DEBUG}" ] && set -x;

export PATH="${TFENV_ROOT}/bin:${PATH}";
35 changes: 4 additions & 31 deletions libexec/tfenv-min-required
Original file line number Diff line number Diff line change
Expand Up @@ -71,34 +71,7 @@ terraform {
see https://www.terraform.io/docs/configuration/terraform.html for details';
};

find_min_required() {
local root="${1}";

versions="$( echo $(grep -h required_version "${root}"/{*.tf,*.tf.json} 2>/dev/null ) | grep -o '\([0-9]\+\.\?\)\{2,3\}\(-[a-z]\+[0-9]\+\)\?')";

if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]*(-[a-z]+[0-9]+)? ]]; then
found_min_required="${BASH_REMATCH[1]}${BASH_REMATCH[2]}";

if [[ "${found_min_required}" =~ ^!=.+ ]]; then
log 'debug' "Error: Min required version is a negation ($found_min_required) - we cannot guess the desired one.";
bailout;
else
found_min_required="$(echo "$found_min_required")";
#echo "Min required version is detected as ${found_min_required}";

# Probably not an advisable way to choose a terraform version,
# but this is the way this functionality works in terraform:
# add .0 to versions without a minor and/or patch version (e.g. 12.0)
while ! [[ "${found_min_required}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; do
found_min_required="${found_min_required}.0";
done;

echo "${found_min_required}";
exit 0;
fi;
fi;

bailout;
};

find_min_required "${TFENV_DIR:-$(pwd)}";
declare min_required="$(tfenv-min-required "${TFENV_DIR:-$(pwd)}")";
[[ -n "${min_required}" ]] \
&& echo "${min_required}" \
|| bailout;
Loading

0 comments on commit 0a3c2ca

Please sign in to comment.