diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18e56fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +versions/ +version diff --git a/README.md b/README.md index ee236d2..cff427b 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,100 @@ $ cd ~/.dctlenv $ git pull ``` +## Command Reference + +### `dctlenv install []` + +Install a specific version of driftctl. + +```shell +$ dctlenv install 0.2.3 +Installing driftctl v0.2.3 +Downloading release tarball from https://github.com/cloudskiff/driftctl/releases/download/v0.2.3/driftctl_darwin_amd64 +######################################################################################################################## 100.0% +Making the /home/wbeuil/.dctlenv/versions/0.2.3/driftctl binary executable +Installation of driftctl v0.2.3 successful. To make this your default version, run 'dctlenv use 0.2.3' +``` + +### `dctlenv use []` + +Switch a version to use. + +```shell +$ dctlenv use 0.2.3 +Switching version to v0.2.3 +Switching completed +``` + +### `dctlenv list` + +List installed versions. + +```shell +$ dctlenv list +* 0.2.3 (set by /home/wbeuil/.dctlenv/version) + 0.2.2 +``` + +### `dctlenv list-remote` + +List all installable versions. + +```shell +$ dctlenv list-remote +0.1.0 +0.1.1 +0.2.0 +0.2.1 +0.2.2 +0.2.3 +``` + +### `dctlenv root` + +Display the root directory where dctlenv is. + +```shell +$ dctlenv root +/home/wbeuil/.dctlenv +``` + +### `dctlenv [version|-v|--version]` + +Display dctlenv version. + +```shell +$ dctlenv version +dctlenv 0.0.2-1-gabff053 +``` + +### `dctlenv [help|-h|--help]` + +Display dctlenv help and usage contents. + +```shell +$ dctlenv help +Usage: dctlenv [] + +Commands: + install Install a specific version of driftctl + uninstall Uninstall a specific version of driftctl + use Switch a version to use + list List all installed versions + list-remote List all installable versions + version Display dctlenv version + help Show this help output + +Flags: + -v, --version An alias for the "version" command + -h, --help An alias for the "help" command + +For full documentation, see: https://github.com/wbeuil/dctlenv#readme +``` + ## Uninstalling dctlenv -You will need to first remove the line from your shell startup configuration file. This will remove dctlenv shims directory from your `$PATH`. +You will need to first remove the line from your shell startup configuration file. This will remove dctlenv from your `$PATH`. Then, you just need to remove its root directory: diff --git a/bin/driftctl b/bin/driftctl new file mode 100755 index 0000000..729dc82 --- /dev/null +++ b/bin/driftctl @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -uo pipefail + +panic() { + echo "panic: $*" >&2 + exit 1 +} + +if [ -z "${DCTLENV_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)" + file_name="$(basename "$target_file")" + target_file="$(readlink "$file_name")" + done + + echo "$(pwd -P)/$file_name" + } + + DCTLENV_ROOT="$(cd "$(dirname "$(readlink_f "$0")")/.." && pwd)" + [ -n $DCTLENV_ROOT ] || panic "Failed to set DCTLENV_ROOT" +else + DCTLENV_ROOT="${DCTLENV_ROOT%/}" +fi +export DCTLENV_ROOT + +if source "$DCTLENV_ROOT/lib/utils.sh"; then + log_debug "Utils from $DCTLENV_ROOT/lib/utils.sh sourced successfully" +else + panic "Failed to source utils from $DCTLENV_ROOT/lib/utils.sh" +fi + +for dir in libexec bin; do + case ":$PATH:" in + *:$DCTLENV_ROOT/$dir:*) + log_debug "\$PATH already contains '$DCTLENV_ROOT/$dir'" + ;; + *) + log_debug "Adding '$DCTLENV_ROOT/$dir' to \$PATH" + export PATH="$DCTLENV_ROOT/$dir:$PATH" + ;; + esac +done + +driftctl_path="$DCTLENV_ROOT/bin/driftctl" + +exec dctlenv-exec "$@" || log_error "Failed to exec: \"$driftctl_path\" \"$@\"" diff --git a/lib/log.sh b/lib/log.sh index 7c2b3d4..ad77bd3 100755 --- a/lib/log.sh +++ b/lib/log.sh @@ -10,16 +10,19 @@ log() { local line="$date [$level] $text" + local usage="${3:-""}" + case "$1" in 'info' | 'debug' | 'warn') if [ "${DCTLENV_DEBUG:-0}" -gt 0 ]; then - echo "$line" >&2 + echo -e "$line" >&2 fi ;; 'error') if [ "${DCTLENV_DEBUG:-0}" -gt 0 ]; then - echo "$line" >&2 + echo -e "$line" >&2 fi + echo -e "$usage" >&2 exit 1 ;; *) @@ -27,19 +30,26 @@ log() { ;; esac } +export -f log log_info() { log "info" "$1" } +export -f log_info log_debug() { log "debug" "$1" } +export -f log_debug log_warn() { log "warn" "$1" } +export -f log_warn log_error() { - log "error" "$1" + local usage="${2:-$1}" + + log "error" "$1" "$usage" } +export -f log_error diff --git a/lib/utils.sh b/lib/utils.sh index 05ddbac..307527d 100755 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -2,3 +2,16 @@ set -uo pipefail source "$DCTLENV_ROOT/lib/log.sh" + +# Curl wrapper to switch TLS option for each OS +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 diff --git a/libexec/dctlenv b/libexec/dctlenv index 3b03846..f0f0637 100755 --- a/libexec/dctlenv +++ b/libexec/dctlenv @@ -16,7 +16,7 @@ abort() { command="${1:-""}" -log_info "CLI command args: \"$command\"" +log_info "CLI command args: $*" case "$command" in "") diff --git a/libexec/dctlenv-exec b/libexec/dctlenv-exec new file mode 100755 index 0000000..0c73cab --- /dev/null +++ b/libexec/dctlenv-exec @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -uo pipefail + +log_debug 'Getting version from dctlenv-version-name' + +DCTLENV_VERSION="$(dctlenv-version-name 2>/dev/null)" \ + && log 'debug' "DCTLENV_VERSION is $DCTLENV_VERSION" \ + || log_error 'Failed to get version from dctlenv-version-name' + +if [ ! -d "$DCTLENV_ROOT/versions/$DCTLENV_VERSION" ]; then + log_error "Version '$DCTLENV_VERSION' was requested, but not installed" +fi + +DRIFTCTL_BIN_PATH="$DCTLENV_ROOT/versions/$DCTLENV_VERSION/driftctl" +log_debug "Adding '$DRIFTCTL_BIN_PATH' to \$PATH" +export PATH="$DRIFTCTL_BIN_PATH:$PATH" + +log_debug "Executing: \"$DRIFTCTL_BIN_PATH\" \"$@\"" +exec "$DRIFTCTL_BIN_PATH" "$@" || log_error "Failed to exec: \"$DRIFTCTL_BIN_PATH\" \"$*\"" diff --git a/libexec/dctlenv-install b/libexec/dctlenv-install new file mode 100755 index 0000000..91e8d25 --- /dev/null +++ b/libexec/dctlenv-install @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -uo pipefail + +[ "$#" -gt 1 ] && log_error "Need 1 version instead of $#" 'usage: dctlenv install []' + +version_requested="${1:-""}" +[ -n "$version_requested" ] || log_error 'Version is not specified' 'usage: dctlenv install []' + +version="$(dctlenv-list-remote | grep "$version_requested" | head -n 1)" +[ -n "$version" ] || log_error "No version $version_requested found in remote" + +dst_path="$DCTLENV_ROOT/versions/$version" +if [ -f "$dst_path/driftctl" ]; then + echo "driftctl v$version is already installed" + exit 0 +fi + +DCTLENV_ARCH="${DCTLENV_ARCH:-amd64}" +case "$(uname -s)" in + Darwin*) + os="darwin_$DCTLENV_ARCH" + ;; + MINGW64*) + os="windows_$DCTLENV_ARCH" + ;; + MSYS_NT*) + os="windows_$DCTLENV_ARCH" + ;; + CYGWIN_NT*) + os="windows_$DCTLENV_ARCH" + ;; + *) + os="linux_$DCTLENV_ARCH" + ;; +esac + +driftctl_url="https://github.com/cloudskiff/driftctl/releases/download" +tarball_name="driftctl_${version}_$os" + +echo "Installing driftctl v$version" + +echo "Downloading release tarball from $driftctl_url/v$version/driftctl_$os" +curlw -# -f -L -o "$dst_path/driftctl" --create-dirs "$driftctl_url/v$version/driftctl_$os" || log_error 'Tarball download failed' + +echo "Making the $dst_path/driftctl binary executable" +chmod +x "$dst_path/driftctl" + +echo "Installation of driftctl v${version} successful. To make this your default version, run 'dctlenv use ${version}'" diff --git a/libexec/dctlenv-list b/libexec/dctlenv-list new file mode 100755 index 0000000..24d77ce --- /dev/null +++ b/libexec/dctlenv-list @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -uo pipefail + +[ -d "$DCTLENV_ROOT/versions" ] || log_error 'No versions of driftctl installed. Please install one with: dctlenv install []' + +[[ -x "$DCTLENV_ROOT/versions" && -r "$DCTLENV_ROOT/versions" ]] \ + || log_error "dctlenv versions directory is inaccessible: $DCTLENV_ROOT/versions" + +version_file="$(dctlenv-version-file)" \ + && log_debug "dctlenv-version-file reported: $version_file" \ + || log_error "dctlenv-version-file failed" + +version_name="$(dctlenv-version-name)" \ + && log_debug "dctlenv-version-name reported: $version_name" \ + || log_error "dctlenv-version-name failed" + +log_debug 'Listing versions...' +local_versions=($(\find "$DCTLENV_ROOT/versions" -type d -exec basename {} \; \ + | tail -n +2 \ + | 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 + if [ "$local_version" == "$version_name" ]; then + echo "* $local_version (set by $version_file)" + else + echo " $local_version" + fi +done + diff --git a/libexec/dctlenv-list-remote b/libexec/dctlenv-list-remote new file mode 100755 index 0000000..bbc6896 --- /dev/null +++ b/libexec/dctlenv-list-remote @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -uo pipefail + +driftctl_url=https://api.github.com/repos/cloudskiff/driftctl/tags\?page\=1\&per_page\=1000 + +remote_versions="$(curlw -sSf $driftctl_url)" || log_error "Failed to download remote versions from GitHub" + +echo $remote_versions | grep -o '"name": "v[^"]*"' | cut -d '"' -f 4 | cut -c 2- | sort + diff --git a/libexec/dctlenv-use b/libexec/dctlenv-use new file mode 100755 index 0000000..d71cba1 --- /dev/null +++ b/libexec/dctlenv-use @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -uo pipefail + +[ "$#" -gt 1 ] && log_error "Need 1 version instead of $#" 'usage: dctlenv use []' + +[ -d "$DCTLENV_ROOT/versions" ] || log_error 'No versions of driftctl installed. Please install one with: dctlenv install []' + +version_requested="${1:-""}" +[ -n "$version_requested" ] || log_error 'Version is not specified' 'usage: dctlenv use []' + +log_debug "Searching $DCTLENV_ROOT/versions for version matching $version_requested" +version="$(\find "$DCTLENV_ROOT/versions" -type d -exec basename {} \; \ + | tail -n +2 \ + | sort -t'.' -k 1nr,1 -k 2nr,2 -k 3nr,3 \ + | grep -e "$version_requested" \ + | head -n 1 +)" + +[ -n "$version" ] \ + && log_debug "Found version: $version" \ + || log_error "No installed versions of driftctl matched '$1'" + +target_path="$DCTLENV_ROOT/versions/$version" +[ -f "$target_path/driftctl" ] \ + || log_error "Version directory for $version is present, but the driftctl binary is not! Manual intervention required" +[ -x "$target_path/driftctl" ] \ + || log_error "Version directory for $version is present, but the driftctl binary is not executable! Manual intervention required" + +echo "Switching version to v$version" +version_file="$(dctlenv-version-file)" + +log_debug "Writing \"$version\" to \"$version_file\"" +echo "$version" > "$version_file" || log_error "Failed to switch to v$version" + +driftctl version 1>/dev/null || log_error "'driftctl version' failed, something is wrong" + +echo "Switching completed" diff --git a/libexec/dctlenv-version-file b/libexec/dctlenv-version-file new file mode 100755 index 0000000..14552dd --- /dev/null +++ b/libexec/dctlenv-version-file @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -uo pipefail + +echo "$DCTLENV_ROOT/version" diff --git a/libexec/dctlenv-version-name b/libexec/dctlenv-version-name new file mode 100755 index 0000000..e3a0717 --- /dev/null +++ b/libexec/dctlenv-version-name @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -uo pipefail + +[ -d "$DCTLENV_ROOT/versions" ] || log_error 'No versions of driftctl installed. Please install one with: dctlenv install []' + +DCTLENV_VERSION_FILE="$(dctlenv-version-file)" \ + && log_debug "DCTLENV_VERSION_FILE retrieved: $DCTLENV_VERSION_FILE" \ + || log_error 'Failed to retrieve DCTLENV_VERSION_FILE' + +DCTLENV_VERSION="$(cat "$DCTLENV_VERSION_FILE" 2>/dev/null)" \ + && log_debug "DCTLENV_VERSION specified in DCTLENV_VERSION_FILE: $DCTLENV_VERSION" \ + || log_error "Version could not be resolved (set by $DCTLENV_VERSION_FILE or dctlenv use )" + +if [ ! -d "$DCTLENV_ROOT/versions/$DCTLENV_VERSION" ]; then + log_error "Version '$DCTLENV_VERSION' is not installed (set by $DCTLENV_VERSION_FILE)" +fi + +echo $DCTLENV_VERSION