From d7b4d81d8ac3e236fc9563e2aad3653609c34923 Mon Sep 17 00:00:00 2001 From: Tyler Wengerd Date: Tue, 29 Oct 2019 13:12:43 -0400 Subject: [PATCH 01/10] Fix link to .terraform-version file details (#146) * Fix link to .terraform-version file details * Quick grammar fixes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 88588ed..11a5b4b 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ 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 @@ -193,8 +193,8 @@ 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 From 3234ff8e40ad2cdd50c8b25f3e92cdb6b2ad233f Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Tue, 29 Oct 2019 16:17:27 +0000 Subject: [PATCH 02/10] Add tests for failing 0.11.15-oci case add 0.11.15-oci case to uninstall test Modify list-remote; add oci and make digit optional add belt and braces alpha beta and rc tests --- CHANGELOG.md | 5 ++++ libexec/tfenv-list-remote | 2 +- test/test_install_and_use.sh | 45 ++++++++++++++++++++++++++++++++++++ test/test_uninstall.sh | 7 ++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62ab04f..71c7d4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 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/libexec/tfenv-list-remote b/libexec/tfenv-list-remote index 394faa9..ba4e971 100755 --- a/libexec/tfenv-list-remote +++ b/libexec/tfenv-list-remote @@ -10,5 +10,5 @@ fi TFENV_REMOTE="${TFENV_REMOTE:-https://releases.hashicorp.com}" curlw -sf "${TFENV_REMOTE}/terraform/" \ - | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha)[0-9]+)?" \ + | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha|oci)[0-9]*)?" \ | uniq diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 4a34fb2..c0ea0d2 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -34,6 +34,51 @@ v="$(tfenv list-remote | head -n 1)" check_version "${v}" || exit 1 ) || error_and_proceed "Installing latest possibly-unstable version ${v}" +echo "### Install latest alpha" +cleanup || error_and_die "Cleanup failed?!" + +v="$(tfenv list-remote | grep 'alpha' | head -n 1)" +( + tfenv install latest:alpha || exit 1 + check_version "${v}" || exit 1 +) || error_and_proceed "Installing latest alpha ${v}" + +echo "### Install latest beta" +cleanup || error_and_die "Cleanup failed?!" + +v="$(tfenv list-remote | grep 'beta' | head -n 1)" +( + tfenv install latest:beta || exit 1 + check_version "${v}" || exit 1 +) || error_and_proceed "Installing latest beta ${v}" + +echo "### Install latest rc" +cleanup || error_and_die "Cleanup failed?!" + +v="$(tfenv list-remote | grep 'rc' | head -n 1)" +( + tfenv install latest:rc || exit 1 + check_version "${v}" || exit 1 +) || error_and_proceed "Installing latest rc ${v}" + +echo "### Install latest possibly-unstable version from 0.11" +cleanup || error_and_die "Cleanup failed?!" + +v="$(tfenv list-remote | grep '^0\.11\.' | head -n 1)" +( + tfenv install latest:^0.11. || exit 1 + check_version "${v}" || exit 1 +) || error_and_proceed "Installing latest possibly-unstable version from 0.11: ${v}" + +echo "### Install 0.11.15-oci" +cleanup || error_and_die "Cleanup failed?!" + +v="0.11.15-oci" +( + tfenv install 0.11.15-oci || exit 1 + check_version "${v}" || exit 1 +) || error_and_proceed "Installing version ${v}" + echo "### Install latest version with Regex" cleanup || error_and_die "Cleanup failed?!" diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index a0b1966..e6489bf 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -19,6 +19,13 @@ source "$(dirname "${0}")/helpers.sh" \ echo "### Uninstall local versions" cleanup || error_and_die "Cleanup failed?!" +v="0.11.15-oci" +( + 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" + v="0.9.1" ( tfenv install "${v}" || exit 1 From d22713804d3e227b164437aa926b8573986f088c Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sat, 2 Nov 2019 06:54:23 +0800 Subject: [PATCH 03/10] Update README.md documentation syntax/format (#147) * Fix Linux supported arch indentation in README.md The indent need to be 2 sapces here, origin one caused wrong rendering on GitHub project package. * Remove trailing spaces in README.md * Fix the blank lines usage in README.md Heading, fence and list should be surrounded by exactly one blank line. --- README.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 11a5b4b..d84daaa 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 + - 64bit + - Arm - Windows (64bit) - only tested 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 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,7 +120,6 @@ terraform { } ``` - ### Specify architecture Architecture other than the default amd64 can be specified with the `TFENV_ARCH` environment variable @@ -128,6 +137,7 @@ TFENV_REMOTE=https://example.jfrog.io/artifactory/hashicorp ``` ### tfenv use <version> + Switch a version to use `latest` is a syntax to use the latest installed version @@ -144,9 +154,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 +166,9 @@ $ tfenv uninstall latest:^0.8 ``` ### tfenv list + List installed versions + ```console % tfenv list * 0.10.7 (set by /opt/tfenv/version) @@ -169,7 +183,9 @@ List installed versions ``` ### tfenv list-remote + List installable versions + ```console % tfenv list-remote 0.9.0-beta2 @@ -194,6 +210,7 @@ List installable versions ``` ## .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 @@ -218,16 +235,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 From 45da04ade6a69ebd79b28b8f8b84974a79400fb5 Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Tue, 29 Oct 2019 17:30:35 +0000 Subject: [PATCH 04/10] Logging! --- .travis.yml | 12 +- CHANGELOG.md | 6 + README.md | 153 +++++++++++++++++- bin/terraform | 42 ++++- bin/tfenv | 127 +++++++++------ lib/bashlog.sh | 174 +++++++++++++++++++++ lib/helpers.sh | 53 +++++++ libexec/helpers | 26 ---- libexec/tfenv---version | 49 +++++- libexec/tfenv-exec | 55 ++++++- libexec/tfenv-help | 36 ++++- libexec/tfenv-install | 220 ++++++++++++++++---------- libexec/tfenv-list | 74 +++++++-- libexec/tfenv-list-remote | 40 ++++- libexec/tfenv-min-required | 73 ++++++--- libexec/tfenv-uninstall | 77 ++++++--- libexec/tfenv-use | 98 ++++++++---- libexec/tfenv-version-file | 60 +++++-- libexec/tfenv-version-name | 84 +++++++--- test/helpers.sh | 12 -- test/run.sh | 71 ++++++--- test/test_install_and_use.sh | 293 ++++++++++++++++------------------- test/test_list.sh | 73 +++++---- test/test_symlink.sh | 82 ++++++---- test/test_uninstall.sh | 108 +++++++------ test/test_use_minrequired.sh | 80 ++++++---- 26 files changed, 1526 insertions(+), 652 deletions(-) create mode 100755 lib/bashlog.sh create mode 100755 lib/helpers.sh delete mode 100755 libexec/helpers delete mode 100755 test/helpers.sh diff --git a/.travis.yml b/.travis.yml index 9c961b3..8eb3706 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,15 @@ +before_install: + - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install bash; fi' # Bash4 Required language: generic matrix: include: - os: linux - dist: trusty + dist: bionic - os: osx - osx_image: xcode8.2 # OS X 10.12 + osx_image: xcode11.2 - os: osx - osx_image: xcode8 # OS X 10.11 - - 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 71c7d4d..c0207ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.0-alpha1 (November 24, 2019) + + * New logging library + * New bash4 dependency + * Massive testing, logging and loading refactoring + ## 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 diff --git a/README.md b/README.md index d84daaa..ac54cbe 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Currently tfenv supports the following OSes - Linux - 64bit - Arm -- Windows (64bit) - only tested in git-bash +- Windows (64bit) - only tested in git-bash - currently presumed failing due to symlink issues in git-bash ## Installation @@ -120,22 +120,165 @@ terraform { } ``` -### Specify architecture +### Environment Variables -Architecture other than the default amd64 can be specified with the `TFENV_ARCH` environment variable +#### TFENV + +##### `TFENV_ARCH` + +String (Default: amd64) + +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. -Installing from a remote other than the default https://releases.hashicorp.com can be done by specifying the `TFENV_REMOTE` environment varible +* 2: v1 Behaviour: Pass `-#` to curl +* 1: Use curl default +* 0: Pass `-s` to curl + +##### `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 diff --git a/bin/terraform b/bin/terraform index d5df53f..1259d99 100755 --- a/bin/terraform +++ b/bin/terraform @@ -1,6 +1,40 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ -n "${TFENV_DEBUG:-""}" ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ -n "${TFENV_DEBUG:-""}" ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ -n "${TFENV_DEBUG:-""}" ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +log 'debug' "program=\"${0##*/}\""; + +declare tfenv_path="$(dirname "$(command -v "${0}")")/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..11c2908 100755 --- a/bin/tfenv +++ b/bin/tfenv @@ -1,63 +1,90 @@ #!/usr/bin/env bash -set -e -if [ -n "${TFENV_DEBUG}" ]; then - export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] ' - set -x -fi +set -euo pipefail; -# 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 +declare arg="${1:-""}"; - while [ "${target_file}" != "" ]; do - cd "$(dirname ${target_file})" - file_name="$(basename "${target_file}")" - target_file="$(readlink "${file_name}")" - done +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 "$(pwd -P)/${file_name}" -} + while [ "${target_file}" != "" ]; do + cd "$(dirname ${target_file})"; + file_name="$(basename "${target_file}")"; + target_file="$(readlink "${file_name}")"; + done; -if [ -z "${TFENV_ROOT}" ]; then - TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)" + echo "$(pwd -P)/${file_name}"; + } + + TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)"; else TFENV_ROOT="${TFENV_ROOT%/}" fi -export TFENV_ROOT -PATH="${TFENV_ROOT}/libexec:${PATH}" -export PATH +export TFENV_ROOT; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ -n "${TFENV_DEBUG:-""}" ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +log 'debug' "Prepending ${TFENV_ROOT}/libexec to PATH"; +PATH="${TFENV_ROOT}/libexec:${PATH}"; +export PATH; + +log 'debug' "Setting TFENV_DIR to ${PWD}"; export TFENV_DIR="${PWD}" abort() { - { if [ "${#}" -eq 0 ]; then cat - - else echo "tfenv: ${*}" - fi - } >&2 - exit 1 + log 'debug' 'Aborting...'; + { + 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 +case "${arg}" in + "") + log 'debug' 'No argument provided, dumping version and help and aborting'; + { + tfenv---version; + tfenv-help; + } | abort; + ;; + -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; + 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..4f4aa0c --- /dev/null +++ b/lib/bashlog.sh @@ -0,0 +1,174 @@ +#!/bin/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 -A severities; + severities['DEBUG']=7; + severities['INFO']=6; + severities['NOTICE']=5; # Unused + severities['WARN']=4; + severities['ERROR']=3; + severities['CRIT']=2; # Unused + severities['ALERT']=1; # Unused + severities['EMERG']=0; # Unused + + local severity="${severities[${upper}]:-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 -A colours; + colours['DEBUG']='\033[34m' # Blue + colours['INFO']='\033[32m' # Green + colours['NOTICE']='' # Unused + colours['WARN']='\033[33m' # Yellow + colours['ERROR']='\033[31m' # Red + colours['CRIT']='' # Unused + colours['ALERT']='' # Unused + colours['EMERG']='' # Unused + colours['DEFAULT']='\033[0m' # Default + + local norm="${colours['DEFAULT']}"; + local colour="${colours[${upper}]:-\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 to debug with. 'exit 0' to continue. Other exit codes will abort - parent shell will terminate."; + 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..d1e7854 --- /dev/null +++ b/lib/helpers.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -uo pipefail; + +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; + +[ -n "${TFENV_ROOT:-""}" ] || export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)" +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_version() { + v="${1}"; + [ -n "$(terraform --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ]; +} +export -f check_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..085a884 100755 --- a/libexec/tfenv---version +++ b/libexec/tfenv---version @@ -9,15 +9,48 @@ # 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 [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +log 'debug' 'Scraping tfenv version from CHANGELOG.md'; +version="$(awk '/^##/{ print $2; exit}' "${TFENV_ROOT}/CHANGELOG.md")" \ + || log 'error' 'Failed to scrape version from CHANGELOG.md'; +git_revision=""; 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 + log 'debug' 'Getting git revision...'; + git_revision="$(git describe --tags HEAD 2>/dev/null || true)"; + log 'debug' "Stripping git revision string ${git_revision}"; + git_revision="${git_revision#v}"; +fi; + +echo "tfenv ${git_revision:-$version}"; -echo "tfenv ${git_revision:-$version}" +exit 0; diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index 5720e8b..04876a6 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -13,10 +13,53 @@ # 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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}"; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}"; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh"; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +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..a21bccc 100755 --- a/libexec/tfenv-help +++ b/libexec/tfenv-help @@ -1,8 +1,36 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x +set -uo pipefail; -echo "Usage: tfenv [] +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +echo 'Usage: tfenv [] Commands: install Install a specific version of Terraform @@ -10,4 +38,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..dfafdf7 100755 --- a/libexec/tfenv-install +++ b/libexec/tfenv-install @@ -1,78 +1,106 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +set -uo pipefail; + +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -[ "${#}" -gt 1 ] && error_and_die "usage: tfenv install []" +[ "${#}" -gt 1 ] && log 'error' 'usage: tfenv install []'; -declare version_requested version regex +declare version_requested version regex; +declare arg="${1:-""}"; -if [ -z "${1}" ]; then - version_file="$(tfenv-version-file)" +if [ -z "${arg}" ]; then + version_file="$(tfenv-version-file)"; if [ "${version_file}" != "${TFENV_ROOT}/version" ]; then - version_requested="$(cat "${version_file}" || true)" - fi + version_requested="$(cat "${version_file}" || true)"; + fi; else - version_requested="${1}" -fi + version_requested="${arg}"; +fi; 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}" ] || 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" +[ -n "${version}" ] || log 'error' 'Version is not specified'; +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}" +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 +109,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..fbcd0d8 100755 --- a/libexec/tfenv-list +++ b/libexec/tfenv-list @@ -1,25 +1,69 @@ #!/usr/bin/env bash -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" +set -uo pipefail; + +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; [ "${#}" -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 ba4e971..d9018cc 100755 --- a/libexec/tfenv-list-remote +++ b/libexec/tfenv-list-remote @@ -1,14 +1,44 @@ #!/usr/bin/env bash -set -e -[ -n "${TFENV_DEBUG}" ] && set -x -source "${TFENV_ROOT}/libexec/helpers" + +set -uo pipefail; + +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; 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}"; curlw -sf "${TFENV_REMOTE}/terraform/" \ | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha|oci)[0-9]*)?" \ - | uniq + | uniq; diff --git a/libexec/tfenv-min-required b/libexec/tfenv-min-required index 68cb7ce..12a9813 100755 --- a/libexec/tfenv-min-required +++ b/libexec/tfenv-min-required @@ -1,42 +1,67 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; 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}"; diff --git a/libexec/tfenv-uninstall b/libexec/tfenv-uninstall index 7e679fc..f6a7ce7 100755 --- a/libexec/tfenv-uninstall +++ b/libexec/tfenv-uninstall @@ -1,39 +1,66 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +[ "${#}" -gt 1 ] && log 'error' 'usage: tfenv uninstall []'; -declare version_requested version regex +declare version_requested version regex; if [ -z "${1}" ]; then - version_file="$(tfenv-version-file)" - if [ "${version_file}" != "${TFENV_ROOT}/version" ];then - version_requested="$(cat "${version_file}" || true)" - fi + version_file="$(tfenv-version-file)"; + if [ "${version_file}" != "${TFENV_ROOT}/version" ]; then + version_requested="$(cat "${version_file}" || true)"; + fi; else - version_requested="${1}" -fi + version_requested="${1}"; +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; -[ -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" +[ -n "${version}" ] || log 'error' "Version is not specified"; +version="$(tfenv-list | sed -E 's/^(\*| )? //g; s/ \(set by .+\)$//' | grep -e "${regex}" | head -n 1)"; +[ -n "${version}" ] || log 'error' "No versions matching '${1}' 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..3de9b03 100755 --- a/libexec/tfenv-use +++ b/libexec/tfenv-use @@ -1,55 +1,91 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -declare version_requested version regex +[ "${#}" -ne 1 ] && log 'error' 'usage: tfenv use '; -version_requested="${1}" +declare version_requested version regex min_required version_file; + +version_requested="${1}"; if [[ "${version_requested}" =~ ^min-required$ ]]; then - echo "Detecting minimal required version..." - found_min_required="$(tfenv-min-required)" + log 'info' 'Detecting minimum required version...'; + min_required="$(tfenv-min-required)" \ + || log 'error' 'tfenv-min-required failed'; - if [[ $? -eq 0 ]]; then - echo "Min required version is detected as ${found_min_required}" - version_requested="${found_min_required}" - else - exit 1 - fi -fi + 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 to v${version}"; + +version_file="$(tfenv-version-file)"; +log 'debug' "Writing \"${version}\" to \"${version_file}\""; +echo "${version}" > "${version_file}" \ + || log 'error' "Switch to v${version} failed"; -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..0f1f360 100755 --- a/libexec/tfenv-version-file +++ b/libexec/tfenv-version-file @@ -1,20 +1,58 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; 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..471204f 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -1,37 +1,75 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +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'; + +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'; + + if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then + regex="${TFENV_VERSION##*\:}"; + log 'debug' "\"latest\" keyword uses regex: ${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; -version_exists() { - local version="${1}" - [ -d "${TFENV_ROOT}/versions/${version}" ] -} +else + log 'debug' 'TFENV_VERSION does not use "latest" keyword'; +fi; + +[ -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..d50d443 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,30 +1,57 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; else - targets="$(\ls "$(dirname "${0}")" | grep 'test_')" -fi + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; + +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 c0ea0d2..43874fe 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -1,177 +1,154 @@ #!/usr/bin/env bash -declare -a errors - -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} - -function error_and_die() { - echo -e "tfenv: ${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" - -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 alpha" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep 'alpha' | head -n 1)" -( - tfenv install latest:alpha || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest alpha ${v}" - -echo "### Install latest beta" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep 'beta' | head -n 1)" -( - tfenv install latest:beta || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest beta ${v}" - -echo "### Install latest rc" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep 'rc' | head -n 1)" -( - tfenv install latest:rc || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest rc ${v}" - -echo "### Install latest possibly-unstable version from 0.11" -cleanup || error_and_die "Cleanup failed?!" - -v="$(tfenv list-remote | grep '^0\.11\.' | head -n 1)" -( - tfenv install latest:^0.11. || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing latest possibly-unstable version from 0.11: ${v}" - -echo "### Install 0.11.15-oci" -cleanup || error_and_die "Cleanup failed?!" - -v="0.11.15-oci" -( - tfenv install 0.11.15-oci || exit 1 - check_version "${v}" || exit 1 -) || error_and_proceed "Installing 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}" +set -uo pipefail; + +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; -echo "### Install with ${HOME}/.terraform-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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; +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_version "${v}" || return 1; + return 0; +}; + +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 "${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_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; + +log 'info' 'Install invalid specific version'; +cleanup || log 'error' 'Cleanup failed?!'; -echo "### Install invalid specific version" -cleanup || error_and_die "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}" +declare -A neg_tests=(); +neg_tests['specific version']="9.9.9"; +neg_tests['latest:word']="latest:word"; -echo "### Install invalid latest: version" -cleanup || error_and_die "Cleanup failed?!" +test_num=1; -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}" +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..905c143 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -1,29 +1,49 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 -} +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +declare -a errors=(); -echo "### List local versions" -cleanup || error_and_die "Cleanup failed?!" +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 +51,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..012fb61 100755 --- a/test/test_symlink.sh +++ b/test/test_symlink.sh @@ -1,39 +1,67 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; + +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -function error_and_die() { - echo -e "tfenv: ${1}" >&2 - exit 1 -} +declare -a errors=(); -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +log 'info' '### Testing symlink functionality'; -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}" +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}"; +log 'info' "## Adding ${TFENV_BIN_DIR} to \$PATH"; +export PATH="${TFENV_BIN_DIR}:${PATH}"; -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" +cleanup || log 'error' 'Cleanup failed?!'; + +log 'info' '## Installing 0.8.2'; +tfenv install 0.8.2 || error_and_proceed 'Install failed'; + +log 'info' '## Using 0.8.2'; +tfenv use 0.8.2 || error_and_proceed 'Use failed'; + +log 'info' '## Check-Version for 0.8.2'; +check_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 e6489bf..b286c8b 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -1,65 +1,71 @@ #!/usr/bin/env bash -declare -a errors +set -uo pipefail; -function error_and_proceed() { - errors+=("${1}") - echo -e "tfenv: ${0}: Test Failed: ${1}" >&2 -} - -function error_and_die() { - echo -e "tfenv: ${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" - -echo "### Uninstall local versions" -cleanup || error_and_die "Cleanup failed?!" +# Ensure we can execute standalone +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; -v="0.11.15-oci" -( - 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" +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -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" +declare -a errors=(); -echo "### Uninstall latest version" -cleanup || error_and_die "Cleanup failed?!" +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_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 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..16ed6e0 100755 --- a/test/test_use_minrequired.sh +++ b/test/test_use_minrequired.sh @@ -1,52 +1,68 @@ #!/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 +if [ -n "${TFENV_ROOT:-""}" ]; then + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; + fi; +else + export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; + if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then + [ -n "${TFENV_HELPERS:-""}" ] \ + && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ + || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; + fi; +fi; -function error_and_die() { - echo -e "tfenv: ${0}: ${1}" >&2 - exit 1 -} +if [ -n "${TFENV_HELPERS:-""}" ]; then + log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; +else + [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + if source "${TFENV_ROOT}/lib/helpers.sh"; then + log 'debug' 'Helpers sourced successfully'; + else + echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + exit 1; + fi; +fi; -[ -n "${TFENV_DEBUG}" ] && set -x -source "$(dirname "${0}")/helpers.sh" \ - || error_and_die "Failed to load test helpers: $(dirname "${0}")/helpers.sh" +declare -a errors=(); -echo "### Install not min-required version" -cleanup || error_and_die "Cleanup failed?!" +log 'info' '### Install not min-required version'; +cleanup || log 'error' 'Cleanup failed?!'; -v="0.8.8" -minv="0.8.0" +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_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_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; From dd2eced756f5be1cdcde5d6a01b0ba2c7d59eb2a Mon Sep 17 00:00:00 2001 From: Zordrak Date: Mon, 25 Nov 2019 20:42:16 +0000 Subject: [PATCH 05/10] Logging (#154) * Add all ubuntu options to travis * Workaround bash 4.3 bug by replacing BASH_SOURCE with dirname $0 * Remove unsupported ubuntu release from travis --- .travis.yml | 4 ++++ libexec/tfenv---version | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8eb3706..d3ca4bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,10 @@ matrix: include: - os: linux dist: bionic + - os: linux + dist: xenial + - os: linux + dist: trusty - os: osx osx_image: xcode11.2 - os: osx diff --git a/libexec/tfenv---version b/libexec/tfenv---version index 085a884..760d34c 100755 --- a/libexec/tfenv---version +++ b/libexec/tfenv---version @@ -41,13 +41,14 @@ fi; 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 "${BASH_SOURCE%/*}" 2>/dev/null && git remote -v 2>/dev/null | grep -q tfenv; then - log 'debug' 'Getting git revision...'; +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 ${git_revision}"; + log 'debug' "Stripping git revision string from ${git_revision}"; git_revision="${git_revision#v}"; fi; From e76ca9e89fd0c22ff20b1af1f7395ea392839af4 Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Mon, 25 Nov 2019 23:10:28 +0000 Subject: [PATCH 06/10] Rewrite script initialisation to cleanly handle symlinks and prep all scripts for standalone --- bin/terraform | 63 +++++++++++++++++------- bin/tfenv | 56 +++++++++++++++------- lib/bashlog.sh | 2 +- lib/helpers.sh | 25 +++++++++- libexec/tfenv---version | 61 +++++++++++++++++------- libexec/tfenv-exec | 61 +++++++++++++++++------- libexec/tfenv-help | 28 ----------- libexec/tfenv-install | 81 ++++++++++++++++++++++++------- libexec/tfenv-list | 61 +++++++++++++++++------- libexec/tfenv-list-remote | 61 +++++++++++++++++------- libexec/tfenv-min-required | 65 ++++++++++++++++++------- libexec/tfenv-uninstall | 92 ++++++++++++++++++++++++++++-------- libexec/tfenv-use | 61 +++++++++++++++++------- libexec/tfenv-version-file | 61 +++++++++++++++++------- libexec/tfenv-version-name | 61 +++++++++++++++++------- test/run.sh | 50 +++++++++++++------- test/test_install_and_use.sh | 54 ++++++++++++++------- test/test_list.sh | 50 +++++++++++++------- test/test_symlink.sh | 56 ++++++++++++++-------- test/test_uninstall.sh | 52 +++++++++++++------- test/test_use_minrequired.sh | 50 +++++++++++++------- 21 files changed, 814 insertions(+), 337 deletions(-) diff --git a/bin/terraform b/bin/terraform index 1259d99..08137ce 100755 --- a/bin/terraform +++ b/bin/terraform @@ -1,37 +1,66 @@ #!/usr/bin/env bash set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ -n "${TFENV_DEBUG:-""}" ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ -n "${TFENV_DEBUG:-""}" ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 - [ -n "${TFENV_DEBUG:-""}" ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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="$(dirname "$(command -v "${0}")")/tfenv"; +declare tfenv_path="${TFENV_ROOT}/bin/tfenv"; log 'debug' "Exec: \"${tfenv_path}\" exec \"${@}\""; exec "${tfenv_path}" exec "${@}" \ diff --git a/bin/tfenv b/bin/tfenv index 11c2908..ccc135d 100755 --- a/bin/tfenv +++ b/bin/tfenv @@ -1,7 +1,14 @@ #!/usr/bin/env bash -set -euo pipefail; +set -uo pipefail; -declare arg="${1:-""}"; +#################################### +# 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 @@ -10,35 +17,48 @@ if [ -z "${TFENV_ROOT:-""}" ]; then local file_name; while [ "${target_file}" != "" ]; do - cd "$(dirname ${target_file})"; - file_name="$(basename "${target_file}")"; + 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 + TFENV_ROOT="${TFENV_ROOT%/}"; +fi; export TFENV_ROOT; if [ -n "${TFENV_HELPERS:-""}" ]; then log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; else - [ -n "${TFENV_DEBUG:-""}" ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh"; fi; fi; -log 'debug' "Prepending ${TFENV_ROOT}/libexec to PATH"; -PATH="${TFENV_ROOT}/libexec:${PATH}"; -export PATH; +# 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}" @@ -52,8 +72,9 @@ abort() { echo "tfenv: ${*}"; fi; } >&2; - exit 1; -} +}; + +log 'debug' "tfenv argument is: ${arg}"; case "${arg}" in "") @@ -61,7 +82,8 @@ case "${arg}" in { tfenv---version; tfenv-help; - } | abort; + } | abort && exit 1; +exit 1; ;; -v | --version ) log 'debug' 'tfenv version requested...'; @@ -79,7 +101,7 @@ case "${arg}" in { echo "No such command '${arg}'"; tfenv-help; - } | abort; + } | abort && exit 1; fi; shift 1; log 'debug' "Exec: \"${command_path}\" \"${@}\""; diff --git a/lib/bashlog.sh b/lib/bashlog.sh index 4f4aa0c..3499cf6 100755 --- a/lib/bashlog.sh +++ b/lib/bashlog.sh @@ -147,7 +147,7 @@ function log() { 'error') echo -e "${std_line}" >&2; if [ "${debug_level}" -gt 1 ]; then - echo -e "Here's a shell to debug with. 'exit 0' to continue. Other exit codes will abort - parent shell will terminate."; + 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; diff --git a/lib/helpers.sh b/lib/helpers.sh index d1e7854..07477e7 100755 --- a/lib/helpers.sh +++ b/lib/helpers.sh @@ -2,6 +2,28 @@ 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 @@ -10,7 +32,6 @@ if [ "${TFENV_DEBUG:-0}" -gt 0 ]; then fi; fi; -[ -n "${TFENV_ROOT:-""}" ] || export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)" source "${TFENV_ROOT}/lib/bashlog.sh"; # Curl wrapper to switch TLS option for each OS @@ -28,7 +49,7 @@ export -f curlw; check_version() { v="${1}"; - [ -n "$(terraform --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ]; + [ -n "$(${TFENV_ROOT}/bin/terraform --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ]; } export -f check_version; diff --git a/libexec/tfenv---version b/libexec/tfenv---version index 760d34c..6bc8e28 100755 --- a/libexec/tfenv---version +++ b/libexec/tfenv---version @@ -11,34 +11,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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" \ diff --git a/libexec/tfenv-exec b/libexec/tfenv-exec index 04876a6..a1359dc 100755 --- a/libexec/tfenv-exec +++ b/libexec/tfenv-exec @@ -15,34 +15,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}"; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}"; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh"; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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}" \ diff --git a/libexec/tfenv-help b/libexec/tfenv-help index a21bccc..49239bb 100755 --- a/libexec/tfenv-help +++ b/libexec/tfenv-help @@ -2,34 +2,6 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; -else - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - fi; -fi; - -if [ -n "${TFENV_HELPERS:-""}" ]; then - log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again'; -else - [ "${TFENV_DEBUG:-0}" -gt 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - if source "${TFENV_ROOT}/lib/helpers.sh"; then - log 'debug' 'Helpers sourced successfully'; - else - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; - fi; -fi; - echo 'Usage: tfenv [] Commands: diff --git a/libexec/tfenv-install b/libexec/tfenv-install index dfafdf7..50efff9 100755 --- a/libexec/tfenv-install +++ b/libexec/tfenv-install @@ -2,34 +2,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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; @@ -37,13 +66,26 @@ 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)"; + 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 'info' 'Detecting minimal required version...'; found_min_required="$(tfenv-min-required)"; @@ -67,7 +109,10 @@ else regex="^${version_requested}$"; fi; -[ -n "${version}" ] || log 'error' 'Version is not specified'; +[ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest'; + +log 'debug' "Processing install for version ${version}, using regex ${regex}"; + version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)"; [ -n "${version}" ] || log 'error' "No versions matching '${arg}' found in remote"; diff --git a/libexec/tfenv-list b/libexec/tfenv-list index fbcd0d8..b8e232a 100755 --- a/libexec/tfenv-list +++ b/libexec/tfenv-list @@ -2,34 +2,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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 ] \ && log 'error' "usage: tfenv list" diff --git a/libexec/tfenv-list-remote b/libexec/tfenv-list-remote index d9018cc..7b59d6b 100755 --- a/libexec/tfenv-list-remote +++ b/libexec/tfenv-list-remote @@ -2,34 +2,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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; diff --git a/libexec/tfenv-min-required b/libexec/tfenv-min-required index 12a9813..b886e93 100755 --- a/libexec/tfenv-min-required +++ b/libexec/tfenv-min-required @@ -4,34 +4,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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() { 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 @@ -45,7 +74,7 @@ see https://www.terraform.io/docs/configuration/terraform.html for details'; find_min_required() { 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]}"; @@ -64,4 +93,4 @@ find_min_required() { bailout; }; -find_min_required "${TFENV_DIR}"; +find_min_required "${TFENV_DIR:-$(pwd)}"; diff --git a/libexec/tfenv-uninstall b/libexec/tfenv-uninstall index f6a7ce7..19a506e 100755 --- a/libexec/tfenv-uninstall +++ b/libexec/tfenv-uninstall @@ -2,45 +2,92 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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 "${1}" ]; then +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)"; + 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}"; + 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 @@ -54,9 +101,12 @@ else regex="^${version_requested}$"; fi; -[ -n "${version}" ] || log 'error' "Version is not specified"; +[ -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}"; + version="$(tfenv-list | sed -E 's/^(\*| )? //g; s/ \(set by .+\)$//' | grep -e "${regex}" | head -n 1)"; -[ -n "${version}" ] || log 'error' "No versions matching '${1}' found in local"; +[ -n "${version}" ] || log 'error' "No versions matching '${regex}' found in local"; dst_path="${TFENV_ROOT}/versions/${version}"; if [ -f "${dst_path}/terraform" ]; then diff --git a/libexec/tfenv-use b/libexec/tfenv-use index 3de9b03..b07dcca 100755 --- a/libexec/tfenv-use +++ b/libexec/tfenv-use @@ -2,34 +2,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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; diff --git a/libexec/tfenv-version-file b/libexec/tfenv-version-file index 0f1f360..7dfb976 100755 --- a/libexec/tfenv-version-file +++ b/libexec/tfenv-version-file @@ -4,34 +4,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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() { log 'debug' "Looking for a version file in ${1}"; local root="${1}"; diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index 471204f..8e5f501 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -3,34 +3,63 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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" ] \ || log 'error' 'No versions of terraform installed. Please install one with: tfenv install'; diff --git a/test/run.sh b/test/run.sh index d50d443..6abb640 100755 --- a/test/run.sh +++ b/test/run.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh"; fi; fi; +##################### +# Begin Script Body # +##################### + export PATH="${TFENV_ROOT}/bin:${PATH}"; errors=(); diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 43874fe..3ed0181 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh"; fi; fi; +##################### +# Begin Script Body # +##################### + test_install_and_use() { # Takes a static version and the optional keyword to install it with local k="${2-""}"; @@ -76,7 +94,7 @@ for desc in "${!string_tests[@]}"; do kv="${string_tests[${desc}]}"; v="${kv%,*}"; k="${kv##*,}"; - log 'info' "## ./.terraform-version Test ${test_num}/${string_tests[*]}: ${desc} ( ${k} / ${v} )"; + 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}" \ @@ -86,7 +104,7 @@ for desc in "${!string_tests[@]}"; do done; cleanup || log 'error' 'Cleanup failed?!'; -log 'info' '## \${HOME}/.terraform-version Test Preparation'; +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 diff --git a/test/test_list.sh b/test/test_list.sh index 905c143..365555f 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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'; diff --git a/test/test_symlink.sh b/test/test_symlink.sh index 012fb61..3a5cc69 100755 --- a/test/test_symlink.sh +++ b/test/test_symlink.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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'; @@ -39,16 +57,14 @@ 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}"; -log 'info' "## Adding ${TFENV_BIN_DIR} to \$PATH"; -export PATH="${TFENV_BIN_DIR}:${PATH}"; cleanup || log 'error' 'Cleanup failed?!'; log 'info' '## Installing 0.8.2'; -tfenv install 0.8.2 || error_and_proceed 'Install failed'; +${TFENV_BIN_DIR}/tfenv install 0.8.2 || error_and_proceed 'Install failed'; log 'info' '## Using 0.8.2'; -tfenv use 0.8.2 || error_and_proceed 'Use failed'; +${TFENV_BIN_DIR}/tfenv use 0.8.2 || error_and_proceed 'Use failed'; log 'info' '## Check-Version for 0.8.2'; check_version 0.8.2 || error_and_proceed 'Version check failed'; diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index b286c8b..e03cc04 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh"; fi; fi; +##################### +# Begin Script Body # +##################### + declare -a errors=(); function test_uninstall() { @@ -50,7 +68,7 @@ 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)"; -declare test_num=1; +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}]}" \ diff --git a/test/test_use_minrequired.sh b/test/test_use_minrequired.sh index 16ed6e0..c0a8b11 100755 --- a/test/test_use_minrequired.sh +++ b/test/test_use_minrequired.sh @@ -2,34 +2,52 @@ set -uo pipefail; -# Ensure we can execute standalone -if [ -n "${TFENV_ROOT:-""}" ]; then - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT already defined as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT already defined as ${TFENV_ROOT}" >&2; - fi; +#################################### +# 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 - export TFENV_ROOT="$(cd "$(dirname "${0}")/.." && pwd)"; - if [ "${TFENV_DEBUG:-0}" -gt 1 ]; then - [ -n "${TFENV_HELPERS:-""}" ] \ - && log 'debug' "TFENV_ROOT declared as ${TFENV_ROOT}" \ - || echo "[DEBUG] TFENV_ROOT declared as ${TFENV_ROOT}" >&2; - 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 1 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; + [ "${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 - echo "[ERROR] Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh" >&2; - exit 1; + 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'; From e21f9e0761ad8d98b7bdc02bc4eeebf4d50f58d0 Mon Sep 17 00:00:00 2001 From: Povilas Daukintis Date: Mon, 2 Dec 2019 22:37:35 +0200 Subject: [PATCH 07/10] Fix shebang to use env for bash in bashlog.sh --- lib/bashlog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bashlog.sh b/lib/bashlog.sh index 3499cf6..6837653 100755 --- a/lib/bashlog.sh +++ b/lib/bashlog.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -uo pipefail; From e36f0271040476a43b58681e10562d8454e50b4c Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Mon, 16 Dec 2019 17:25:43 -0800 Subject: [PATCH 08/10] OSX ships with bash 3.x, which doesn't support associative arrays --- lib/bashlog.sh | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/bashlog.sh b/lib/bashlog.sh index 6837653..52ad6db 100755 --- a/lib/bashlog.sh +++ b/lib/bashlog.sh @@ -55,17 +55,17 @@ function log() { # 6 Informational: informational messages # 7 Debug: debug-level messages - local -A severities; - severities['DEBUG']=7; - severities['INFO']=6; - severities['NOTICE']=5; # Unused - severities['WARN']=4; - severities['ERROR']=3; - severities['CRIT']=2; # Unused - severities['ALERT']=1; # Unused - severities['EMERG']=0; # Unused - - local severity="${severities[${upper}]:-3}" + 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 @@ -99,19 +99,19 @@ function log() { fi; - local -A colours; - colours['DEBUG']='\033[34m' # Blue - colours['INFO']='\033[32m' # Green - colours['NOTICE']='' # Unused - colours['WARN']='\033[33m' # Yellow - colours['ERROR']='\033[31m' # Red - colours['CRIT']='' # Unused - colours['ALERT']='' # Unused - colours['EMERG']='' # Unused - colours['DEFAULT']='\033[0m' # Default - - local norm="${colours['DEFAULT']}"; - local colour="${colours[${upper}]:-\033[31m}"; + 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 From b7f72a515fdc2803036f3245920379d0c5347497 Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Sun, 16 Feb 2020 16:01:01 +0000 Subject: [PATCH 09/10] Don't overwrite .terraform-version files. Only change the default. Warn when overridden --- CHANGELOG.md | 3 ++- libexec/tfenv-use | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0207ca..a371754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -## 2.0.0-alpha1 (November 24, 2019) +## 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) diff --git a/libexec/tfenv-use b/libexec/tfenv-use index b07dcca..d701f28 100755 --- a/libexec/tfenv-use +++ b/libexec/tfenv-use @@ -108,13 +108,16 @@ target_path="${TFENV_ROOT}/versions/${version}"; [ -x "${target_path}/terraform" ] \ || log 'error' "Version directory for ${version} is present, but the terraform binary is not executable! Manual intervention required."; -log 'info' "Switching to v${version}"; - -version_file="$(tfenv-version-file)"; +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; + terraform --version 1>/dev/null \ || log 'error' "'terraform --version' failed. Something is seriously wrong"; log 'info' "Switching completed"; From a9c7e38b5c2c33aa2701bea08fec4714f0a0037f Mon Sep 17 00:00:00 2001 From: Mike Peachey Date: Sun, 16 Feb 2020 17:33:11 +0000 Subject: [PATCH 10/10] Fix tests for new logic --- lib/helpers.sh | 20 +++++++++++++++++--- libexec/tfenv-list-remote | 2 +- libexec/tfenv-version-name | 7 +++++-- test/test_install_and_use.sh | 17 ++++++++++++++--- test/test_symlink.sh | 2 +- test/test_uninstall.sh | 2 +- test/test_use_minrequired.sh | 4 ++-- 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/lib/helpers.sh b/lib/helpers.sh index 07477e7..cfddfba 100755 --- a/lib/helpers.sh +++ b/lib/helpers.sh @@ -47,11 +47,25 @@ function curlw () { } export -f curlw; -check_version() { - v="${1}"; +check_active_version() { + local v="${1}"; [ -n "$(${TFENV_ROOT}/bin/terraform --version | grep -E "^Terraform v${v}((-dev)|( \([a-f0-9]+\)))?$")" ]; } -export -f check_version; +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'; diff --git a/libexec/tfenv-list-remote b/libexec/tfenv-list-remote index 7b59d6b..8b04997 100755 --- a/libexec/tfenv-list-remote +++ b/libexec/tfenv-list-remote @@ -67,7 +67,7 @@ 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}"; +#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|oci)[0-9]*)?" \ | uniq; diff --git a/libexec/tfenv-version-name b/libexec/tfenv-version-name index 8e5f501..64bb7c0 100755 --- a/libexec/tfenv-version-name +++ b/libexec/tfenv-version-name @@ -71,11 +71,14 @@ TFENV_VERSION="$(cat "${TFENV_VERSION_FILE}" || true)" \ && log 'debug' "TFENV_VERSION specified in TFENV_VERSION_FILE: ${TFENV_VERSION}"; if [[ "${TFENV_VERSION}" =~ ^latest.*$ ]]; then - log 'debug' 'TFENV_VERSION uses "latest" keyword'; + log 'debug' "TFENV_VERSION uses 'latest' keyword: ${TFENV_VERSION}"; if [[ "${TFENV_VERSION}" =~ ^latest\:.*$ ]]; then regex="${TFENV_VERSION##*\:}"; - log 'debug' "\"latest\" keyword uses regex: ${regex}"; + 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" \ diff --git a/test/test_install_and_use.sh b/test/test_install_and_use.sh index 3ed0181..8bea949 100755 --- a/test/test_install_and_use.sh +++ b/test/test_install_and_use.sh @@ -53,10 +53,21 @@ test_install_and_use() { local k="${2-""}"; local v="${1}"; tfenv install "${k}" || return 1; - check_version "${v}" || 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; +} + declare -a errors=(); log 'info' '### Test Suite: Install and Use' @@ -120,14 +131,14 @@ test_install_and_use "${v1}" \ || error_and_proceed "## \${HOME}/.terraform-version Test 1/1: ( ${v1} ) failed"; log 'info' "## \${HOME}/.terraform-version Test 2/3: Override Install with Parameter ( ${v2} )"; -test_install_and_use "${v2}" "${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"; log 'info' "## \${HOME}/.terraform-version Test 3/3: Override Use with Parameter ( ${v2} )"; ( tfenv use "${v2}" || exit 1; - check_version "${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"; diff --git a/test/test_symlink.sh b/test/test_symlink.sh index 3a5cc69..3c99bfd 100755 --- a/test/test_symlink.sh +++ b/test/test_symlink.sh @@ -67,7 +67,7 @@ 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_version 0.8.2 || error_and_proceed 'Version check failed'; +check_active_version 0.8.2 || error_and_proceed 'Version check failed'; if [ "${#errors[@]}" -gt 0 ]; then log 'warn' '===== The following symlink tests failed ====='; diff --git a/test/test_uninstall.sh b/test/test_uninstall.sh index e03cc04..ccaad83 100755 --- a/test/test_uninstall.sh +++ b/test/test_uninstall.sh @@ -56,7 +56,7 @@ function test_uninstall() { tfenv install "${v}" || return 1; tfenv uninstall "${v}" || return 1; log 'info' 'Confirming uninstall success; an error indicates success:'; - check_version "${v}" && return 1 || return 0; + check_active_version "${v}" && return 1 || return 0; } log 'info' '### Test Suite: Uninstall Local Versions' diff --git a/test/test_use_minrequired.sh b/test/test_use_minrequired.sh index c0a8b11..a71041a 100755 --- a/test/test_use_minrequired.sh +++ b/test/test_use_minrequired.sh @@ -58,7 +58,7 @@ minv='0.8.0'; ( tfenv install "${v}" || true; tfenv use "${v}" || exit 1; - check_version "${v}" || exit 1; + check_active_version "${v}" || exit 1; ) || error_and_proceed "Installing specific version ${v}"; echo "terraform { @@ -69,7 +69,7 @@ echo "terraform { tfenv install min-required; tfenv use min-required; -check_version "${minv}" || error_and_proceed 'Min required version does not match'; +check_active_version "${minv}" || error_and_proceed 'Min required version does not match'; cleanup || log 'error' 'Cleanup failed?!';