Skip to content

Commit

Permalink
Merge pull request tfutils#309 from OJFord/impl-307
Browse files Browse the repository at this point in the history
Add `latest-allowed` option from required_version
  • Loading branch information
Zordrak authored Oct 1, 2022
2 parents 34c744b + 699f998 commit 9151b3e
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 5 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,17 @@ If a parameter is passed, available options:
- `x.y.z` [Semver 2.0.0](https://semver.org/) string specifying the exact version to install
- `latest` is a syntax to install latest version
- `latest:<regex>` is a syntax to install latest version matching regex (used by grep -e)
- `min-required` is a syntax to recursively scan your Terraform files to detect which version is minimally required. See [required_version](https://www.terraform.io/docs/configuration/terraform.html) docs. Also [see min-required](#min-required) section below.
- `latest-allowed` is a syntax to scan your Terraform files to detect which version is maximally allowed.
- `min-required` is a syntax to scan your Terraform files to detect which version is minimally required.

See [required_version](https://www.terraform.io/docs/configuration/terraform.html) docs. Also [see min-required & latest-allowed](#min-required) section below.

```console
$ tfenv install
$ tfenv install 0.7.0
$ tfenv install latest
$ tfenv install latest:^0.8
$ tfenv install latest-allowed
$ tfenv install min-required
```

Expand All @@ -121,7 +125,8 @@ validation failure.

If you use a [.terraform-version](#terraform-version-file) file, `tfenv install` (no argument) will install the version written in it.

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

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 All @@ -133,9 +138,9 @@ terraform {
```

```terraform
// this will detect 0.10.0
// this will detect 0.10.8 (the latest 0.10.x release)
terraform {
required_version = ">= 0.10.0, <0.12.3"
required_version = "~> 0.10.0, <0.12.3"
}
```

Expand Down
4 changes: 3 additions & 1 deletion lib/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ function check_active_version() {

local active_version="$(${TFENV_ROOT}/bin/terraform ${maybe_chdir} version | grep '^Terraform')";

if ! grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?\$" <(echo "${active_version}"); then
if ! grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?( is already installed)?\$" <(echo "${active_version}"); then
log 'debug' "Expected version ${v} but found ${active_version}";
return 1;
fi;
Expand Down Expand Up @@ -124,6 +124,8 @@ function cleanup() {
rm -rf ./versions;
log 'debug' "Deleting ${pwd}/.terraform-version";
rm -rf ./.terraform-version;
log 'debug' "Deleting ${pwd}/latest_allowed.tf";
rm -rf ./latest_allowed.tf;
log 'debug' "Deleting ${pwd}/min_required.tf";
rm -rf ./min_required.tf;
log 'debug' "Deleting ${pwd}/chdir-dir";
Expand Down
5 changes: 5 additions & 0 deletions lib/tfenv-version-name.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ function tfenv-version-name() {
if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then
log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}";

if [[ "${TFENV_VERSION}" == latest-allowed ]]; then
TFENV_VERSION="$(tfenv-resolve-version)";
log 'debug' "Resolved latest-allowed to: ${TFENV_VERSION}";
fi;

if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then
regex="${TFENV_VERSION##*\:}";
log 'debug' "'latest' keyword uses regex: ${regex}";
Expand Down
24 changes: 24 additions & 0 deletions libexec/tfenv-resolve-version
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,30 @@ if [[ "${version_requested}" =~ ^min-required$ ]]; then
version_requested="${min_required}";
fi;

if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
log 'debug' 'Detecting latest allowable version...';
version_spec="$(grep -h required_version "${TFENV_DIR:-$(pwd)}"/{*.tf,*.tf.json} 2>/dev/null | rev | cut -d'"' -f2 | rev | cut -d, -f1)";
version_num="$(echo "${version_spec}" | sed -E 's/[^0-9.]+//')";
log 'debug' "Using ${version_num} from version spec: ${version_spec}";

case "${version_spec}" in
'>'*)
version_requested=latest;
;;
'<='*)
version_requested="${version_num}";
;;
'~>'*)
version_without_rightmost="$(echo "${version_num}" | rev | cut -d. -f2- | rev)";
version_requested="latest:^${version_without_rightmost}";
;;
*)
log 'error' "Unsupported version spec: '${version_spec}', only >, >=, <=, and ~> are supported.";
;;
esac;
log 'debug' "Determined the requested version to be: ${version_requested}";
fi;

if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
version="${version_requested%%\:*}";
regex="${version_requested##*\:}";
Expand Down
4 changes: 4 additions & 0 deletions libexec/tfenv-uninstall
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ if [[ "${version_requested}" =~ ^min-required$ ]]; then
log 'error' 'min-required is an unsupported option for uninstall';
fi;

if [[ "${version_requested}" == latest-allowed ]]; then
log 'error' 'latest-allowed is an unsupported option for uninstall';
fi;

if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
version="${version_requested%%\:*}";
regex="${version_requested##*\:}";
Expand Down
141 changes: 141 additions & 0 deletions test/test_use_latestallowed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/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 normal version (#.#.#)';

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

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

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


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

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

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

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


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

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

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

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


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

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

(
TFENV_AUTO_INSTALL=true terraform version;
check_active_version 1.0.11;
) || error_and_proceed 'Latest allowed 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' > 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;

0 comments on commit 9151b3e

Please sign in to comment.