Skip to content
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ If a parameter is passed, available options:
- `latest` is a syntax to install latest version
- `latest:<regex>` is a syntax to install latest version matching regex (used by grep -e)
- `latest-allowed` is a syntax to scan your Terraform files to detect which version is maximally allowed.
- `latest-allowed-stable` is the same as `latest-allowed`, but only considers stable versions and not pre-releases.
- `min-required` is a syntax to scan your Terraform files to detect which version is minimally required.

See [required_version](https://developer.hashicorp.com/terraform/language/settings) docs. Also [see min-required & latest-allowed](#min-required) section below.
Expand All @@ -104,6 +105,7 @@ $ tfenv install 0.7.0
$ tfenv install latest
$ tfenv install latest:^0.8
$ tfenv install latest-allowed
$ tfenv install latest-allowed-stable
$ tfenv install min-required
```

Expand Down Expand Up @@ -131,7 +133,7 @@ validation failure.
If you use a [.terraform-version](#terraform-version-file) file, `tfenv install` (no argument) will install the version written in it.

<a name="min-required"></a>
#### min-required & latest-allowed
#### min-required & latest-allowed & latest-allowed-stable

Please note that we don't do semantic version range parsing but use first ever found version as the candidate for minimally required one. It is up to the user to keep the definition reasonable. I.e.

Expand Down
3 changes: 3 additions & 0 deletions lib/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ function cleanup() {
rm -rf ./.terraform-version;
log 'debug' "Deleting ${pwd}/latest_allowed.tf";
rm -rf ./latest_allowed.tf;
log 'debug' "Deleting ${pwd}/latest_allowed_stable.tf";
rm -rf ./latest_allowed_stable.tf;
log 'debug' "Deleting ${pwd}/min_required.tf";
rm -rf ./min_required.tf;
log 'debug' "Deleting ${pwd}/chdir-dir";
Expand All @@ -149,6 +151,7 @@ function check_dependencies() {
alias grep=ggrep;
fi;
};

export -f check_dependencies;

source "$TFENV_ROOT/lib/tfenv-exec.sh";
Expand Down
34 changes: 31 additions & 3 deletions libexec/tfenv-resolve-version
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ if [[ "${version_requested}" =~ ^min-required$ ]]; then
version_requested="${min_required}";
fi;

if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
if [[ "${version_requested}" =~ ^latest-allowed(-stable)?$ ]]; then
log 'debug' 'Detecting latest allowable version...';
version_spec="$(grep -h required_version "${TFENV_DIR:-$(pwd)}"/{*.tf,*.tf.json} 2>/dev/null | { IFS='"' read -r _ ver _; echo "${ver%%,*}"; })";
version_num="$(echo "${version_spec}" | sed -E 's/[^0-9.]+//')";
Expand All @@ -138,11 +138,39 @@ if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
version_requested=latest;
;;
'<='*)
version_requested="${version_num}";
IFS='.-' read -r major minor patch pre_release <<< "${version_num}"
if [[ "${version_requested}" =~ latest-allowed-stable$ && -n "${pre_release}" ]]; then
constraint_regex="^"
if [[ "$major" != 0 ]]; then
constraint_regex="^$(seq 0 $((major - 1)) | sed "s/[0-9]*/&\\\.[0-9]\\\+\\\.[0-9]\\\+\\\|/g" | tr -d '\n')"
fi

if [[ "$minor" != 0 ]]; then
constraint_regex+="$(seq 0 $((minor - 1)) | sed "s/[0-9]*/${major}\\\.&\\\.[0-9]\\\+\\\|/g" | tr -d '\n')"
fi

constraint_regex="${constraint_regex%\\|}" # Remove trailing '|'
constraint_regex+="$"
version_requested="latest:${constraint_regex}"
else
version_requested="${version_num}";
fi
;;
'~>'*)
version_without_rightmost="${version_num%.*}";
version_requested="latest:^${version_without_rightmost}";
if [[ "${version_without_rightmost}" == "${version_num}" ]]; then
version_requested=latest
elif [[ "${version_requested}" =~ ^latest-allowed-stable$ ]]; then
IFS='.' read -ra version_without_rightmost_parts <<< "${version_without_rightmost}"
version_without_rightmost_count=${#version_without_rightmost_parts[@]}
version_requested="latest:^${version_without_rightmost}"
for (( i = version_without_rightmost_count; i < 3; i++ )); do
version_requested+="\.[0-9]\+"
done
version_requested+="$"
else
version_requested="latest:^${version_without_rightmost}"
fi
;;
*)
log 'error' "Unsupported version spec: '${version_spec}', only >, >=, <=, and ~> are supported.";
Expand Down
153 changes: 153 additions & 0 deletions test/test_use_latestallowedstable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/env bash

set -uo pipefail;

####################################
# Ensure we can execute standalone #
####################################

function early_death() {
echo "[FATAL] ${0}: ${1}" >&2;
exit 1;
};

if [ -z "${TFENV_ROOT:-""}" ]; then
# http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
readlink_f() {
local target_file="${1}";
local file_name;

while [ "${target_file}" != "" ]; do
cd "$(dirname ${target_file})" || early_death "Failed to 'cd \$(dirname ${target_file})' while trying to determine TFENV_ROOT";
file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TFENV_ROOT";
target_file="$(readlink "${file_name}")";
done;

echo "$(pwd -P)/${file_name}";
};

TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
[ -n ${TFENV_ROOT} ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TFENV_ROOT";
else
TFENV_ROOT="${TFENV_ROOT%/}";
fi;
export TFENV_ROOT;

if [ -n "${TFENV_HELPERS:-""}" ]; then
log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again';
else
[ "${TFENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh";
if source "${TFENV_ROOT}/lib/helpers.sh"; then
log 'debug' 'Helpers sourced successfully';
else
early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh";
fi;
fi;

#####################
# Begin Script Body #
#####################

declare -a errors=();

cleanup || log 'error' 'Cleanup failed?!';


log 'info' '### Install latest-allowed-stable normal version (#.#.#)';

echo "terraform {
required_version = \"~> 1.1.0\"
}" > latest_allowed_stable.tf;

(
tfenv install latest-allowed-stable;
tfenv use latest-allowed-stable;
check_active_version 1.1.9;
) || error_and_proceed 'Latest allowed stable version does not match';

cleanup || log 'error' 'Cleanup failed?!';

log 'info' '### Install latest-allowed-stable tagged version (#.#.#-tag#)'

echo "terraform {
required_version = \"<=0.13.0-rc1\"
}" > latest_allowed_stable.tf;

(
tfenv install latest-allowed-stable;
tfenv use latest-allowed-stable;
check_active_version 0.12.31;
) || error_and_proceed 'Latest allowed stable tagged-version does not match';

cleanup || log 'error' 'Cleanup failed?!';


echo "terraform {
required_version = \"<=1.1.0-alpha20211006\"
}" > latest_allowed_stable.tf;

(
tfenv install latest-allowed-stable;
tfenv use latest-allowed-stable;
check_active_version 1.0.11;
) || error_and_proceed 'Latest allowed stable tagged-version does not match';

cleanup || log 'error' 'Cleanup failed?!';


log 'info' '### Install latest-allowed-stable incomplete version (#.#.<missing>)'

echo "terraform {
required_version = \"~> 0.12\"
}" >> latest_allowed_stable.tf;

(
tfenv install latest-allowed-stable;
tfenv use latest-allowed-stable;
check_active_version 0.15.5;
) || error_and_proceed 'Latest allowed stable incomplete-version does not match';

cleanup || log 'error' 'Cleanup failed?!';


log 'info' '### Install latest-allowed-stable with TFENV_AUTO_INSTALL';

echo "terraform {
required_version = \"~> 1.0.0\"
}" >> latest_allowed_stable.tf;
echo 'latest-allowed-stable' > .terraform-version;

(
TFENV_AUTO_INSTALL=true terraform version;
check_active_version 1.0.11;
) || error_and_proceed 'Latest allowed stable auto-installed version does not match';

cleanup || log 'error' 'Cleanup failed?!';


log 'info' '### Install latest-allowed with TFENV_AUTO_INSTALL & -chdir';

mkdir -p chdir-dir
echo "terraform {
required_version = \"~> 0.14.3\"
}" >> chdir-dir/latest_allowed.tf;
echo 'latest-allowed-stable' > chdir-dir/.terraform-version

(
TFENV_AUTO_INSTALL=true terraform -chdir=chdir-dir version;
check_active_version 0.14.11 chdir-dir;
) || error_and_proceed 'Latest allowed version from -chdir does not match';

cleanup || log 'error' 'Cleanup failed?!';

if [ "${#errors[@]}" -gt 0 ]; then
log 'warn' '===== The following use_latestallowed tests failed =====';
for error in "${errors[@]}"; do
log 'warn' "\t${error}";
done;
log 'error' 'use_latestallowed test failure(s)';
else
log 'info' 'All use_latestallowed tests passed.';
fi;

exit 0;