From 0007f77751eecb149c88ddf78eb213878e4c6430 Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Sun, 15 Mar 2020 19:52:29 +0000 Subject: [PATCH 1/7] A shot at v2.0.0-beta2 without use-on-install --- CHANGELOG.md | 7 +- README.md | 22 +++++- lib/helpers.sh | 60 +++++++++++++++++ libexec/tfenv-install | 56 ++-------------- libexec/tfenv-resolve-version | 122 ++++++++++++++++++++++++++++++++++ libexec/tfenv-use | 38 +++-------- libexec/tfenv-version-file | 2 +- libexec/tfenv-version-name | 2 +- test/run.sh | 1 - test/test_install_and_use.sh | 10 ++- test/test_list.sh | 2 + 11 files changed, 233 insertions(+), 89 deletions(-) create mode 100755 libexec/tfenv-resolve-version diff --git a/CHANGELOG.md b/CHANGELOG.md index a371754..81d12d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ ## 2.0.0 (Unreleased) - * New logging library - * New bash4 dependency + * New logging and debugging library * Massive testing, logging and loading refactoring * Fix to 'use' logic: don't overwrite .terraform-version files + * Fix #167 - Never invoke use automatically on install - multiple code and testing changes for new logic + * Fix to not use 0.12.22 during testing which reports its version incorrectly + * Introduce tfenv-resolve-version to deduplicate translation of requested version into actual version + * README.md updates ## 1.0.2 (October 29, 2019) diff --git a/README.md b/README.md index ac54cbe..7cf4e46 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,11 @@ include ::tfenv ### tfenv install [version] -Install a specific version of Terraform. Available options for version: +Install a specific version of Terraform. + +If no parameter is passed, the version to use is resolved automatically via .terraform-version files, defaulting to 'latest' if none are found. + +If a parameter is passed, available options: - `i.j.k` exact version to install - `latest` is a syntax to install latest version @@ -74,11 +78,11 @@ Install a specific version of Terraform. Available options for version: - `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. ```console +$ tfenv install $ tfenv install 0.7.0 $ tfenv install latest $ tfenv install latest:^0.8 $ tfenv install min-required -$ tfenv install ``` If `shasum` is present in the path, tfenv will verify the download against Hashicorp's published sha256 hash. @@ -146,6 +150,15 @@ Set the mechanism used for displaying download progress when downloading terrafo ##### `TFENV_DEBUG` +Integer (Default: 0) + +Set the debug level for TFENV. + +* 0: No debug output +* 1: Simple debug output +* 2: Extended debug output, with source file names and interactive debug shells on error +* 3: Debug level 2 + Bash execution tracing + ##### `TFENV_REMOTE` String (Default: https://releases.hashicorp.com) @@ -279,10 +292,12 @@ Defaults to the PID of the calling process. -### tfenv use <version> +### tfenv use [version] Switch a version to use +If no parameter is passed, the version to use is resolved automatically via .terraform-version files, defaulting to 'latest' if none are found. + `latest` is a syntax to use the latest installed version `latest:` is a syntax to use latest installed version matching regex (used by grep -e) @@ -290,6 +305,7 @@ Switch a version to use `min-required` will switch to the version minimally required by your terraform sources (see above `tfenv install`) ```console +$ tfenv use $ tfenv use min-required $ tfenv use 0.7.0 $ tfenv use latest diff --git a/lib/helpers.sh b/lib/helpers.sh index cfddfba..e97047f 100755 --- a/lib/helpers.sh +++ b/lib/helpers.sh @@ -34,6 +34,66 @@ fi; source "${TFENV_ROOT}/lib/bashlog.sh"; +resolve_version () { + declare version_requested version regex min_required version_file; + + declare arg="${1:-""}"; + + if [ -z "${arg}" ]; then + version_file="$(tfenv-version-file)"; + log 'debug' "Version File: ${version_file}"; + + if [ "${version_file}" != "${TFENV_ROOT}/version" ]; then + log 'debug' "Version File (${version_file}) is not the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; + version_requested="$(cat "${version_file}")" \ + || log 'error' "Failed to open ${version_file}"; + + elif [ -f "${version_file}" ]; then + log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; + version_requested="$(cat "${version_file}")" \ + || log 'error' "Failed to open ${version_file}"; + + # Absolute fallback + if [ -z "${version_requested}" ]; then + log 'debug' 'Version file had no content. Falling back to "latest"'; + version_requested='latest'; + fi; + + else + log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/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; + 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"; diff --git a/libexec/tfenv-install b/libexec/tfenv-install index 50efff9..33d39b8 100755 --- a/libexec/tfenv-install +++ b/libexec/tfenv-install @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -uo pipefail; #################################### @@ -61,60 +60,20 @@ done; [ "${#}" -gt 1 ] && log 'error' 'usage: tfenv install []'; -declare version_requested version regex; -declare arg="${1:-""}"; - -if [ -z "${arg}" ]; then - version_file="$(tfenv-version-file)"; - log 'debug' "Version File: ${version_file}"; - if [ "${version_file}" != "${TFENV_ROOT}/version" ]; then - log 'debug' "Version File (${version_file}) is not the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; - version_requested="$(cat "${version_file}")" \ - || log 'error' "Failed to open ${version_file}"; - elif [ -f "${version_file}" ]; then - log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; - version_requested="$(cat "${version_file}")" \ - || log 'error' "Failed to open ${version_file}"; - else - log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/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; -else - version_requested="${arg}"; -fi; - -log 'debug' "Version Requested: ${version_requested}"; +declare requested="${1:-""}"; -if [[ "${version_requested}" =~ ^min-required$ ]]; then - log 'info' 'Detecting minimal required version...'; - found_min_required="$(tfenv-min-required)"; +log debug "Resolving version with: tfenv-resolve-version ${requested}"; +declare resolved="$(tfenv-resolve-version ${requested})"; - if [[ $? -eq 0 ]]; then - log 'info' "Min required version is detected as ${found_min_required}"; - version_requested="${found_min_required}"; - else - exit 1; - fi; -fi; - -if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then - version="${version_requested%%\:*}"; - regex="${version_requested##*\:}"; -elif [[ "${version_requested}" =~ ^latest$ ]]; then - version="${version_requested}"; - regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$"; -else - version="${version_requested}"; - regex="^${version_requested}$"; -fi; +declare version="${resolved%%\:*}"; +declare regex="${resolved##*\:}"; [ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest'; log 'debug' "Processing install for version ${version}, using regex ${regex}"; version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)"; -[ -n "${version}" ] || log 'error' "No versions matching '${arg}' found in remote"; +[ -n "${version}" ] || log 'error' "No versions matching '${requested}' found in remote"; dst_path="${TFENV_ROOT}/versions/${version}"; if [ -f "${dst_path}/terraform" ]; then @@ -279,5 +238,4 @@ while IFS= read -r unzip_line; do log 'info' "${unzip_line}"; done < <(printf '%s\n' "${unzip_output}"); -log 'info' "Installation of terraform v${version} successful"; -tfenv-use "${version}"; +log 'info' "Installation of terraform v${version} successful. To make this your default version, run 'tfenv use ${version}'"; diff --git a/libexec/tfenv-resolve-version b/libexec/tfenv-resolve-version new file mode 100755 index 0000000..d488e52 --- /dev/null +++ b/libexec/tfenv-resolve-version @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +# Usage: tfenv resolve-version [] +# Summary: Resolve the version to action based on the environment and optional input token + +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; + +# Ensure libexec and bin are in $PATH +for dir in libexec bin; do + case ":${PATH}:" in + *:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";; + *) + log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now"; + export PATH="${TFENV_ROOT}/${dir}:${PATH}"; + ;; + esac; +done; + +##################### +# Begin Script Body # +##################### + +declare version_requested version regex min_required version_file; + +declare arg="${1:-""}"; + +if [ -z "${arg}" ]; then + version_file="$(tfenv-version-file)"; + log 'debug' "Version File: ${version_file}"; + + if [ "${version_file}" != "${TFENV_ROOT}/version" ]; then + log 'debug' "Version File (${version_file}) is not the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; + version_requested="$(cat "${version_file}")" \ + || log 'error' "Failed to open ${version_file}"; + + elif [ -f "${version_file}" ]; then + log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/version)"; + version_requested="$(cat "${version_file}")" \ + || log 'error' "Failed to open ${version_file}"; + + # Absolute fallback + if [ -z "${version_requested}" ]; then + log 'debug' 'Version file had no content. Falling back to "latest"'; + version_requested='latest'; + fi; + + else + log 'debug' "Version File is the default \${TFENV_ROOT}/version (${TFENV_ROOT}/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; +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; + +echo "${version}:${regex}"; diff --git a/libexec/tfenv-use b/libexec/tfenv-use index d701f28..2a96a2d 100755 --- a/libexec/tfenv-use +++ b/libexec/tfenv-use @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -uo pipefail; #################################### @@ -59,40 +58,21 @@ done; # Begin Script Body # ##################### -[ "${#}" -ne 1 ] && log 'error' 'usage: tfenv use '; - -declare version_requested version regex min_required version_file; - -version_requested="${1}"; +[ "${#}" -gt 1 ] && log 'error' 'usage: tfenv use []'; -if [[ "${version_requested}" =~ ^min-required$ ]]; then - log 'info' 'Detecting minimum required version...'; - min_required="$(tfenv-min-required)" \ - || log 'error' 'tfenv-min-required failed'; +[ -d "${TFENV_ROOT}/versions" ] \ + || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; - log 'info' "Minimum required version detected: ${min_required}"; - version_requested="${min_required}"; -fi; +declare requested="${1:-""}"; -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; +log debug "Resolving version with: tfenv-resolve-version ${requested}"; +declare resolved="$(tfenv-resolve-version ${requested})"; -[ -d "${TFENV_ROOT}/versions" ] \ - || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; +declare version="${resolved%%\:*}"; +declare regex="${resolved##*\:}"; log 'debug' "Searching ${TFENV_ROOT}/versions for latest version matching ${regex}"; -version="$(\ls "${TFENV_ROOT}/versions" \ +declare version="$(\ls "${TFENV_ROOT}/versions" \ | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ | grep -e "${regex}" \ | head -n 1 diff --git a/libexec/tfenv-version-file b/libexec/tfenv-version-file index 7dfb976..e4f0ce7 100755 --- a/libexec/tfenv-version-file +++ b/libexec/tfenv-version-file @@ -80,7 +80,7 @@ find_local_version_file() { } if ! find_local_version_file "${TFENV_DIR:-${PWD}}"; then - if ! find_local_version_file "${HOME}"; then + if ! find_local_version_file "${HOME:-/}"; then log 'debug' "No version file found in search paths. Defaulting to TFENV_ROOT: ${TFENV_ROOT}/version"; echo "${TFENV_ROOT}/version"; fi; diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index 64bb7c0..dbd71fd 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -103,5 +103,5 @@ fi; if [ -d "${TFENV_ROOT}/versions/${TFENV_VERSION}" ]; then echo "${TFENV_VERSION}"; else - log 'error' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})"; + log 'warn' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})"; fi; diff --git a/test/run.sh b/test/run.sh index 6abb640..3a24dd5 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -uo pipefail; #################################### diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 8bea949..65d608d 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -uo pipefail; #################################### @@ -54,6 +53,7 @@ test_install_and_use() { local v="${1}"; tfenv install "${k}" || return 1; check_installed_version "${v}" || return 1; + tfenv use "${k}" || return 1; check_active_version "${v}" || return 1; return 0; }; @@ -64,6 +64,7 @@ test_install_and_use_overridden() { local v="${1}"; tfenv install "${k}" || return 1; check_installed_version "${v}" || return 1; + tfenv use "${k}" || return 1; check_default_version "${v}" || return 1; return 0; } @@ -116,8 +117,11 @@ done; cleanup || log 'error' 'Cleanup failed?!'; log 'info' '## ${HOME}/.terraform-version Test Preparation'; -declare v1="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 2 | tail -n 1)"; -declare v2="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1)"; + +# 0.12.22 reports itself as 0.12.21 and breaks testing +declare v1="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | grep -v '0.12.22' | head -n 2 | tail -n 1)"; +declare v2="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | grep -v '0.12.22' | head -n 1)"; + if [ -f "${HOME}/.terraform-version" ]; then log 'info' "Backing up ${HOME}/.terraform-version to ${HOME}/.terraform-version.bup"; mv "${HOME}/.terraform-version" "${HOME}/.terraform-version.bup"; diff --git a/test/test_list.sh b/test/test_list.sh index 365555f..c640e27 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -60,6 +60,8 @@ for v in 0.7.2 0.7.13 0.9.1 0.9.2 0.9.11; do || error_and_proceed "Install of version ${v} failed"; done; +tfenv use 0.9.11 + log 'info' '## Comparing "tfenv list" to expectations'; result="$(tfenv list)"; expected="$(cat << EOS From 45a8ad7ae0de2f2633bf658b9f0e2cf9f0d88947 Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Tue, 17 Mar 2020 16:51:54 +0000 Subject: [PATCH 2/7] Fix the failure to correctly handle a version requested not being installed. Handle it with the defaulted option to automatically install missing versions --- CHANGELOG.md | 1 + README.md | 10 ++++++++++ libexec/tfenv-exec | 9 +++++++++ libexec/tfenv-version-name | 9 +++++---- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d12d5..a61c6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Fix to not use 0.12.22 during testing which reports its version incorrectly * Introduce tfenv-resolve-version to deduplicate translation of requested version into actual version * README.md updates + * Fix #176 - New parameter TFENV_AUTO_INSTALL to handle the version specified by `use` or a `.terraform-version` file not being installed ## 1.0.2 (October 29, 2019) diff --git a/README.md b/README.md index 7cf4e46..157008f 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,16 @@ Specify architecture. Architecture other than the default amd64 can be specified TFENV_ARCH=arm tfenv install 0.7.9 ``` +##### `TFENV_AUTO_INSTALL` + +String (Default: true) + +Should tfenv automatically install terraform if the version specified by defaults or a .terraform-version file is not currently installed. + +```console +TFENV_AUTO_INSTALL=false terraform plan +``` + ##### `TFENV_CURL_OUTPUT` Integer (Default: 2) diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index a1359dc..4195fd2 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -83,6 +83,15 @@ TFENV_VERSION="$(tfenv-version-name)" \ }; export TFENV_VERSION; +if [ ! -d "${TFENV_ROOT}/versions/${TFENV_VERSION}" ]; then + if [ "${TFENV_AUTO_INSTALL:-true}" == "true" ]; then + log 'info' "version '${TFENV_VERSION}' is not installed (set by $(tfenv-version-file)). Installing now as TFENV_AUTO_INSTALL==true"; + tfenv-install; + else + log 'error' "version '${TFENV_VERSION}' was requested, but not installed and TFENV_AUTO_INSTALL is not 'true'"; + fi; +fi; + TF_BIN_PATH="${TFENV_ROOT}/versions/${TFENV_VERSION}/terraform"; export PATH="${TF_BIN_PATH}:${PATH}"; log 'debug' "TF_BIN_PATH added to PATH: ${TF_BIN_PATH}"; diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index dbd71fd..d9bbe5d 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -100,8 +100,9 @@ fi; [ -z "${TFENV_VERSION}" ] \ && log 'error' "Version could not be resolved (set by ${TFENV_VERSION_FILE} or tfenv use )"; -if [ -d "${TFENV_ROOT}/versions/${TFENV_VERSION}" ]; then - echo "${TFENV_VERSION}"; -else - log 'warn' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})"; +if [ ! -d "${TFENV_ROOT}/versions/${TFENV_VERSION}" ]; then + log 'debug' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})"; fi; + +echo "${TFENV_VERSION}"; + From 8302fc7afd764a65d9bf149feb1e908fa5be5a9a Mon Sep 17 00:00:00 2001 From: Alexander Bayandin Date: Fri, 1 May 2020 12:13:44 +0100 Subject: [PATCH 3/7] Fix brace expansion of @ for bash 3.x --- bin/terraform | 6 +++--- bin/tfenv | 4 ++-- lib/bashlog.sh | 6 +++--- libexec/tfenv-exec | 6 +++--- test/run.sh | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/terraform b/bin/terraform index 08137ce..a2e12a9 100755 --- a/bin/terraform +++ b/bin/terraform @@ -62,8 +62,8 @@ log 'debug' "program=\"${0##*/}\""; declare tfenv_path="${TFENV_ROOT}/bin/tfenv"; -log 'debug' "Exec: \"${tfenv_path}\" exec \"${@}\""; -exec "${tfenv_path}" exec "${@}" \ - || log 'error' "Failed to exec: \"${tfenv_path}\" exec \"${@}\""; +log 'debug' "Exec: \"${tfenv_path}\" exec \"$*\""; +exec "${tfenv_path}" exec "$@" \ + || log 'error' "Failed to exec: \"${tfenv_path}\" exec \"$*\""; log 'error' 'This line should not be reachable. Something catastrophic has occurred'; diff --git a/bin/tfenv b/bin/tfenv index ccc135d..ae6334a 100755 --- a/bin/tfenv +++ b/bin/tfenv @@ -104,8 +104,8 @@ exit 1; } | abort && exit 1; fi; shift 1; - log 'debug' "Exec: \"${command_path}\" \"${@}\""; - exec "${command_path}" "${@}"; + log 'debug' "Exec: \"${command_path}\" \"$*\""; + exec "${command_path}" "$@"; ;; esac; diff --git a/lib/bashlog.sh b/lib/bashlog.sh index 52ad6db..aa8a450 100755 --- a/lib/bashlog.sh +++ b/lib/bashlog.sh @@ -8,7 +8,7 @@ function _log_exception() { BASHLOG_JSON=0; BASHLOG_SYSLOG=0; - log 'error' "Logging Exception: ${@}"; + log 'error' "Logging Exception: $*"; ); }; export -f _log_exception; @@ -39,7 +39,7 @@ function log() { shift 1; - local line="${@}"; + local line="$@"; # RFC 5424 # @@ -154,7 +154,7 @@ function log() { fi; ;; *) - log 'error' "Undefined log level trying to log: ${@}"; + log 'error' "Undefined log level trying to log: $*"; ;; esac }; diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index 4195fd2..6ebcb04 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -95,9 +95,9 @@ fi; TF_BIN_PATH="${TFENV_ROOT}/versions/${TFENV_VERSION}/terraform"; export PATH="${TF_BIN_PATH}:${PATH}"; log 'debug' "TF_BIN_PATH added to PATH: ${TF_BIN_PATH}"; -log 'debug' "Executing: ${TF_BIN_PATH} ${@}"; +log 'debug' "Executing: ${TF_BIN_PATH} $@"; -exec "${TF_BIN_PATH}" "${@}" \ - || log 'error' "Failed to execute: ${TF_BIN_PATH} ${@}"; +exec "${TF_BIN_PATH}" "$@" \ + || log 'error' "Failed to execute: ${TF_BIN_PATH} $*"; exit 0; diff --git a/test/run.sh b/test/run.sh index 3a24dd5..0e1d362 100755 --- a/test/run.sh +++ b/test/run.sh @@ -51,7 +51,7 @@ export PATH="${TFENV_ROOT}/bin:${PATH}"; errors=(); if [ "${#}" -ne 0 ]; then - targets="${@}"; + targets="$@"; else targets="$(\ls "$(dirname "${0}")" | grep 'test_')"; fi; From 9a066135d6db28ec749f6dd62ac3ab948be12b09 Mon Sep 17 00:00:00 2001 From: Alexander Bayandin Date: Fri, 1 May 2020 12:14:22 +0100 Subject: [PATCH 4/7] Run tests on default macOS bash (3.2) --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index d3ca4bd..a8742e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -before_install: - - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install bash; fi' # Bash4 Required language: generic matrix: include: @@ -10,10 +8,10 @@ matrix: - os: linux dist: trusty - os: osx - osx_image: xcode11.2 + osx_image: xcode11.3 - os: osx osx_image: xcode10.3 - #- os: osx - # osx_image: xcode9.4 # brew update is excruciatingly slow on xcode9.4 + - os: osx + osx_image: xcode9.4 script: - ./test/run.sh From ea93b04ecf254b3a9dbaceb4ab9328b40568a276 Mon Sep 17 00:00:00 2001 From: Alexander Bayandin Date: Wed, 6 May 2020 23:03:54 +0100 Subject: [PATCH 5/7] Replace usages of associative arrays for bash 3.x in tests --- test/test_install_and_use.sh | 88 +++++++++++++++++++++--------------- test/test_uninstall.sh | 32 ++++++++----- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 65d608d..6eccf22 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -73,46 +73,56 @@ declare -a errors=(); log 'info' '### Test Suite: Install and Use' -declare -A string_tests=(); - -string_tests['latest version']="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1),latest"; -string_tests['latest possibly-unstable version']="$(tfenv list-remote | head -n 1),latest:"; -string_tests['latest alpha']="$(tfenv list-remote | grep 'alpha' | head -n 1),latest:alpha"; -string_tests['latest beta']="$(tfenv list-remote | grep 'beta' | head -n 1),latest:beta"; -string_tests['latest rc']="$(tfenv list-remote | grep 'rc' | head -n 1),latest:rc"; -string_tests['latest possibly-unstable version from 0.11']="$(tfenv list-remote | grep '^0\.11\.' | head -n 1),latest:^0.11."; -string_tests['0.11.15-oci']='0.11.15-oci,0.11.15-oci'; -string_tests['latest version matching regex']='0.8.8,latest:^0.8'; -string_tests['specific version']="0.7.13,0.7.13"; - -declare kv k v; -declare -i test_num=1; - -for desc in "${!string_tests[@]}"; do +tests__desc=( + 'latest version' + 'latest possibly-unstable version' + 'latest alpha' + 'latest beta' + 'latest rc' + 'latest possibly-unstable version from 0.11' + '0.11.15-oci' + 'latest version matching regex' + 'specific version' +); +tests__kv=( + "$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1),latest" + "$(tfenv list-remote | head -n 1),latest:" + "$(tfenv list-remote | grep 'alpha' | head -n 1),latest:alpha" + "$(tfenv list-remote | grep 'beta' | head -n 1),latest:beta" + "$(tfenv list-remote | grep 'rc' | head -n 1),latest:rc" + "$(tfenv list-remote | grep '^0\.11\.' | head -n 1),latest:^0.11." + '0.11.15-oci,0.11.15-oci' + '0.8.8,latest:^0.8' + "0.7.13,0.7.13" +); +tests_count=${#tests__desc[@]} + +declare desc kv k v; + +for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!'; - kv="${string_tests[${desc}]}"; + desc=${tests__desc[${test_num}]} + kv="${tests__kv[${test_num}]}"; v="${kv%,*}"; k="${kv##*,}"; - log 'info' "## Param Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} )"; + log 'info' "## Param Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} )"; test_install_and_use "${v}" "${k}" \ - && log info "## Param Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} ) succeeded" \ - || error_and_proceed "## Param Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} ) failed"; - test_num+=1; + && log info "## Param Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} ) succeeded" \ + || error_and_proceed "## Param Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} ) failed"; done; -test_num=1; -for desc in "${!string_tests[@]}"; do +for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!'; - kv="${string_tests[${desc}]}"; + desc=${tests__desc[${test_num}]} + kv="${tests__kv[${test_num}]}"; v="${kv%,*}"; k="${kv##*,}"; - log 'info' "## ./.terraform-version Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} )"; + log 'info' "## ./.terraform-version Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} )"; log 'info' "Writing ${k} to ./.terraform-version"; echo "${k}" > ./.terraform-version; test_install_and_use "${v}" \ - && log info "## ./.terraform-version Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} ) succeeded" \ - || error_and_proceed "## ./.terraform-version Test ${test_num}/${#string_tests[*]}: ${desc} ( ${k} / ${v} ) failed"; - test_num+=1; + && log info "## ./.terraform-version Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} ) succeeded" \ + || error_and_proceed "## ./.terraform-version Test ${test_num}/${tests_count}: ${desc} ( ${k} / ${v} ) failed"; done; cleanup || log 'error' 'Cleanup failed?!'; @@ -158,20 +168,24 @@ log 'info' 'Install invalid specific version'; cleanup || log 'error' 'Cleanup failed?!'; -declare -A neg_tests=(); -neg_tests['specific version']="9.9.9"; -neg_tests['latest:word']="latest:word"; +neg_tests__desc=( + 'specific version' + 'latest:word' +); +neg_tests__kv=( + '9.9.9' + "latest:word" +); +neg_tests_count=${#neg_tests__desc[@]} -test_num=1; - -for desc in "${!neg_tests[@]}"; do +for ((test_num=0; test_num<${neg_tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!'; - k="${neg_tests[${desc}]}"; + desc=${neg_tests__desc[${test_num}]} + k="${neg_tests__kv[${test_num}]}"; expected_error_message="No versions matching '${k}' found in remote"; - log 'info' "## Invalid Version Test ${test_num}/${#neg_tests[*]}: ${desc} ( ${k} )"; + log 'info' "## Invalid Version Test ${test_num}/${neg_tests_count}: ${desc} ( ${k} )"; [ -z "$(tfenv install "${k}" 2>&1 | grep "${expected_error_message}")" ] \ && error_and_proceed "Installing invalid version ${k}"; - test_num+=1; done; if [ "${#errors[@]}" -gt 0 ]; then diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index ccaad83..0de2b4e 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -62,19 +62,27 @@ function test_uninstall() { log 'info' '### Test Suite: Uninstall Local Versions' cleanup || log 'error' 'Cleanup failed?!'; -declare -A tests; -tests['0.9.1']='0.9.1'; -tests['0.11.15-oci']='0.11.15-oci'; -tests['latest']="$(tfenv list-remote | head -n1)"; -tests['latest:^0.8']="$(tfenv list-remote | grep -e "^0.8" | head -n1)"; +tests__keywords=( + '0.9.1' + '0.11.15-oci' + 'latest' + 'latest:^0.8' +) +tests__versions=( + '0.9.1' + '0.11.15-oci' + "$(tfenv list-remote | head -n1)" + "$(tfenv list-remote | grep -e "^0.8" | head -n1)" +) +tests_count=${#tests__keywords[@]} -declare -i test_num=1; -for k in "${!tests[@]}"; do - log 'info' "Test ${test_num}/${#tests[@]}: Testing uninstall of version ${tests[${k}]} via keyword ${k}"; - test_uninstall "${k}" "${tests[${k}]}" \ - && log info "Test uninstall of version ${tests[${k}]} succeeded" \ - || error_and_proceed "Test uninstall of version ${tests[${k}]} failed"; - test_num+=1; +for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do + keyword=${tests__keywords[${test_num}]} + version=${tests__versions[${test_num}]} + log 'info' "Test $(( ${test_num} + 1 ))/${tests_count}: Testing uninstall of version ${version} via keyword ${keyword}"; + test_uninstall "${keyword}" "${version}" \ + && log info "Test uninstall of version ${version} (via ${keyword}) succeeded" \ + || error_and_proceed "Test uninstall of version ${version} (via ${keyword}) failed"; done; if [ "${#errors[@]}" -gt 0 ]; then From 606b88711bbb8294580872d67d7c8cb1d7c582cf Mon Sep 17 00:00:00 2001 From: Zordrak Date: Tue, 26 May 2020 16:33:18 +0100 Subject: [PATCH 6/7] test_uninstall.sh semicolons and spacing --- test/test_uninstall.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index 0de2b4e..e3dde27 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -57,9 +57,9 @@ function test_uninstall() { tfenv uninstall "${v}" || return 1; log 'info' 'Confirming uninstall success; an error indicates success:'; check_active_version "${v}" && return 1 || return 0; -} +}; -log 'info' '### Test Suite: Uninstall Local Versions' +log 'info' '### Test Suite: Uninstall Local Versions'; cleanup || log 'error' 'Cleanup failed?!'; tests__keywords=( @@ -67,18 +67,20 @@ tests__keywords=( '0.11.15-oci' 'latest' 'latest:^0.8' -) +); + tests__versions=( '0.9.1' '0.11.15-oci' "$(tfenv list-remote | head -n1)" "$(tfenv list-remote | grep -e "^0.8" | head -n1)" -) -tests_count=${#tests__keywords[@]} +); + +tests_count=${#tests__keywords[@]}; for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do - keyword=${tests__keywords[${test_num}]} - version=${tests__versions[${test_num}]} + keyword=${tests__keywords[${test_num}]}; + version=${tests__versions[${test_num}]}; log 'info' "Test $(( ${test_num} + 1 ))/${tests_count}: Testing uninstall of version ${version} via keyword ${keyword}"; test_uninstall "${keyword}" "${version}" \ && log info "Test uninstall of version ${version} (via ${keyword}) succeeded" \ From fccc1549d6257526342642524d5f90a702438dae Mon Sep 17 00:00:00 2001 From: Zordrak Date: Tue, 26 May 2020 16:35:01 +0100 Subject: [PATCH 7/7] test_install_and_use.sh semicolons and spacing --- test/test_install_and_use.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 6eccf22..c66ffab 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -67,11 +67,11 @@ test_install_and_use_overridden() { tfenv use "${k}" || return 1; check_default_version "${v}" || return 1; return 0; -} +}; declare -a errors=(); -log 'info' '### Test Suite: Install and Use' +log 'info' '### Test Suite: Install and Use'; tests__desc=( 'latest version' @@ -84,6 +84,7 @@ tests__desc=( 'latest version matching regex' 'specific version' ); + tests__kv=( "$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1),latest" "$(tfenv list-remote | head -n 1),latest:" @@ -95,13 +96,14 @@ tests__kv=( '0.8.8,latest:^0.8' "0.7.13,0.7.13" ); -tests_count=${#tests__desc[@]} + +tests_count=${#tests__desc[@]}; declare desc kv k v; for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!'; - desc=${tests__desc[${test_num}]} + desc=${tests__desc[${test_num}]}; kv="${tests__kv[${test_num}]}"; v="${kv%,*}"; k="${kv##*,}"; @@ -113,7 +115,7 @@ done; for ((test_num=0; test_num<${tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!'; - desc=${tests__desc[${test_num}]} + desc=${tests__desc[${test_num}]}; kv="${tests__kv[${test_num}]}"; v="${kv%,*}"; k="${kv##*,}"; @@ -167,16 +169,17 @@ fi; log 'info' 'Install invalid specific version'; cleanup || log 'error' 'Cleanup failed?!'; - neg_tests__desc=( 'specific version' 'latest:word' ); + neg_tests__kv=( '9.9.9' "latest:word" ); -neg_tests_count=${#neg_tests__desc[@]} + +neg_tests_count=${#neg_tests__desc[@]}; for ((test_num=0; test_num<${neg_tests_count}; ++test_num )) ; do cleanup || log 'error' 'Cleanup failed?!';