diff --git a/bin/terraform b/bin/terraform index a66a0ce..c00b16a 100755 --- a/bin/terraform +++ b/bin/terraform @@ -17,16 +17,16 @@ if [ -z "${TFENV_ROOT:-""}" ]; then 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"; + cd "${target_file%/*}" || early_death "Failed to 'cd \$(${target_file%/*})' while trying to determine TFENV_ROOT"; + file_name="${target_file##*/}" || early_death "Failed to '\"${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"; + TFENV_SHIM=$(readlink_f "${0}") + TFENV_ROOT="${TFENV_SHIM%/*/*}"; + [ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT" else TFENV_ROOT="${TFENV_ROOT%/}"; fi; @@ -62,8 +62,4 @@ 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'; +tfenv-exec "$@" diff --git a/bin/tfenv b/bin/tfenv index db60c90..0d963bb 100755 --- a/bin/tfenv +++ b/bin/tfenv @@ -17,16 +17,17 @@ if [ -z "${TFENV_ROOT:-""}" ]; then 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"; + cd "${target_file%/*}" || early_death "Failed to 'cd \$(${target_file%/*})' while trying to determine TFENV_ROOT"; + file_name="${target_file##*/}" || early_death "Failed to '\"${target_file##*/}\"' while trying to determine TFENV_ROOT"; target_file="$(readlink "${file_name}")"; done; echo "$(pwd -P)/${file_name}"; }; + TFENV_SHIM=$(readlink_f "${0}") + TFENV_ROOT="${TFENV_SHIM%/*/*}"; + [ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT" - 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; diff --git a/lib/bashlog.sh b/lib/bashlog.sh index 4436e77..4abdadd 100755 --- a/lib/bashlog.sh +++ b/lib/bashlog.sh @@ -14,18 +14,20 @@ function _log_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 syslog="${BASHLOG_SYSLOG:-0}"; 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 stdout_extra="${BASHLOG_EXTRA:-0}"; - local syslog="${BASHLOG_SYSLOG:-0}"; - local tag="${BASHLOG_SYSLOG_TAG:-$(basename "${0}")}"; + if [ "${file}" -eq 1 ] || [ "${json}" -eq 1 ] || [ "${stdout_extra}" -eq 1 ]; then + local date_format="${BASHLOG_DATE_FORMAT:-+%F %T}"; + local date="$(date "${date_format}")"; + local date_s="$(date "+%s")"; + fi + local file_path="${BASHLOG_FILE_PATH:-/tmp/${0##*/}.log}"; + local json_path="${BASHLOG_JSON_PATH:-/tmp/${0##*/}.log.json}"; + + local tag="${BASHLOG_SYSLOG_TAG:-${0##*/})}"; local facility="${BASHLOG_SYSLOG_FACILITY:-local0}"; local pid="${$}"; @@ -33,7 +35,6 @@ function log() { local upper="$(echo "${level}" | awk '{print toupper($0)}')"; local debug_level="${TFENV_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:-""}"; @@ -162,7 +163,7 @@ export -f log; declare prev_cmd="null"; declare this_cmd="null"; -trap 'prev_cmd=$this_cmd; this_cmd=$BASH_COMMAND' DEBUG \ +trap 'prev_cmd=${this_cmd:-null}; this_cmd=$BASH_COMMAND' DEBUG \ && log debug 'DEBUG trap set' \ || log 'error' 'DEBUG trap failed to set'; diff --git a/lib/helpers.sh b/lib/helpers.sh index 1e73dfc..582c428 100755 --- a/lib/helpers.sh +++ b/lib/helpers.sh @@ -9,16 +9,16 @@ if [ -z "${TFENV_ROOT:-""}" ]; then 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"; + cd "${target_file%/*}" || early_death "Failed to 'cd \$(${target_file%/*})' while trying to determine TFENV_ROOT"; + file_name="${target_file##*/}" || early_death "Failed to '\"${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"; + TFENV_SHIM=$(readlink_f "${0}") + TFENV_ROOT="${TFENV_SHIM%/*/*}"; + [ -n "${TFENV_ROOT}" ] || early_death "Failed to determine TFENV_ROOT" else TFENV_ROOT="${TFENV_ROOT%/}"; fi; @@ -42,7 +42,25 @@ if [ "${TFENV_DEBUG:-0}" -gt 0 ]; then fi; fi; -source "${TFENV_ROOT}/lib/bashlog.sh"; +function load_bashlog () { + source "${TFENV_ROOT}/lib/bashlog.sh"; +} +export -f load_bashlog; +if [ "${TFENV_DEBUG:-0}" -gt 0 ] ; then + # our shim below cannot be used when debugging is enabled + load_bashlog +else + # Shim that understands to no-op for debug messages, and defers to + # full bashlog for everything else. + function log () { + if [ "$1" != 'debug' ] ; then + # Loading full bashlog will overwrite the `log` function + load_bashlog + log "$@" + fi + } + export -f log; +fi resolve_version () { declare version_requested version regex min_required version_file; @@ -158,4 +176,8 @@ function error_and_proceed() { }; export -f error_and_proceed; +source "$TFENV_ROOT/lib/tfenv-exec.sh"; +source "$TFENV_ROOT/lib/tfenv-version-file.sh"; +source "$TFENV_ROOT/lib/tfenv-version-name.sh"; + export TFENV_HELPERS=1; diff --git a/lib/tfenv-exec.sh b/lib/tfenv-exec.sh new file mode 100644 index 0000000..d3fe8eb --- /dev/null +++ b/lib/tfenv-exec.sh @@ -0,0 +1,37 @@ +function tfenv-exec() { + 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'; + return 1; + }; + export TFENV_VERSION; + + if [ ! -d "${TFENV_CONFIG_DIR}/versions/${TFENV_VERSION}" ]; then + if [ "${TFENV_AUTO_INSTALL:-true}" == "true" ]; then + if [ -z "${TFENV_TERRAFORM_VERSION:-""}" ]; then + TFENV_VERSION_SOURCE="$(tfenv-version-file)"; + else + TFENV_VERSION_SOURCE='TFENV_TERRAFORM_VERSION'; + fi + log 'info' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_SOURCE}). Installing now as TFENV_AUTO_INSTALL==true"; + tfenv-install; + else + log 'error' "version '${TFENV_VERSION}' was requested, but not installed and TFENV_AUTO_INSTALL is not 'true'"; + fi; + fi; + + TF_BIN_PATH="${TFENV_CONFIG_DIR}/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} $*"; + + return 0; +} +export -f tfenv-exec; diff --git a/lib/tfenv-version-file.sh b/lib/tfenv-version-file.sh new file mode 100644 index 0000000..78b8de9 --- /dev/null +++ b/lib/tfenv-version-file.sh @@ -0,0 +1,28 @@ +find_local_version_file() { + log 'debug' "Looking for a version file in ${1}"; + local root="${1}"; + while ! [[ "${root}" =~ ^//[^/]*$ ]]; do + if [ -e "${root}/.terraform-version" ]; then + log 'debug' "Found at ${root}/.terraform-version"; + echo "${root}/.terraform-version"; + return 0; + else + log 'debug' "Not found at ${root}/.terraform-version"; + fi; + [ -n "${root}" ] || break; + root="${root%/*}"; + done; + log 'debug' "No version file found in ${1}"; + return 1; +} +export -f find_local_version_file; + +function tfenv-version-file() { + 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_CONFIG_DIR: ${TFENV_CONFIG_DIR}/version"; + echo "${TFENV_CONFIG_DIR}/version"; + fi; + fi; +} +export -f tfenv-version-file; diff --git a/lib/tfenv-version-name.sh b/lib/tfenv-version-name.sh new file mode 100644 index 0000000..421bce8 --- /dev/null +++ b/lib/tfenv-version-name.sh @@ -0,0 +1,81 @@ +function tfenv-version-name() { + if [[ -z "${TFENV_TERRAFORM_VERSION:-""}" ]]; then + 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="$(cat "${TFENV_VERSION_FILE}" || true)" \ + && log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}"; + + TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}"; + else + TFENV_VERSION="${TFENV_TERRAFORM_VERSION}" \ + && log 'debug' "TFENV_VERSION specified in TFENV_TERRAFORM_VERSION: ${TFENV_VERSION}"; + + TFENV_VERSION_SOURCE='TFENV_TERRAFORM_VERSION'; + fi; + + if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then + log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}"; + + [ -d "${TFENV_CONFIG_DIR}/versions" ] \ + || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; + + if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then + regex="${TFENV_VERSION##*\:}"; + log 'debug' "'latest' keyword uses regex: ${regex}"; + else + regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$"; + log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}"; + fi; + + declare local_version=''; + if [[ -d "${TFENV_CONFIG_DIR}/versions" ]]; then + local_version="$(\find "${TFENV_CONFIG_DIR}/versions/" -type d -exec basename {} \; \ + | tail -n +2 \ + | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ + | grep -e "${regex}" \ + | head -n 1)"; + fi; + + if [[ "${TFENV_AUTO_INSTALL:-true}" == "true" ]]; then + log 'debug' "Trying to find the remote version using the regex: ${regex}"; + remote_version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)"; + if [[ -n "${remote_version}" ]]; then + if [[ "${local_version}" != "${remote_version}" ]]; then + log 'debug' "The installed version '${local_version}' does not much the remote version '${remote_version}'"; + TFENV_VERSION="${remote_version}"; + else + TFENV_VERSION="${local_version}"; + fi; + else + log 'error' "No versions matching '${requested}' found in remote"; + fi; + else + if [[ -n "${local_version}" ]]; then + TFENV_VERSION="${local_version}"; + else + log 'error' "No installed versions of terraform matched '${TFENV_VERSION}'"; + fi; + fi; + else + log 'debug' 'TFENV_VERSION does not use "latest" keyword'; + + # Accept a v-prefixed version, but strip the v. + if [[ "${TFENV_VERSION}" =~ ^v.*$ ]]; then + log 'debug' "Version Requested is prefixed with a v. Stripping the v." + TFENV_VERSION="${TFENV_VERSION#v*}"; + fi; + fi; + + if [[ -z "${TFENV_VERSION}" ]]; then + log 'error' "Version could not be resolved (set by ${TFENV_VERSION_SOURCE} or tfenv use )"; + fi; + + if [[ ! -d "${TFENV_CONFIG_DIR}/versions/${TFENV_VERSION}" ]]; then + log 'debug' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_SOURCE})"; + fi; + + echo "${TFENV_VERSION}"; +} +export -f tfenv-version-name; diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index 25d6aac..d3ae7dc 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -72,37 +72,4 @@ 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; - -if [ ! -d "${TFENV_CONFIG_DIR}/versions/${TFENV_VERSION}" ]; then - if [ "${TFENV_AUTO_INSTALL:-true}" == "true" ]; then - if [ -z "${TFENV_TERRAFORM_VERSION:-""}" ]; then - TFENV_VERSION_SOURCE="$(tfenv-version-file)"; - else - TFENV_VERSION_SOURCE='TFENV_TERRAFORM_VERSION'; - fi - log 'info' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_SOURCE}). Installing now as TFENV_AUTO_INSTALL==true"; - tfenv-install; - else - log 'error' "version '${TFENV_VERSION}' was requested, but not installed and TFENV_AUTO_INSTALL is not 'true'"; - fi; -fi; - -TF_BIN_PATH="${TFENV_CONFIG_DIR}/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; +tfenv-exec "$@" diff --git a/libexec/tfenv-version-file b/libexec/tfenv-version-file index a906ff9..6c2ae3f 100755 --- a/libexec/tfenv-version-file +++ b/libexec/tfenv-version-file @@ -61,27 +61,4 @@ done; # Begin Script Body # ##################### -find_local_version_file() { - log 'debug' "Looking for a version file in ${1}"; - local root="${1}"; - while ! [[ "${root}" =~ ^//[^/]*$ ]]; do - if [ -e "${root}/.terraform-version" ]; then - log 'debug' "Found at ${root}/.terraform-version"; - echo "${root}/.terraform-version"; - return 0; - else - log 'debug' "Not found at ${root}/.terraform-version"; - fi; - [ -n "${root}" ] || break; - root="${root%/*}"; - done; - log 'debug' "No version file found in ${1}"; - return 1; -} - -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_CONFIG_DIR: ${TFENV_CONFIG_DIR}/version"; - echo "${TFENV_CONFIG_DIR}/version"; - fi; -fi; +tfenv-version-file "$@" diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index 70c4fbf..d90884e 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -60,81 +60,4 @@ done; # Begin Script Body # ##################### -if [[ -z "${TFENV_TERRAFORM_VERSION:-""}" ]]; then - 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="$(cat "${TFENV_VERSION_FILE}" || true)" \ - && log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}"; - - TFENV_VERSION_SOURCE="${TFENV_VERSION_FILE}"; -else - TFENV_VERSION="${TFENV_TERRAFORM_VERSION}" \ - && log 'debug' "TFENV_VERSION specified in TFENV_TERRAFORM_VERSION: ${TFENV_VERSION}"; - - TFENV_VERSION_SOURCE='TFENV_TERRAFORM_VERSION'; -fi; - -if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then - log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}"; - - [ -d "${TFENV_CONFIG_DIR}/versions" ] \ - || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; - - if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then - regex="${TFENV_VERSION##*\:}"; - log 'debug' "'latest' keyword uses regex: ${regex}"; - else - regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$"; - log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}"; - fi; - - declare local_version=''; - if [[ -d "${TFENV_CONFIG_DIR}/versions" ]]; then - local_version="$(\find "${TFENV_CONFIG_DIR}/versions/" -type d -exec basename {} \; \ - | tail -n +2 \ - | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ - | grep -e "${regex}" \ - | head -n 1)"; - fi; - - if [[ "${TFENV_AUTO_INSTALL:-true}" == "true" ]]; then - log 'debug' "Trying to find the remote version using the regex: ${regex}"; - remote_version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)"; - if [[ -n "${remote_version}" ]]; then - if [[ "${local_version}" != "${remote_version}" ]]; then - log 'debug' "The installed version '${local_version}' does not much the remote version '${remote_version}'"; - TFENV_VERSION="${remote_version}"; - else - TFENV_VERSION="${local_version}"; - fi; - else - log 'error' "No versions matching '${requested}' found in remote"; - fi; - else - if [[ -n "${local_version}" ]]; then - TFENV_VERSION="${local_version}"; - else - log 'error' "No installed versions of terraform matched '${TFENV_VERSION}'"; - fi; - fi; -else - log 'debug' 'TFENV_VERSION does not use "latest" keyword'; - - # Accept a v-prefixed version, but strip the v. - if [[ "${TFENV_VERSION}" =~ ^v.*$ ]]; then - log 'debug' "Version Requested is prefixed with a v. Stripping the v." - TFENV_VERSION="${TFENV_VERSION#v*}"; - fi; -fi; - -if [[ -z "${TFENV_VERSION}" ]]; then - log 'error' "Version could not be resolved (set by ${TFENV_VERSION_SOURCE} or tfenv use )"; -fi; - -if [[ ! -d "${TFENV_CONFIG_DIR}/versions/${TFENV_VERSION}" ]]; then - log 'debug' "version '${TFENV_VERSION}' is not installed (set by ${TFENV_VERSION_SOURCE})"; -fi; - -echo "${TFENV_VERSION}"; +tfenv-version-name "$@"