diff --git a/.travis.yml b/.travis.yml index ff93f51..fe9b90d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,21 @@ +before_install: + - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install bash; fi' # Bash4 Required language: generic matrix: include: - os: windows language: shell + - os: linux + dist: bionic + - os: linux + dist: xenial - os: linux dist: trusty - os: osx - osx_image: xcode8.2 # OS X 10.12 - - os: osx - osx_image: xcode8 # OS X 10.11 + osx_image: xcode11.2 - os: osx - osx_image: xcode7 # OS X 10.10 + osx_image: xcode10.3 + #- os: osx + # osx_image: xcode9.4 # brew update is excruciatingly slow on xcode9.4 script: - ./test/run.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 62ab04f..a371754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 2.0.0 (Unreleased) + + * New logging library + * New bash4 dependency + * Massive testing, logging and loading refactoring + * Fix to 'use' logic: don't overwrite .terraform-version files + +## 1.0.2 (October 29, 2019) + + * Fix failing 0.11.15-oci version, Add additional tests for 0.11.15-oci and alphas, betas and rcs #145 + * Fix to README section link for .terraform-version file #146 + ## 1.0.1 (June 22, 2019) * Fix '--version' flag to return version from CHANGELOG.md when not running from a git checkout. diff --git a/README.md b/README.md index 88588ed..ac54cbe 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,23 @@ [![Build Status](https://travis-ci.com/tfutils/tfenv.svg?branch=master)](https://travis-ci.com/tfutils/tfenv) # tfenv + [Terraform](https://www.terraform.io/) version manager inspired by [rbenv](https://github.com/rbenv/rbenv) ## Support + Currently tfenv supports the following OSes + - Mac OS X (64bit) - Linux - - 64bit - - Arm -- Windows (64bit) - only tested in git-bash + - 64bit + - Arm +- Windows (64bit) - only tested in git-bash - currently presumed failing due to symlink issues in git-bash ## Installation + ### Automatic + Install via Homebrew ```console @@ -28,6 +33,7 @@ include ::tfenv ``` ### Manual + 1. Check out tfenv into any path (here is `${HOME}/.tfenv`) ```console @@ -45,21 +51,23 @@ include ::tfenv ```console $ ln -s ~/.tfenv/bin/* /usr/local/bin ``` - + On Ubuntu/Debian touching `/usr/local/bin` might require sudo access, but you can create `${HOME}/bin` or `${HOME}/.local/bin` and on next login it will get added to the session `$PATH` or by running `. ${HOME}/.profile` it will get added to the current shell session's `$PATH`. - + ```console $ mkdir -p ~/.local/bin/ $ . ~/.profile $ ln -s ~/.tfenv/bin/* ~/.local/bin $ which tfenv ``` - ## Usage + ### tfenv install [version] + Install a specific version of Terraform. Available options for version: + - `i.j.k` exact version to install - `latest` is a syntax to install latest version - `latest:` is a syntax to install latest version matching regex (used by grep -e) @@ -91,11 +99,13 @@ see web-of-trust status; beware that a lack of trust path will not cause a validation failure. #### .terraform-version -If you use [.terraform-version](#terraform-version), `tfenv install` (no argument) will install the version written in it. + +If you use a [.terraform-version file](#terraform-version-file), `tfenv install` (no argument) will install the version written in it. #### min-required 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. + ```terraform // this will detect 0.12.3 terraform { @@ -110,24 +120,167 @@ terraform { } ``` +### Environment Variables + +#### TFENV + +##### `TFENV_ARCH` -### Specify architecture +String (Default: amd64) -Architecture other than the default amd64 can be specified with the `TFENV_ARCH` environment variable +Specify architecture. Architecture other than the default amd64 can be specified with the `TFENV_ARCH` environment variable ```console TFENV_ARCH=arm tfenv install 0.7.9 ``` -### Customize remote +##### `TFENV_CURL_OUTPUT` + +Integer (Default: 2) + +Set the mechanism used for displaying download progress when downloading terraform versions from the remote server. + +* 2: v1 Behaviour: Pass `-#` to curl +* 1: Use curl default +* 0: Pass `-s` to curl -Installing from a remote other than the default https://releases.hashicorp.com can be done by specifying the `TFENV_REMOTE` environment varible +##### `TFENV_DEBUG` + +##### `TFENV_REMOTE` + +String (Default: https://releases.hashicorp.com) + +To install from a remote other than the default ```console TFENV_REMOTE=https://example.jfrog.io/artifactory/hashicorp ``` +#### Bashlog Logging Library + +##### `BASHLOG_COLOURS` + +Integer (Default: 1) + +To disable colouring of console output, set to 0. + + +##### `BASHLOG_DATE_FORMAT` + +String (Default: +%F %T) + +The display format for the date as passed to the `date` binary to generate a datestamp used as a prefix to: + +* `FILE` type log file lines. +* Each console output line when `BASHLOG_EXTRA=1` + +##### `BASHLOG_EXTRA` + +Integer (Default: 0) + +By default, console output from tfenv does not print a date stamp or log severity. + +To enable this functionality, making normal output equivalent to FILE log output, set to 1. + +##### `BASHLOG_FILE` + +Integer (Default: 0) + +Set to 1 to enable plain text logging to file (FILE type logging). + +The default path for log files is defined by /tmp/$(basename $0).log +Each executable logs to its own file. + +e.g. + +```console +BASHLOG_FILE=1 tfenv use latest +``` + +will log to `/tmp/tfenv-use.log` + +##### `BASHLOG_FILE_PATH` + +String (Default: /tmp/$(basename ${0}).log) + +To specify a single file as the target for all FILE type logging regardless of the executing script. + +##### `BASHLOG_I_PROMISE_TO_BE_CAREFUL_CUSTOM_EVAL_PREFIX` + +String (Default: "") + +*BE CAREFUL - MISUSE WILL DESTROY EVERYTHING YOU EVER LOVED* + +This variable allows you to pass a string containing a command that will be executed using `eval` in order to produce a prefix to each console output line, and each FILE type log entry. + +e.g. + +```console +BASHLOG_I_PROMISE_TO_BE_CAREFUL_CUSTOM_EVAL_PREFIX='echo "${$$} "' +``` +will prefix every log line with the calling process' PID. + +##### `BASHLOG_JSON` + +Integer (Default: 0) + +Set to 1 to enable JSON logging to file (JSON type logging). + +The default path for log files is defined by /tmp/$(basename $0).log.json +Each executable logs to its own file. + +e.g. + +```console +BASHLOG_JSON=1 tfenv use latest +``` + +will log in JSON format to `/tmp/tfenv-use.log.json` + +JSON log content: + +`{"timestamp":"","level":"","message":""}` + +##### `BASHLOG_JSON_PATH` + +String (Default: /tmp/$(basename ${0}).log.json) + +To specify a single file as the target for all JSON type logging regardless of the executing script. + +##### `BASHLOG_SYSLOG` + +Integer (Default: 0) + +To log to syslog using the `logger` binary, set this to 1. + +The basic functionality is thus: + +```console +local tag="${BASHLOG_SYSLOG_TAG:-$(basename "${0}")}"; +local facility="${BASHLOG_SYSLOG_FACILITY:-local0}"; +local pid="${$}"; + +logger --id="${pid}" -t "${tag}" -p "${facility}.${severity}" "${syslog_line}" +``` + +##### `BASHLOG_SYSLOG_FACILITY` + +String (Default: local0) + +The syslog facility to specify when using SYSLOG type logging. + +##### `BASHLOG_SYSLOG_TAG` + +String (Default: $(basename $0)) + +The syslog tag to specify when using SYSLOG type logging. + +Defaults to the PID of the calling process. + + + ### tfenv use <version> + Switch a version to use `latest` is a syntax to use the latest installed version @@ -144,9 +297,11 @@ $ tfenv use latest:^0.8 ``` ### tfenv uninstall <version> + Uninstall a specific version of Terraform `latest` is a syntax to uninstall latest version `latest:` is a syntax to uninstall latest version matching regex (used by grep -e) + ```console $ tfenv uninstall 0.7.0 $ tfenv uninstall latest @@ -154,7 +309,9 @@ $ tfenv uninstall latest:^0.8 ``` ### tfenv list + List installed versions + ```console % tfenv list * 0.10.7 (set by /opt/tfenv/version) @@ -169,7 +326,9 @@ List installed versions ``` ### tfenv list-remote + List installable versions + ```console % tfenv list-remote 0.9.0-beta2 @@ -193,8 +352,9 @@ List installable versions ... ``` -## .terraform-version -If you put `.terraform-version` file on your project root, or in your home directory, tfenv detects it and use the version written in it. If the version is `latest` or `latest:`, the latest matching version currently installed will be selected. +## .terraform-version file + +If you put a `.terraform-version` file on your project root, or in your home directory, tfenv detects it and uses the version written in it. If the version is `latest` or `latest:`, the latest matching version currently installed will be selected. ```console $ cat .terraform-version @@ -218,16 +378,19 @@ Terraform v0.8.8 ``` ## Upgrading + ```console $ git --git-dir=~/.tfenv/.git pull ``` ## Uninstalling + ```console $ rm -rf /some/path/to/tfenv ``` ## LICENSE + - [tfenv itself](https://github.com/tfutils/tfenv/blob/master/LICENSE) - [rbenv](https://github.com/rbenv/rbenv/blob/master/LICENSE) - tfenv partially uses rbenv's source code diff --git a/bin/terraform b/bin/terraform index d5df53f..08137ce 100755 --- a/bin/terraform +++ b/bin/terraform @@ -1,6 +1,69 @@ #!/usr/bin/env bash -set -e -[ -n "${TFENV_DEBUG}" ] && set -x +set -uo pipefail; -program="${0##*/}" -exec "$(dirname "$(command -v "${0}")")/tfenv" exec "${@}" +#################################### +# 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 # +##################### + +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 'error' 'This line should not be reachable. Something catastrophic has occurred'; diff --git a/bin/tfenv b/bin/tfenv index 56d8d5b..ccc135d 100755 --- a/bin/tfenv +++ b/bin/tfenv @@ -1,63 +1,112 @@ #!/usr/bin/env bash -set -e -if [ -n "${TFENV_DEBUG}" ]; then - export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] ' - set -x -fi - -# 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})" - file_name="$(basename "${target_file}")" - target_file="$(readlink "${file_name}")" - done - - echo "$(pwd -P)/${file_name}" -} - -if [ -z "${TFENV_ROOT}" ]; then - TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)" +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_ROOT="${TFENV_ROOT%/}" -fi -export TFENV_ROOT -PATH="${TFENV_ROOT}/libexec:${PATH}" -export PATH + [ "${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 arg="${1:-""}"; + +log 'debug' "Setting TFENV_DIR to ${PWD}"; export TFENV_DIR="${PWD}" abort() { - { if [ "${#}" -eq 0 ]; then cat - - else echo "tfenv: ${*}" - fi - } >&2 - exit 1 -} - -command="${1}" -case "${command}" in -"" ) - { tfenv---version - tfenv-help - } | abort - ;; --v | --version ) - exec tfenv---version - ;; --h | --help ) - exec tfenv-help - ;; -* ) - command_path="$(command -v "tfenv-${command}" || true)" - if [ -z "${command_path}" ];then - { echo "no such command '${command}'" - tfenv-help - } | abort - fi - shift 1 - exec "${command_path}" "${@}" - ;; -esac + log 'debug' 'Aborting...'; + { + if [ "${#}" -eq 0 ]; then + cat -; + else + echo "tfenv: ${*}"; + fi; + } >&2; +}; + +log 'debug' "tfenv argument is: ${arg}"; + +case "${arg}" in + "") + log 'debug' 'No argument provided, dumping version and help and aborting'; + { + tfenv---version; + tfenv-help; + } | abort && exit 1; +exit 1; + ;; + -v | --version ) + log 'debug' 'tfenv version requested...'; + exec tfenv---version; + ;; + -h | --help ) + log 'debug' 'tfenv help requested...'; + exec tfenv-help; + ;; + *) + log 'debug' "Long argument provided: ${arg}"; + command_path="$(command -v "tfenv-${arg}" || true)"; + log 'debug' "Resulting command-path: ${command_path}"; + if [ -z "${command_path}" ]; then + { + echo "No such command '${arg}'"; + tfenv-help; + } | abort && exit 1; + fi; + shift 1; + log 'debug' "Exec: \"${command_path}\" \"${@}\""; + exec "${command_path}" "${@}"; + ;; +esac; + +log 'error' 'This line should not be reachable. Something catastrophic has occurred'; diff --git a/lib/bashlog.sh b/lib/bashlog.sh new file mode 100755 index 0000000..52ad6db --- /dev/null +++ b/lib/bashlog.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash + +set -uo pipefail; + +function _log_exception() { + ( + BASHLOG_FILE=0; + BASHLOG_JSON=0; + BASHLOG_SYSLOG=0; + + log 'error' "Logging Exception: ${@}"; + ); +}; +export -f _log_exception; + +function log() { + local date_format="${BASHLOG_DATE_FORMAT:-+%F %T}"; + local date="$(date "${date_format}")"; + local date_s="$(date "+%s")"; + + local file="${BASHLOG_FILE:-0}"; + local file_path="${BASHLOG_FILE_PATH:-/tmp/$(basename "${0}").log}"; + + local json="${BASHLOG_JSON:-0}"; + local json_path="${BASHLOG_JSON_PATH:-/tmp/$(basename "${0}").log.json}"; + + local syslog="${BASHLOG_SYSLOG:-0}"; + local tag="${BASHLOG_SYSLOG_TAG:-$(basename "${0}")}"; + local facility="${BASHLOG_SYSLOG_FACILITY:-local0}"; + local pid="${$}"; + + local level="${1}"; + local upper="$(echo "${level}" | awk '{print toupper($0)}')"; + local debug_level="${DEBUG:-0}"; + local stdout_colours="${BASHLOG_COLOURS:-1}"; + local stdout_extra="${BASHLOG_EXTRA:-0}"; + + local custom_eval_prefix="${BASHLOG_I_PROMISE_TO_BE_CAREFUL_CUSTOM_EVAL_PREFIX:-""}"; + + shift 1; + + local line="${@}"; + + # RFC 5424 + # + # Numerical Severity + # Code + # + # 0 Emergency: system is unusable + # 1 Alert: action must be taken immediately + # 2 Critical: critical conditions + # 3 Error: error conditions + # 4 Warning: warning conditions + # 5 Notice: normal but significant condition + # 6 Informational: informational messages + # 7 Debug: debug-level messages + + local severities_DEBUG=7; + local severities_INFO=6; + local severities_NOTICE=5; # Unused + local severities_WARN=4; + local severities_ERROR=3; + local severities_CRIT=2; # Unused + local severities_ALERT=1; # Unused + local severities_EMERG=0; # Unused + + local severity_var="severities_${upper}" + local severity="${!severity_var:-3}" + + if [ "${debug_level}" -gt 0 ] || [ "${severity}" -lt 7 ]; then + + if [ "${syslog}" -eq 1 ]; then + local syslog_line="${upper}: ${line}"; + + logger \ + --id="${pid}" \ + -t "${tag}" \ + -p "${facility}.${severity}" \ + "${syslog_line}" \ + || _log_exception "logger --id=\"${pid}\" -t \"${tag}\" -p \"${facility}.${severity}\" \"${syslog_line}\""; + fi; + + if [ "${file}" -eq 1 ]; then + local file_line="${date} [${upper}] ${line}"; + + if [ -n "${custom_eval_prefix}" ]; then + file_line="$(eval "${custom_eval_prefix}")${file_line}"; + fi; + + echo -e "${file_line}" >> "${file_path}" \ + || _log_exception "echo -e \"${file_line}\" >> \"${file_path}\""; + fi; + + if [ "${json}" -eq 1 ]; then + local json_line="$(printf '{"timestamp":"%s","level":"%s","message":"%s"}' "${date_s}" "${level}" "${line}")"; + echo -e "${json_line}" >> "${json_path}" \ + || _log_exception "echo -e \"${json_line}\" >> \"${json_path}\""; + fi; + + fi; + + local colours_DEBUG='\033[34m' # Blue + local colours_INFO='\033[32m' # Green + local colours_NOTICE='' # Unused + local colours_WARN='\033[33m' # Yellow + local colours_ERROR='\033[31m' # Red + local colours_CRIT='' # Unused + local colours_ALERT='' # Unused + local colours_EMERG='' # Unused + local colours_DEFAULT='\033[0m' # Default + + local norm="${colours_DEFAULT}"; + local colour_var="colours_${upper}" + local colour="${!colour_var:-\033[31m}"; + + local std_line; + if [ "${debug_level}" -le 1 ]; then + std_line="${line}"; + elif [ "${debug_level}" -ge 2 ]; then + std_line="${0}: ${line}"; + fi; + + if [ "${stdout_extra}" -eq 1 ]; then + std_line="${date} [${upper}] ${std_line}"; + fi; + + if [ -n "${custom_eval_prefix}" ]; then + std_line="$(eval "${custom_eval_prefix}")${std_line}"; + fi; + + if [ "${stdout_colours}" -eq 1 ]; then + std_line="${colour}${std_line}${norm}"; + fi; + + # Standard Output (Pretty) + case "${level}" in + 'info'|'warn') + echo -e "${std_line}"; + ;; + 'debug') + if [ "${debug_level}" -gt 0 ]; then + # We are debugging to STDERR on purpose + # tfenv relies on STDOUT between libexecs to function + echo -e "${std_line}" >&2; + fi; + ;; + 'error') + echo -e "${std_line}" >&2; + if [ "${debug_level}" -gt 1 ]; then + echo -e "Here's a shell for debugging the current environment. 'exit 0' to resume script from here. Non-zero exit code will abort - parent shell will terminate." >&2; + bash || exit "${?}"; + else + exit 1; + fi; + ;; + *) + log 'error' "Undefined log level trying to log: ${@}"; + ;; + esac +}; +export -f log; + +declare prev_cmd="null"; +declare this_cmd="null"; +trap 'prev_cmd=$this_cmd; this_cmd=$BASH_COMMAND' DEBUG \ + && log debug 'DEBUG trap set' \ + || log 'error' 'DEBUG trap failed to set'; + +# This is an option if you want to log every single command executed, +# but it will significantly impact script performance and unit tests will fail + +#trap 'prev_cmd=$this_cmd; this_cmd=$BASH_COMMAND; log debug $this_cmd' DEBUG \ +# && log debug 'DEBUG trap set' \ +# || log 'error' 'DEBUG trap failed to set'; diff --git a/lib/helpers.sh b/lib/helpers.sh new file mode 100755 index 0000000..cfddfba --- /dev/null +++ b/lib/helpers.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -uo pipefail; + +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 [ "${TFENV_DEBUG:-0}" -gt 0 ]; then + [ "${DEBUG:-0}" -gt "${TFENV_DEBUG:-0}" ] || export DEBUG="${TFENV_DEBUG:-0}"; + if [[ "${TFENV_DEBUG}" -gt 2 ]]; then + export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] '; + set -x; + fi; +fi; + +source "${TFENV_ROOT}/lib/bashlog.sh"; + +# Curl wrapper to switch TLS option for each OS +function curlw () { + local TLS_OPT="--tlsv1.2"; + + # Check if curl is 10.12.6 or above + if [[ -n "$(command -v sw_vers 2>/dev/null)" && ("$(sw_vers)" =~ 10\.12\.([6-9]|[0-9]{2}) || "$(sw_vers)" =~ 10\.1[3-9]) ]]; then + TLS_OPT=""; + fi; + + curl ${TLS_OPT} "$@"; +} +export -f curlw; + +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() { + local v="${1}"; + local bin="${TFENV_ROOT}/versions/${v}/terraform"; + [ -n "$(${bin} --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ]; +}; +export -f check_installed_version; + +check_default_version() { + local v="${1}"; + local def="$(cat "${TFENV_ROOT}/version")"; + [ "${def}" == "${v}" ]; +}; +export -f check_default_version; + +cleanup() { + log 'info' 'Performing cleanup'; + local pwd="$(pwd)"; + log 'debug' "Deleting ${pwd}/versions"; + rm -rf ./versions; + log 'debug' "Deleting ${pwd}/.terraform-version"; + rm -rf ./.terraform-version; + log 'debug' "Deleting ${pwd}/min_required.tf"; + rm -rf ./min_required.tf; +}; +export -f cleanup; + +function error_and_proceed() { + errors+=("${1}"); + log 'warn' "Test Failed: ${1}"; +}; +export -f error_and_proceed; + +export TFENV_HELPERS=1; diff --git a/libexec/helpers b/libexec/helpers deleted file mode 100755 index 143fe12..0000000 --- a/libexec/helpers +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -function error_and_die() { - echo -e "tfenv: $(basename ${0}): \033[0;31m[ERROR] ${1}\033[0;39m" >&2 - exit 1 -} - -function warn_and_continue() { - echo -e "tfenv: $(basename ${0}): \033[0;33m[WARN] ${1}\033[0;39m" >&2 -} - -function info() { - echo -e "\033[0;32m[INFO] ${1}\033[0;39m" -} - -# Curl wrapper to switch TLS option for each OS -function curlw () { - local TLS_OPT="--tlsv1.2" - - # Check if curl is 10.12.6 or above - if [[ -n "$(command -v sw_vers 2>/dev/null)" && ("$(sw_vers)" =~ 10\.12\.([6-9]|[0-9]{2}) || "$(sw_vers)" =~ 10\.1[3-9]) ]]; then - TLS_OPT="" - fi - - curl ${TLS_OPT} "$@" -} diff --git a/libexec/tfenv---version b/libexec/tfenv---version index 3337e9f..6bc8e28 100755 --- a/libexec/tfenv---version +++ b/libexec/tfenv---version @@ -9,15 +9,78 @@ # where `num_commits` is the number of commits since `version` was # tagged. -set -e -[ -n "${TFENV_DEBUG}" ] && set -x +set -uo pipefail; -version=$(awk '/^##/{ print $2; exit}' "${TFENV_ROOT}"/CHANGELOG.md) -git_revision="" +#################################### +# Ensure we can execute standalone # +#################################### -if cd "${BASH_SOURCE%/*}" 2>/dev/null && git remote -v 2>/dev/null | grep -q tfenv; then - git_revision="$(git describe --tags HEAD 2>/dev/null || true)" - git_revision="${git_revision#v}" -fi +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -echo "tfenv ${git_revision:-$version}" +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 # +##################### + +log 'debug' 'Scraping tfenv version from CHANGELOG.md'; +version="$(awk '/^##/{ print $2; exit}' "${TFENV_ROOT}/CHANGELOG.md")" \ + && log 'debug' "Found version '${version}' in CHANGELOG.md" \ + || log 'error' 'Failed to scrape version from CHANGELOG.md'; + +git_revision=""; +if cd "$(dirname ${0})" 2>/dev/null && git remote -v 2>/dev/null | grep -q tfenv; then + log 'debug' 'Git configuration found. Overriding CHANGELOG version from git revision...'; + git_revision="$(git describe --tags HEAD 2>/dev/null || true)"; + log 'debug' "Stripping git revision string from ${git_revision}"; + git_revision="${git_revision#v}"; +fi; + +echo "tfenv ${git_revision:-$version}"; + +exit 0; diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index 5720e8b..a1359dc 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -13,10 +13,82 @@ # is equivalent to: # PATH="$TFENV_ROOT/versions/0.7.0/bin:$PATH" terraform plan -set -e -[ -n "${TFENV_DEBUG}" ] && set -x +set -uo pipefail; -export TFENV_VERSION="$(tfenv-version-name)" -TF_BIN_PATH="${TFENV_ROOT}/versions/${TFENV_VERSION}/terraform" -export PATH="${TF_BIN_PATH}:${PATH}" -exec "${TF_BIN_PATH}" "${@}" +#################################### +# 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 # +##################### + +log 'debug' 'Getting version from tfenv-version-name'; +TFENV_VERSION="$(tfenv-version-name)" \ + && log 'debug' "TFENV_VERSION is ${TFENV_VERSION}" \ + || { + # Errors will be logged from tfenv-version name, + # we don't need to trouble STDERR with repeat information here + log 'debug' 'Failed to get version from tfenv-version-name'; + exit 1; + }; +export TFENV_VERSION; + +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} ${@}"; + +exec "${TF_BIN_PATH}" "${@}" \ + || log 'error' "Failed to execute: ${TF_BIN_PATH} ${@}"; + +exit 0; diff --git a/libexec/tfenv-help b/libexec/tfenv-help index 8597459..49239bb 100755 --- a/libexec/tfenv-help +++ b/libexec/tfenv-help @@ -1,8 +1,8 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x +set -uo pipefail; -echo "Usage: tfenv [] +echo 'Usage: tfenv [] Commands: install Install a specific version of Terraform @@ -10,4 +10,6 @@ Commands: uninstall Uninstall a specific version of Terraform list List all installed versions list-remote List all installable versions -" +'; + +exit 0; diff --git a/libexec/tfenv-install b/libexec/tfenv-install index 0aa243b..50efff9 100755 --- a/libexec/tfenv-install +++ b/libexec/tfenv-install @@ -1,78 +1,151 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +set -uo pipefail; -[ "${#}" -gt 1 ] && error_and_die "usage: tfenv install []" +#################################### +# Ensure we can execute standalone # +#################################### -declare version_requested version regex +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -if [ -z "${1}" ]; then - version_file="$(tfenv-version-file)" +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 # +##################### + +[ "${#}" -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 - version_requested="$(cat "${version_file}" || true)" - fi + 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="${1}" -fi + version_requested="${arg}"; +fi; + +log 'debug' "Version Requested: ${version_requested}"; if [[ "${version_requested}" =~ ^min-required$ ]]; then - echo "Detecting minimal required version..." - found_min_required="$(tfenv-min-required)" + log 'info' 'Detecting minimal required version...'; + found_min_required="$(tfenv-min-required)"; if [[ $? -eq 0 ]]; then - echo "Min required version is detected as ${found_min_required}" - version_requested="${found_min_required}" + log 'info' "Min required version is detected as ${found_min_required}"; + version_requested="${found_min_required}"; else - exit 1 - fi -fi + exit 1; + fi; +fi; if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then - version="${version_requested%%\:*}" - regex="${version_requested##*\:}" + version="${version_requested%%\:*}"; + regex="${version_requested##*\:}"; elif [[ "${version_requested}" =~ ^latest$ ]]; then - version="${version_requested}" - regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$" + version="${version_requested}"; + regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$"; else - version="${version_requested}" - regex="^${version_requested}$" -fi + version="${version_requested}"; + regex="^${version_requested}$"; +fi; + +[ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest'; -[ -n "${version}" ] || error_and_die "Version is not specified" -version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)" -[ -n "${version}" ] || error_and_die "No versions matching '${1}' found in remote" +log 'debug' "Processing install for version ${version}, using regex ${regex}"; -dst_path="${TFENV_ROOT}/versions/${version}" +version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)"; +[ -n "${version}" ] || log 'error' "No versions matching '${arg}' found in remote"; + +dst_path="${TFENV_ROOT}/versions/${version}"; if [ -f "${dst_path}/terraform" ]; then - echo "Terraform v${version} is already installed" - exit 0 -fi + echo "Terraform v${version} is already installed"; + exit 0; +fi; -TFENV_ARCH="${TFENV_ARCH:-amd64}" +TFENV_ARCH="${TFENV_ARCH:-amd64}"; case "$(uname -s)" in Darwin*) - os="darwin_${TFENV_ARCH}" + os="darwin_${TFENV_ARCH}"; ;; MINGW64*) - os="windows_${TFENV_ARCH}" + os="windows_${TFENV_ARCH}"; ;; MSYS_NT*) - os="windows_${TFENV_ARCH}" + os="windows_${TFENV_ARCH}"; ;; CYGWIN_NT*) - os="windows_${TFENV_ARCH}" + os="windows_${TFENV_ARCH}"; ;; *) - os="linux_${TFENV_ARCH}" + os="linux_${TFENV_ARCH}"; ;; -esac +esac; -keybase_bin="$(command -v keybase 2>/dev/null)" -shasum_bin="$(command -v shasum 2>/dev/null)" +keybase_bin="$(command -v keybase 2>/dev/null)"; +shasum_bin="$(command -v shasum 2>/dev/null)"; -TFENV_REMOTE="${TFENV_REMOTE:-https://releases.hashicorp.com}" -version_url="${TFENV_REMOTE}/terraform/${version}" +TFENV_REMOTE="${TFENV_REMOTE:-https://releases.hashicorp.com}"; +version_url="${TFENV_REMOTE}/terraform/${version}"; # Thanks for the inconsistency in 0.12-alpha, Hashicorp(!) if [[ "${version}" =~ 0.12.0-alpha[3-9] ]]; then @@ -81,104 +154,130 @@ else tarball_name="terraform_${version}_${os}.zip"; fi; -shasums_name="terraform_${version}_SHA256SUMS" +shasums_name="terraform_${version}_SHA256SUMS"; -info "Installing Terraform v${version}" +log 'info' "Installing Terraform v${version}"; # Create a local temporary directory for downloads -download_tmp="$(mktemp -d tfenv_download.XXXXXX)" || error_and_die "Unable to create temporary download directory in $(pwd)" +download_tmp="$(mktemp -d tfenv_download.XXXXXX)" || log 'error' "Unable to create temporary download directory in $(pwd)"; # Clean it up in case of error trap "rm -rf ${download_tmp}" EXIT; -info "Downloading release tarball from ${version_url}/${tarball_name}" -curlw -# -f -o "${download_tmp}/${tarball_name}" "${version_url}/${tarball_name}" || error_and_die "Tarball download failed" -info "Downloading SHA hash file from ${version_url}/${shasums_name}" -curlw -s -f -o "${download_tmp}/${shasums_name}" "${version_url}/${shasums_name}" || error_and_die "SHA256 hashes download failed" +declare curl_progress=""; +case "${TFENV_CURL_OUTPUT:-2}" in + '2') + log 'debug' 'Setting curl progress bar with "-#"'; + curl_progress="-#"; + ;; + '1') + log 'debug' 'Using default curl output'; + curl_progress=""; + ;; + '0') + log 'debug' 'Running curl silently with "-s"'; + curl_progress="-s"; + ;; + *) + log 'error' 'TFENV_CURL_OUTPUT specified, but not with a support value ([0,1,2])'; + ;; +esac; + +log 'info' "Downloading release tarball from ${version_url}/${tarball_name}"; +curlw ${curl_progress} -f -o "${download_tmp}/${tarball_name}" "${version_url}/${tarball_name}" || log 'error' 'Tarball download failed'; +log 'info' "Downloading SHA hash file from ${version_url}/${shasums_name}"; +curlw -s -f -o "${download_tmp}/${shasums_name}" "${version_url}/${shasums_name}" || log 'error' 'SHA256 hashes download failed'; download_signature() { - info "Downloading SHA hash signature file from ${version_url}/${shasums_name}.sig" + log 'info' "Downloading SHA hash signature file from ${version_url}/${shasums_name}.sig"; curlw -s -f \ -o "${download_tmp}/${shasums_name}.sig" \ "${version_url}/${shasums_name}.sig" \ - || error_and_die "SHA256SUMS signature download failed" -} + && log 'debug' "SHA256SUMS signature file downloaded successfully to ${download_tmp}/${shasums_name}.sig" \ + || log 'error' 'SHA256SUMS signature download failed'; +}; # Verify signature if verification mechanism (keybase, gpg, etc) is present if [[ -n "${keybase_bin}" && -x "${keybase_bin}" ]]; then - "${keybase_bin}" status | grep -Eq '^Logged in:[[:space:]]*yes' - keybase_logged_in="${?}" - "${keybase_bin}" list-following | grep -Fq hashicorp - keybase_following_hc="${?}" + "${keybase_bin}" status | grep -Eq '^Logged in:[[:space:]]*yes'; + keybase_logged_in="${?}"; + "${keybase_bin}" list-following | grep -Fq hashicorp; + keybase_following_hc="${?}"; if [[ "${keybase_logged_in}" -ne 0 || "${keybase_following_hc}" -ne 0 ]]; then - warn_and_continue "Unable to verify OpenPGP signature unless logged into keybase and following hashicorp" + log 'warn' 'Unable to verify OpenPGP signature unless logged into keybase and following hashicorp'; else - download_signature + download_signature; "${keybase_bin}" pgp verify \ -S hashicorp \ -d "${download_tmp}/${shasums_name}.sig" \ -i "${download_tmp}/${shasums_name}" \ - || error_and_die "SHA256SUMS signature does not match!" - fi + && log 'debug' 'SHA256SUMS signature matched' \ + || log 'error' 'SHA256SUMS signature does not match!'; + fi; elif [[ -f "${TFENV_ROOT}/use-gnupg" ]]; then # GnuPG uses the user's keyring, and any web-of-trust or local signatures or # anything else they have setup. This is the crazy-powerful mode which is # overly confusing to newcomers. We don't support it without the user creating # the file use-gnupg, optionally with directives in it. - gnupg_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gnupg")" - [[ -n "${gnupg_command}" ]] || gnupg_command=gpg + gnupg_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gnupg")"; + [[ -n "${gnupg_command}" ]] || gnupg_command=gpg; - download_signature + download_signature; # Deliberately unquoted command, in case caller has something fancier in "use-gnupg". # Also, don't use batch mode. If someone specifies GnuPG, let them deal with any prompting. ${gnupg_command} \ --verify "${download_tmp}/${shasums_name}.sig" \ "${download_tmp}/${shasums_name}" \ - || error_and_die "PGP signature rejected by GnuPG" + || log 'error' 'PGP signature rejected by GnuPG'; elif [[ -f "${TFENV_ROOT}/use-gpgv" ]]; then # gpgv is a much simpler interface to verification, but does require that the # key have been downloaded and marked trusted. # We don't force the caller to trust the tfenv repo's copy of their key, they # have to choose to make that trust decision. - gpgv_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gpgv")" - trust_tfenv="$(sed -E -n -e 's/^trust.?tfenv: *//p' <"${TFENV_ROOT}/use-gpgv")" - [[ -n "${gpgv_command}" ]] || gpgv_command=gpgv + gpgv_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gpgv")"; + trust_tfenv="$(sed -E -n -e 's/^trust.?tfenv: *//p' <"${TFENV_ROOT}/use-gpgv")"; + [[ -n "${gpgv_command}" ]] || gpgv_command=gpgv; - download_signature - if [[ "${trust_tfenv}" == "yes" ]]; then + download_signature; + if [[ "${trust_tfenv}" == 'yes' ]]; then ${gpgv_command} \ --keyring "${TFENV_ROOT}/share/hashicorp-keys.pgp" \ "${download_tmp}/${shasums_name}.sig" \ "${download_tmp}/${shasums_name}" \ - || error_and_die "PGP signature rejected" + || log 'error' 'PGP signature rejected'; else ${gpgv_command} \ "${download_tmp}/${shasums_name}.sig" \ "${download_tmp}/${shasums_name}" \ - || error_and_die "PGP signature rejected" - fi + || log 'error' 'PGP signature rejected'; + fi; else # Warning about this avoids an unwarranted sense of confidence in the SHA check - warn_and_continue "No keybase install found, skipping OpenPGP signature verification" -fi + log 'warn' 'No keybase install found, skipping OpenPGP signature verification'; +fi; if [[ -n "${shasum_bin}" && -x "${shasum_bin}" ]]; then ( - cd "${download_tmp}" + cd "${download_tmp}"; "${shasum_bin}" \ -a 256 \ -s \ - -c <(grep -F "${tarball_name}" "${shasums_name}") || error_and_die "SHA256 hash does not match!" - ) + -c <(grep -F "${tarball_name}" "${shasums_name}") \ + || log 'error' 'SHA256 hash does not match!'; + ); else # Lack of shasum deserves a proper warning - warn_and_continue "No shasum tool available. Skipping SHA256 hash validation" -fi + log 'warn' 'No shasum tool available. Skipping SHA256 hash validation'; +fi; + +mkdir -p "${dst_path}" || log 'error' "Failed to make directory ${dst_path}"; -mkdir -p "${dst_path}" || error_and_die "Failed to make directory ${dst_path}" -unzip "${download_tmp}/${tarball_name}" -d "${dst_path}" || error_and_die "Tarball unzip failed" +declare unzip_output="$(unzip "${download_tmp}/${tarball_name}" -d "${dst_path}" || log 'error' 'Tarball unzip failed')"; +while IFS= read -r unzip_line; do + log 'info' "${unzip_line}"; +done < <(printf '%s\n' "${unzip_output}"); -info "Installation of terraform v${version} successful" -tfenv-use "${version}" +log 'info' "Installation of terraform v${version} successful"; +tfenv-use "${version}"; diff --git a/libexec/tfenv-list b/libexec/tfenv-list index 5d0de94..b8e232a 100755 --- a/libexec/tfenv-list +++ b/libexec/tfenv-list @@ -1,25 +1,98 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +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 # +##################### [ "${#}" -ne 0 ] \ - && error_and_die "usage: tfenv list" + && log 'error' "usage: tfenv list" [ -d "${TFENV_ROOT}/versions" ] \ - || error_and_die "No versions available. Please install one with: tfenv install" + || log 'error' 'No versions available. Please install one with: tfenv install' [[ -x "${TFENV_ROOT}/versions" && -r "${TFENV_ROOT}/versions" ]] \ - || error_and_die "tfenv versions directory is inaccessible!" + || log 'error' "tfenv versions directory is inaccessible: ${TFENV_ROOT}/versions"; + +version_name="$(tfenv-version-name)" \ + && log 'debug' "tfenv-version-name reported: ${version_name}" \ + || log 'error' "tfenv-version-name failed"; +export version_name; + +export version_file="$(tfenv-version-file)" \ + && log 'debug' "tfenv-version-file reported: ${version_file}" \ + || log 'error' "tfenv-version-file failed"; +export version_file; print_version () { - if [ "${1}" == "$(tfenv-version-name)" ]; then - echo "* ${1} (set by $(tfenv-version-file))" - else - echo " ${1}" - fi -} - -for local_version in $(ls -1 "${TFENV_ROOT}/versions" | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3); do - print_version "${local_version}" -done + if [ "${1}" == "${version_name}" ]; then + echo "* ${1} (set by ${version_file})"; + else + echo " ${1}"; + fi; +}; + +log 'debug' 'Listing versions...'; +local_versions=($(ls -1 "${TFENV_ROOT}/versions" \ + | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3)); + +log 'debug' "Local versions: ${local_versions[@]}"; + +log 'debug' 'Printing versions...'; +for local_version in ${local_versions[@]}; do + print_version "${local_version}"; +done; diff --git a/libexec/tfenv-list-remote b/libexec/tfenv-list-remote index 394faa9..8b04997 100755 --- a/libexec/tfenv-list-remote +++ b/libexec/tfenv-list-remote @@ -1,14 +1,73 @@ #!/usr/bin/env bash -set -e -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" + +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 # +##################### if [ "${#}" -ne 0 ];then echo "usage: tfenv list-remote" 1>&2 - exit 1 + exit 1; fi TFENV_REMOTE="${TFENV_REMOTE:-https://releases.hashicorp.com}" +log 'debug' "TFENV_REMOTE: ${TFENV_REMOTE}"; +declare remote_versions="$(curlw -sf "${TFENV_REMOTE}/terraform/")"; +#log 'debug' "Remote versions available: ${remote_versions}"; # Even in debug mode this is too verbose curlw -sf "${TFENV_REMOTE}/terraform/" \ - | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha)[0-9]+)?" \ - | uniq + | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha|oci)[0-9]*)?" \ + | uniq; diff --git a/libexec/tfenv-min-required b/libexec/tfenv-min-required index 68cb7ce..b886e93 100755 --- a/libexec/tfenv-min-required +++ b/libexec/tfenv-min-required @@ -1,42 +1,96 @@ #!/usr/bin/env bash # Usage: tfenv min-required # Summary: Detect the minimal required version from *tf files -set -e -[ -n "${TFENV_DEBUG}" ] && set -x + +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 # +##################### bailout() { - >&2 echo "Error: Could not determine required_version based on your terraform sources. + log 'error' 'Error: Could not determine required_version based on your terraform sources. Make sure at least one of your *tf files includes a required version section like terraform { - required_version = \">= 0.0.0\" + required_version = ">= 0.0.0" } -see https://www.terraform.io/docs/configuration/terraform.html for details - -" - exit 1 -} +see https://www.terraform.io/docs/configuration/terraform.html for details'; +}; find_min_required() { - local root="${1}" + local root="${1}"; - versions="$(grep -h -R required_version --include '*tf' "$root"/* | tr -c -d '0-9. ~=!<>' )" + versions="$(grep -h -R required_version --include '*tf' "${root}"/* | tr -c -d '0-9. ~=!<>' )"; if [[ "${versions}" =~ ([~=!<>]{0,2}[[:blank:]]*[0-9]+[0-9.]+)[^0-9]* ]]; then - found_min_required="${BASH_REMATCH[1]}" + found_min_required="${BASH_REMATCH[1]}"; if [[ "${found_min_required}" =~ ^!=.+ ]]; then - echo "Error: Min required version is a negation ($found_min_required) - we cannot guess the desired one." - bailout + 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" | tr -c -d '0-9.')" - #echo "Min required version is detected as ${found_min_required}" - echo "${found_min_required}" - exit 0 - fi - fi - - bailout -} + found_min_required="$(echo "$found_min_required" | tr -c -d '0-9.')"; + #echo "Min required version is detected as ${found_min_required}"; + echo "${found_min_required}"; + exit 0; + fi; + fi; + + bailout; +}; -find_min_required "${TFENV_DIR}" +find_min_required "${TFENV_DIR:-$(pwd)}"; diff --git a/libexec/tfenv-uninstall b/libexec/tfenv-uninstall index 7e679fc..19a506e 100755 --- a/libexec/tfenv-uninstall +++ b/libexec/tfenv-uninstall @@ -1,39 +1,116 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +set -uo pipefail; -[ "${#}" -gt 1 ] && error_and_die "usage: tfenv uninstall []" +#################################### +# Ensure we can execute standalone # +#################################### -declare version_requested version regex +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -if [ -z "${1}" ]; then - version_file="$(tfenv-version-file)" - if [ "${version_file}" != "${TFENV_ROOT}/version" ];then - version_requested="$(cat "${version_file}" || true)" - fi +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 - version_requested="${1}" -fi + [ "${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 # +##################### + +[ "${#}" -gt 1 ] && log 'error' 'usage: tfenv uninstall []'; + +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}"; + +if [[ "${version_requested}" =~ ^min-required$ ]]; then + log 'error' 'min-required is an unsupported option for uninstall'; +fi; if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then - version="${version_requested%%\:*}" - regex="${version_requested##*\:}" + version="${version_requested%%\:*}"; + regex="${version_requested##*\:}"; elif [[ "${version_requested}" =~ ^latest$ ]]; then - version="${version_requested}" - regex="" + version="${version_requested}"; + regex=""; else - version="${version_requested}" - regex="^${version_requested}$" -fi + version="${version_requested}"; + regex="^${version_requested}$"; +fi; + +[ -z "${version:-""}" ] && log 'error' "Version not specified on the command line or on version file search path."; + +log 'debug' "Processing uninstall for version ${version}, using regex ${regex}"; -[ -n "${version}" ] || error_and_die "Version is not specified" -version="$(tfenv-list | sed -E 's/^(\*| )? //g; s/ \(set by .+\)$//' | grep -e "${regex}" | head -n 1)" -[ -n "${version}" ] || error_and_die "No versions matching '${1}' found in local" +version="$(tfenv-list | sed -E 's/^(\*| )? //g; s/ \(set by .+\)$//' | grep -e "${regex}" | head -n 1)"; +[ -n "${version}" ] || log 'error' "No versions matching '${regex}' found in local"; -dst_path="${TFENV_ROOT}/versions/${version}" +dst_path="${TFENV_ROOT}/versions/${version}"; if [ -f "${dst_path}/terraform" ]; then - info "Uninstall Terraform v${version}" - rm -r "${dst_path}" - info "\033[0;32mTerraform v${version} is successfully uninstalled\033[0;39m" -fi + log 'info' "Uninstall Terraform v${version}"; + rm -r "${dst_path}"; + log 'info' "Terraform v${version} is successfully uninstalled"; +fi; diff --git a/libexec/tfenv-use b/libexec/tfenv-use index 53296f1..d701f28 100755 --- a/libexec/tfenv-use +++ b/libexec/tfenv-use @@ -1,55 +1,123 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +set -uo pipefail; -[ "${#}" -ne 1 ] && error_and_die "usage: tfenv use " +#################################### +# Ensure we can execute standalone # +#################################### -declare version_requested version regex +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -version_requested="${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; -if [[ "${version_requested}" =~ ^min-required$ ]]; then - echo "Detecting minimal required version..." - found_min_required="$(tfenv-min-required)" + 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 [[ $? -eq 0 ]]; then - echo "Min required version is detected as ${found_min_required}" - version_requested="${found_min_required}" +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 - exit 1 - fi -fi + 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 # +##################### + +[ "${#}" -ne 1 ] && log 'error' 'usage: tfenv use '; + +declare version_requested version regex min_required version_file; + +version_requested="${1}"; + +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##*\:}" + 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]\+$" + 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}$" -fi + version="${version_requested}"; + regex="^${version_requested}$"; + log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}"; +fi; [ -d "${TFENV_ROOT}/versions" ] \ - || error_and_die "No versions of terraform installed. Please install one with: tfenv install" + || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; +log 'debug' "Searching ${TFENV_ROOT}/versions for latest version matching ${regex}"; version="$(\ls "${TFENV_ROOT}/versions" \ | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ | grep -e "${regex}" \ | head -n 1 -)" +)"; -[ -n "${version}" ] || error_and_die "No installed versions of terraform matched '${1}'" +[ -n "${version}" ] \ + && log 'debug' "Found version: ${version}" \ + || log 'error' "No installed versions of terraform matched '${1}'"; -target_path="${TFENV_ROOT}/versions/${version}" +target_path="${TFENV_ROOT}/versions/${version}"; [ -f "${target_path}/terraform" ] \ - || error_and_die "Version directory for ${version} is present, but the terraform binary is not! Manual intervention required." + || log 'error' "Version directory for ${version} is present, but the terraform binary is not! Manual intervention required."; [ -x "${target_path}/terraform" ] \ - || error_and_die "Version directory for ${version} is present, but the terraform binary is not executable! Manual intervention required. " + || log 'error' "Version directory for ${version} is present, but the terraform binary is not executable! Manual intervention required."; + +log 'info' "Switching default version to v${version}"; +version_file="${TFENV_ROOT}/version"; +log 'debug' "Writing \"${version}\" to \"${version_file}\""; +echo "${version}" > "${version_file}" \ + || log 'error' "Switch to v${version} failed"; + +if [ "${version_file}" != "$(tfenv-version-file)" ]; then + log 'warn' "Default version file overridden by $(tfenv-version-file), changing the default version has no effect"; +fi; -info "Switching to v${version}" -echo "${version}" > "$(tfenv-version-file)" || error_and_die "'switch to v${version} failed'" -terraform --version 1>/dev/null || error_and_die "'terraform --version' failed. Something is seriously wrong" -info "Switching completed" +terraform --version 1>/dev/null \ + || log 'error' "'terraform --version' failed. Something is seriously wrong"; +log 'info' "Switching completed"; diff --git a/libexec/tfenv-version-file b/libexec/tfenv-version-file index 85ec814..7dfb976 100755 --- a/libexec/tfenv-version-file +++ b/libexec/tfenv-version-file @@ -1,20 +1,87 @@ #!/usr/bin/env bash # Usage: tfenv version-file # Summary: Detect the file that sets the current tfenv version -set -e -[ -n "${TFENV_DEBUG}" ] && set -x + +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 # +##################### find_local_version_file() { - local root="${1}" + log 'debug' "Looking for a version file in ${1}"; + local root="${1}"; while ! [[ "${root}" =~ ^//[^/]*$ ]]; do if [ -e "${root}/.terraform-version" ]; then - echo "${root}/.terraform-version" - return 0 - fi - [ -n "${root}" ] || break - root="${root%/*}" - done - return 1 + 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; } -find_local_version_file "${TFENV_DIR}" || find_local_version_file "${HOME}" || echo "${TFENV_ROOT}/version" +if ! find_local_version_file "${TFENV_DIR:-${PWD}}"; 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; +fi; diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index 25a122a..64bb7c0 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -1,37 +1,107 @@ #!/usr/bin/env bash -# Summary: Show the current Terraform version -set -e +# Summary: Show the currently-selected terraform version -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +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 # +##################### [ -d "${TFENV_ROOT}/versions" ] \ - || error_and_die "No versions of terraform installed. Please install one with: tfenv install" + || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; + +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'; -TFENV_VERSION_FILE="$(tfenv-version-file)" -TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" +TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \ + && log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}"; if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then - [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]] && regex="${TFENV_VERSION##*\:}" + log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}"; + + if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then + regex="${TFENV_VERSION##*\:}"; + log 'debug' "'latest' keyword uses regex: ${regex}"; + else + regex='.*'; # Just saves a seperate command below without the grep + log 'debug' "'latest' keyword does not use regex"; + fi; + version="$(\ls "${TFENV_ROOT}/versions" \ | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ | grep -e "${regex}" \ - | head -n 1 - )" - [ -n "${version}" ] || error_and_die "No installed versions of terraform matched '${TFENV_VERSION}'" - TFENV_VERSION="${version}" -fi + | head -n 1)"; -[ -z "${TFENV_VERSION}" ] \ - && error_and_die "Version could not be resolved (set by ${TFENV_VERSION_FILE} or tfenv use )" + if [ -n "${version}" ]; then + log 'debug' "Version selected: ${version}"; + TFENV_VERSION="${version}" + else + log 'error' "No installed versions of terraform matched '${TFENV_VERSION}'"; + fi; + +else + log 'debug' 'TFENV_VERSION does not use "latest" keyword'; +fi; -version_exists() { - local version="${1}" - [ -d "${TFENV_ROOT}/versions/${version}" ] -} +[ -z "${TFENV_VERSION}" ] \ + && log 'error' "Version could not be resolved (set by ${TFENV_VERSION_FILE} or tfenv use )"; -if version_exists "${TFENV_VERSION}"; then - echo "${TFENV_VERSION}" +if [ -d "${TFENV_ROOT}/versions/${TFENV_VERSION}" ]; then + echo "${TFENV_VERSION}"; else - error_and_die "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})" -fi + log 'error' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_FILE})"; +fi; diff --git a/test/helpers.sh b/test/helpers.sh deleted file mode 100755 index 61ccaca..0000000 --- a/test/helpers.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -check_version() { - v="${1}" - [ -n "$(terraform --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ] -} - -cleanup() { - rm -rf ./versions - rm -rf ./.terraform-version - rm -rf ./min_required.tf -} diff --git a/test/run.sh b/test/run.sh index 3ad2fb6..6abb640 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,30 +1,75 @@ #!/usr/bin/env bash -if [ -n "${TFENV_DEBUG}" ]; then - export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] ' - set -x -fi -TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)" -export PATH="${TFENV_ROOT}/bin:${PATH}" - -errors=() -if [ "${#}" -ne 0 ];then - targets="${@}" +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 - targets="$(\ls "$(dirname "${0}")" | grep 'test_')" -fi + 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 # +##################### + +export PATH="${TFENV_ROOT}/bin:${PATH}"; + +errors=(); +if [ "${#}" -ne 0 ]; then + targets="${@}"; +else + targets="$(\ls "$(dirname "${0}")" | grep 'test_')"; +fi; for t in ${targets}; do - bash "$(dirname "${0}")/${t}" || errors+=( "${t}" ) -done + bash "$(dirname "${0}")/${t}" \ + || errors+=( "${t}" ); +done; -if [ "${#errors[@]}" -ne 0 ];then - echo -e "\033[0;31m===== The following test suites failed =====\033[0;39m" >&2 +if [ "${#errors[@]}" -ne 0 ]; then + log 'warn' '===== The following test suites failed ====='; for error in "${errors[@]}"; do - echo -e "\t${error}" >&2 - done - exit 1 + log 'warn' "\t${error}"; + done; + log 'error' 'Test suite failure(s)'; else - echo -e "\033[0;32mAll test suites passed.\033[0;39m" -fi -exit 0 + log 'info' 'All test suites passed.'; +fi; + +exit 0; diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 4a34fb2..8bea949 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -1,132 +1,183 @@ #!/usr/bin/env bash -declare -a errors +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; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} +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; -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 +##################### +# Begin Script Body # +##################### + +test_install_and_use() { + # Takes a static version and the optional keyword to install it with + local k="${2-""}"; + local v="${1}"; + tfenv install "${k}" || return 1; + check_installed_version "${v}" || return 1; + check_active_version "${v}" || return 1; + return 0; +}; + +test_install_and_use_overridden() { + # Takes a static version and the optional keyword to install it with + local k="${2-""}"; + local v="${1}"; + tfenv install "${k}" || return 1; + check_installed_version "${v}" || return 1; + check_default_version "${v}" || return 1; + return 0; } -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname ${0})/helpers.sh" - -echo "### Install latest version" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1)" -( - tfenv install latest || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest version ${v}" - -echo "### Install latest possibly-unstable version" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | head -n 1)" -( - tfenv install latest: || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest possibly-unstable version ${v}" - -echo "### Install latest version with Regex" -cleanup || error_and_die "Cleanup failed?!" - -v="0.8.8" -( - tfenv install latest:^0.8 || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest version ${v} with Regex" - -echo "### Install specific version" -cleanup || error_and_die "Cleanup failed?!" - -v="0.7.13" -( - tfenv install "${v}" || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing specific version ${v}" - -echo "### Install specific .terraform-version" -cleanup || error_and_die "Cleanup failed?!" - -v="0.9.1" -echo "${v}" > ./.terraform-version -( - tfenv install || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing .terraform-version ${v}" - -echo "### Install latest: .terraform-version" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep -e '^0.8' | head -n 1)" -echo "latest:^0.8" > ./.terraform-version -( - tfenv install || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing .terraform-version ${v}" - -echo "### Install with ${HOME}/.terraform-version" -cleanup || error_and_die "Cleanup failed?!" - +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 + cleanup || log 'error' 'Cleanup failed?!'; + kv="${string_tests[${desc}]}"; + v="${kv%,*}"; + k="${kv##*,}"; + log 'info' "## Param Test ${test_num}/${#string_tests[*]}: ${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; +done; + +test_num=1; +for desc in "${!string_tests[@]}"; do + cleanup || log 'error' 'Cleanup failed?!'; + kv="${string_tests[${desc}]}"; + v="${kv%,*}"; + k="${kv##*,}"; + log 'info' "## ./.terraform-version Test ${test_num}/${#string_tests[*]}: ${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; +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)"; if [ -f "${HOME}/.terraform-version" ]; then - mv "${HOME}/.terraform-version" "${HOME}/.terraform-version.bup" -fi -v="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 2 | tail -n 1)" -echo "${v}" > "${HOME}/.terraform-version" -( - tfenv install || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing ${HOME}/.terraform-version ${v}" + log 'info' "Backing up ${HOME}/.terraform-version to ${HOME}/.terraform-version.bup"; + mv "${HOME}/.terraform-version" "${HOME}/.terraform-version.bup"; +fi; +log 'info' "Writing ${v1} to ${HOME}/.terraform-version"; +echo "${v1}" > "${HOME}/.terraform-version"; -echo "### Install with parameter and use ~/.terraform-version" -v="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 1)" -( - tfenv install "${v}" || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Use ${HOME}/.terraform-version ${v}" +log 'info' "## \${HOME}/.terraform-version Test 1/3: Install and Use ( ${v1} )"; +test_install_and_use "${v1}" \ + && log info "## \${HOME}/.terraform-version Test 1/1: ( ${v1} ) succeeded" \ + || error_and_proceed "## \${HOME}/.terraform-version Test 1/1: ( ${v1} ) failed"; -echo "### Use with parameter and ~/.terraform-version" -v="$(tfenv list-remote | grep -e "^[0-9]\+\.[0-9]\+\.[0-9]\+$" | head -n 2 | tail -n 1)" -( - tfenv use "${v}" || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Use ${HOME}/.terraform-version ${v}" +log 'info' "## \${HOME}/.terraform-version Test 2/3: Override Install with Parameter ( ${v2} )"; +test_install_and_use_overridden "${v2}" "${v2}" \ + && log info "## \${HOME}/.terraform-version Test 2/3: ( ${v2} ) succeeded" \ + || error_and_proceed "## \${HOME}/.terraform-version Test 2/3: ( ${v2} ) failed"; -rm "${HOME}/.terraform-version" +log 'info' "## \${HOME}/.terraform-version Test 3/3: Override Use with Parameter ( ${v2} )"; +( + tfenv use "${v2}" || exit 1; + check_default_version "${v2}" || exit 1; +) && log info "## \${HOME}/.terraform-version Test 3/3: ( ${v2} ) succeeded" \ + || error_and_proceed "## \${HOME}/.terraform-version Test 3/3: ( ${v2} ) failed"; + +log 'info' '## \${HOME}/.terraform-version Test Cleanup'; +log 'info' "Deleting ${HOME}/.terraform-version"; +rm "${HOME}/.terraform-version"; if [ -f "${HOME}/.terraform-version.bup" ]; then - mv "${HOME}/.terraform-version.bup" "${HOME}/.terraform-version" -fi + log 'info' "Restoring backup from ${HOME}/.terraform-version.bup to ${HOME}/.terraform-version"; + mv "${HOME}/.terraform-version.bup" "${HOME}/.terraform-version"; +fi; -echo "### Install invalid specific version" -cleanup || error_and_die "Cleanup failed?!" +log 'info' 'Install invalid specific version'; +cleanup || log 'error' 'Cleanup failed?!'; -v="9.9.9" -expected_error_message="No versions matching '${v}' found in remote" -[ -z "$(tfenv install "${v}" 2>&1 | grep "${expected_error_message}")" ] \ - && error_and_proceed "Installing invalid version ${v}" -echo "### Install invalid latest: version" -cleanup || error_and_die "Cleanup failed?!" +declare -A neg_tests=(); +neg_tests['specific version']="9.9.9"; +neg_tests['latest:word']="latest:word"; -v="latest:word" -expected_error_message="No versions matching '${v}' found in remote" -[ -z "$(tfenv install "${v}" 2>&1 | grep "${expected_error_message}")" ] \ - && error_and_proceed "Installing invalid version ${v}" +test_num=1; + +for desc in "${!neg_tests[@]}"; do + cleanup || log 'error' 'Cleanup failed?!'; + k="${neg_tests[${desc}]}"; + expected_error_message="No versions matching '${k}' found in remote"; + log 'info' "## Invalid Version Test ${test_num}/${#neg_tests[*]}: ${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 - echo -e "\033[0;31m===== The following install_and_use tests failed =====\033[0;39m" >&2 + log 'warn' '===== The following install_and_use tests failed ====='; for error in "${errors[@]}"; do - echo -e "\t${error}" + log 'warn' "\t${error}"; done - exit 1 + log 'error' 'Test failure(s): install_and_use'; else - echo -e "\033[0;32mAll install_and_use tests passed.\033[0;39m" + log 'info' 'All install_and_use tests passed'; fi; -exit 0 + +exit 0; diff --git a/test/test_list.sh b/test/test_list.sh index 27c24be..365555f 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -1,29 +1,67 @@ #!/usr/bin/env bash -declare -a errors +set -uo pipefail; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} +#################################### +# Ensure we can execute standalone # +#################################### -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 -} +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +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; -echo "### List local versions" -cleanup || error_and_die "Cleanup failed?!" + 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=(); + +log 'info' '### List local versions'; +cleanup || log 'error' "Cleanup failed?!"; for v in 0.7.2 0.7.13 0.9.1 0.9.2 0.9.11; do - tfenv install "${v}" || error_and_proceed "Install of version ${v} failed" -done + log 'info' "## Installing version ${v} to construct list"; + tfenv install "${v}" \ + && log 'debug' "Install of version ${v} succeeded" \ + || error_and_proceed "Install of version ${v} failed"; +done; -result="$(tfenv list)" +log 'info' '## Comparing "tfenv list" to expectations'; +result="$(tfenv list)"; expected="$(cat << EOS * 0.9.11 (set by $(tfenv version-file)) 0.9.2 @@ -31,19 +69,20 @@ expected="$(cat << EOS 0.7.13 0.7.2 EOS -)" +)"; if [ "${expected}" != "${result}" ]; then - error_and_proceed "List mismatch.\nExpected:\n${expected}\nGot:\n${result}" -fi + error_and_proceed "List mismatch.\nExpected:\n${expected}\nGot:\n${result}"; +fi; if [ "${#errors[@]}" -gt 0 ]; then - echo -e "\033[0;31m===== The following list tests failed =====\033[0;39m" >&2 + log 'warn' "===== The following list tests failed ====="; for error in "${errors[@]}"; do - echo -e "\t${error}" - done - exit 1 + log 'warn' "\t${error}"; + done; + log 'error' 'List test failure(s)'; else - echo -e "\033[0;32mAll list tests passed.\033[0;39m" + log 'info' 'All list tests passed.'; fi; -exit 0 + +exit 0; diff --git a/test/test_symlink.sh b/test/test_symlink.sh index 0dd540f..3c99bfd 100755 --- a/test/test_symlink.sh +++ b/test/test_symlink.sh @@ -1,39 +1,83 @@ #!/usr/bin/env bash -declare -a errors +set -uo pipefail; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: Test Failed: ${1}" >&2 -} +#################################### +# Ensure we can execute standalone # +#################################### -function error_and_die() { - echo -e "tfenv: ${1}" >&2 - exit 1 -} +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +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; -TFENV_BIN_DIR="/tmp/tfenv-test" -rm -rf "${TFENV_BIN_DIR}" && mkdir "${TFENV_BIN_DIR}" -ln -s "${PWD}"/bin/* "${TFENV_BIN_DIR}" -export PATH="${TFENV_BIN_DIR}:${PATH}" + 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 "### Test supporting symlink" -cleanup || error_and_die "Cleanup failed?!" -tfenv install 0.8.2 || error_and_proceed "Install failed" -tfenv use 0.8.2 || error_and_proceed "Use failed" -check_version 0.8.2 || error_and_proceed "Version check failed" + 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=(); + +log 'info' '### Testing symlink functionality'; + +TFENV_BIN_DIR='/tmp/tfenv-test'; +log 'info' "## Creating/clearing ${TFENV_BIN_DIR}" +rm -rf "${TFENV_BIN_DIR}" && mkdir "${TFENV_BIN_DIR}"; +log 'info' "## Symlinking ${PWD}/bin/* into ${TFENV_BIN_DIR}"; +ln -s "${PWD}"/bin/* "${TFENV_BIN_DIR}"; + +cleanup || log 'error' 'Cleanup failed?!'; + +log 'info' '## Installing 0.8.2'; +${TFENV_BIN_DIR}/tfenv install 0.8.2 || error_and_proceed 'Install failed'; + +log 'info' '## Using 0.8.2'; +${TFENV_BIN_DIR}/tfenv use 0.8.2 || error_and_proceed 'Use failed'; + +log 'info' '## Check-Version for 0.8.2'; +check_active_version 0.8.2 || error_and_proceed 'Version check failed'; if [ "${#errors[@]}" -gt 0 ]; then - echo -e "\033[0;31m===== The following symlink tests failed =====\033[0;39m" >&2 + log 'warn' '===== The following symlink tests failed ====='; for error in "${errors[@]}"; do - echo -e "\t${error}" - done - exit 1 + log 'warn' "\t${error}"; + done; + log 'error' 'Symlink test failure(s)'; + exit 1; else - echo -e "\033[0;32mAll symlink tests passed.\033[0;39m" + log 'info' 'All symlink tests passed.'; fi; -exit 0 + +exit 0; diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index a0b1966..ccaad83 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -1,58 +1,89 @@ #!/usr/bin/env bash -declare -a errors +set -uo pipefail; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} +#################################### +# Ensure we can execute standalone # +#################################### -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 -} +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; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" + 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 "### Uninstall local versions" -cleanup || error_and_die "Cleanup failed?!" + echo "$(pwd -P)/${file_name}"; + }; -v="0.9.1" -( - tfenv install "${v}" || exit 1 - tfenv uninstall "${v}" || exit 1 - check_version "${v}" && exit 1 || exit 0 -) || error_and_proceed "Uninstall of version "${v}" failed" + 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; -echo "### Uninstall latest version" -cleanup || error_and_die "Cleanup failed?!" +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=(); + +function test_uninstall() { + local k="${1}"; + local v="${2}"; + tfenv install "${v}" || return 1; + tfenv uninstall "${v}" || return 1; + log 'info' 'Confirming uninstall success; an error indicates success:'; + check_active_version "${v}" && return 1 || return 0; +} -v="$(tfenv list-remote | head -n 1)" -( - tfenv install latest || exit 1 - tfenv uninstall latest || exit 1 - check_version "${v}" && exit 1 || exit 0 -) || error_and_proceed "Uninstalling latest version ${v}" +log 'info' '### Test Suite: Uninstall Local Versions' +cleanup || log 'error' 'Cleanup failed?!'; -echo "### Uninstall latest version with Regex" -cleanup || error_and_die "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)"; -v="$(tfenv list-remote | grep 0.8 | head -n 1)" -( - tfenv install latest:^0.8 || exit 1 - tfenv uninstall latest:^0.8 || exit 1 - check_version "${v}" && exit 1 || exit 0 -) || error_and_proceed "Uninstalling latest version "${v}" with Regex" +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; +done; if [ "${#errors[@]}" -gt 0 ]; then - echo -e "\033[0;31m===== The following list tests failed =====\033[0;39m" >&2 + log 'warn' "===== The following list tests failed ====="; for error in "${errors[@]}"; do - echo -e "\t${error}" - done - exit 1 + log 'warn' "\t${error}"; + done; + log 'error' 'List test failure(s)'; else - echo -e "\033[0;32mAll list tests passed.\033[0;39m" + log 'info' 'All list tests passed.'; fi; -exit 0 +exit 0; diff --git a/test/test_use_minrequired.sh b/test/test_use_minrequired.sh index c416b43..a71041a 100755 --- a/test/test_use_minrequired.sh +++ b/test/test_use_minrequired.sh @@ -1,52 +1,86 @@ #!/usr/bin/env bash -declare -a errors +set -uo pipefail; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} +#################################### +# Ensure we can execute standalone # +#################################### -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 -} +function early_death() { + echo "[FATAL] ${0}: ${1}" >&2; + exit 1; +}; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +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; -echo "### Install not min-required version" -cleanup || error_and_die "Cleanup failed?!" + 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; -v="0.8.8" -minv="0.8.0" + 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=(); + +log 'info' '### Install not min-required version'; +cleanup || log 'error' 'Cleanup failed?!'; + +v='0.8.8'; +minv='0.8.0'; ( - tfenv install "${v}" || true - tfenv use "${v}" || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing specific version ${v}" + tfenv install "${v}" || true; + tfenv use "${v}" || exit 1; + check_active_version "${v}" || exit 1; +) || error_and_proceed "Installing specific version ${v}"; echo "terraform { required_version = \">=${minv}\" -}" >> min_required.tf - -tfenv install min-required -tfenv use min-required +}" >> min_required.tf; -check_version "${minv}" || error_and_proceed "Min required version doesn't match" +tfenv install min-required; +tfenv use min-required; -cleanup || error_and_die "Cleanup failed?!" +check_active_version "${minv}" || error_and_proceed 'Min required version does not match'; +cleanup || log 'error' 'Cleanup failed?!'; if [ "${#errors[@]}" -gt 0 ]; then - echo -e "\033[0;31m===== The following install_and_use tests failed =====\033[0;39m" >&2 + log 'warn' '===== The following use_minrequired tests failed ====='; for error in "${errors[@]}"; do - echo -e "\t${error}" - done - exit 1 + log 'warn' "\t${error}"; + done; + log 'error' 'use_minrequired test failure(s)'; else - echo -e "\033[0;32mAll install_and_use tests passed.\033[0;39m" + log 'info' 'All use_minrequired tests passed.'; fi; -exit 0 + +exit 0;