From 3f45c002166c3b5e3b44042729f7716601dd8d62 Mon Sep 17 00:00:00 2001
From: kvendingoldo
Date: Fri, 5 Jan 2024 13:25:20 +0100
Subject: [PATCH 01/32] Initial commit
---
README.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..57185b3
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# tenv
\ No newline at end of file
From e6814537bef56861cdef21ce9115312129f0ecbc Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Sun, 7 Jan 2024 15:03:40 +0100
Subject: [PATCH 02/32] feat: init commit
---
.gitignore | 10 ++++++++++
LICENSE | 22 ++++++++++++++++++++++
README.md | 25 ++++++++++++++++++++++++-
3 files changed, 56 insertions(+), 1 deletion(-)
create mode 100644 .gitignore
create mode 100644 LICENSE
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3aa3cb7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+versions/
+version
+.opentofu-version
+.terraform-version
+bin/tofu-*
+bin/terraform-*
+/use-gnupg
+/use-gpgv
+.*.swp
+.idea
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b27740f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+
+The MIT License (MIT)
+
+Copyright (c) 2024 Nikolai Mishin, Alexander Sharov, Anastasiia Kozlova
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 57185b3..e2ed1d0 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,24 @@
-# tenv
\ No newline at end of file
+# tenv
+
+[tenv](https://github.com/tofuutils/tenv) version manager that build on top of [tofuenv](https://github.com/tofuutils/tofuenv) and [tfenv](https://github.com/tfutils/tfenv) and manages [Terraform](https://www.terraform.io/) and [OpenTofu](https://opentofu.org/) binaries
+
+## Support
+
+Currently tenv supports the following operating systems:
+
+- macOS
+ - 64bit
+ - Arm (Apple Silicon)
+- Linux
+ - 64bit
+ - Arm
+- Windows (64bit) - only tested in git-bash - currently presumed failing due to symlink issues in git-bash
+
+## Installation
+WIP
+
+## LICENSE
+- [tenv inself](https://github.com/tofuutils/tenv/blob/main/LICENSE)
+- [tofuenv](https://github.com/tofuutils/tofuenv/blob/main/LICENSE)
+- [tfenv](https://github.com/tfutils/tfenv/blob/master/LICENSE)
+ - tofuenv uses tfenv's source code
From 891e8d2607978ae8609c69d57f1b7406906afcc5 Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Sun, 7 Jan 2024 15:37:47 +0100
Subject: [PATCH 03/32] faet: initial design
---
README.md | 2 +-
lib/tenv-bashlog.sh | 175 ++++++++++++++++++++++++++++++++++++++++++++
libexec/tenv-help | 16 ++++
3 files changed, 192 insertions(+), 1 deletion(-)
create mode 100755 lib/tenv-bashlog.sh
create mode 100755 libexec/tenv-help
diff --git a/README.md b/README.md
index e2ed1d0..1097ee8 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
## Support
-Currently tenv supports the following operating systems:
+Currently, tenv supports the following operating systems:
- macOS
- 64bit
diff --git a/lib/tenv-bashlog.sh b/lib/tenv-bashlog.sh
new file mode 100755
index 0000000..6ae2cbe
--- /dev/null
+++ b/lib/tenv-bashlog.sh
@@ -0,0 +1,175 @@
+#!/usr/bin/env bash
+
+set -uo pipefail;
+
+function _log_exception() {
+ (
+ BASHLOG_FILE=0;
+ BASHLOG_JSON=0;
+ BASHLOG_SYSLOG=0;
+
+ log 'error' "Logging Exception: $*";
+ );
+};
+export -f _log_exception;
+
+function log() {
+ local syslog="${BASHLOG_SYSLOG:-0}";
+ local file="${BASHLOG_FILE:-0}";
+ local json="${BASHLOG_JSON:-0}";
+ local stdout_extra="${BASHLOG_EXTRA:-0}";
+
+ if [ "${file}" -eq 1 ] || [ "${json}" -eq 1 ] || [ "${stdout_extra}" -eq 1 ]; then
+ local date_format="${BASHLOG_DATE_FORMAT:-+%F %T}";
+ local date="$(date "${date_format}")";
+ local date_s="$(date "+%s")";
+ fi
+ local file_path="${BASHLOG_FILE_PATH:-/tmp/${0##*/}.log}";
+ local json_path="${BASHLOG_JSON_PATH:-/tmp/${0##*/}.log.json}";
+
+ local tag="${BASHLOG_SYSLOG_TAG:-${0##*/})}";
+ local facility="${BASHLOG_SYSLOG_FACILITY:-local0}";
+ local pid="${$}";
+
+ local level="${1}";
+ local upper="$(echo "${level}" | awk '{print toupper($0)}')";
+ local debug_level="${TOFUENV_DEBUG:-0}";
+ local stdout_colours="${BASHLOG_COLOURS:-1}";
+
+ local custom_eval_prefix="${BASHLOG_I_PROMISE_TO_BE_CAREFUL_CUSTOM_EVAL_PREFIX:-""}";
+
+ shift 1;
+
+ local line="$@";
+
+ # RFC 5424
+ #
+ # Numerical Severity
+ # Code
+ #
+ # 0 Emergency: system is unusable
+ # 1 Alert: action must be taken immediately
+ # 2 Critical: critical conditions
+ # 3 Error: error conditions
+ # 4 Warning: warning conditions
+ # 5 Notice: normal but significant condition
+ # 6 Informational: informational messages
+ # 7 Debug: debug-level messages
+
+ local severities_DEBUG=7;
+ local severities_INFO=6;
+ local severities_NOTICE=5; # Unused
+ local severities_WARN=4;
+ local severities_ERROR=3;
+ local severities_CRIT=2; # Unused
+ local severities_ALERT=1; # Unused
+ local severities_EMERG=0; # Unused
+
+ local severity_var="severities_${upper}";
+ local severity="${!severity_var:-3}";
+
+ if [ "${debug_level}" -gt 0 ] || [ "${severity}" -lt 7 ]; then
+
+ if [ "${syslog}" -eq 1 ]; then
+ local syslog_line="${upper}: ${line}";
+
+ logger \
+ --id="${pid}" \
+ -t "${tag}" \
+ -p "${facility}.${severity}" \
+ "${syslog_line}" \
+ || _log_exception "logger --id=\"${pid}\" -t \"${tag}\" -p \"${facility}.${severity}\" \"${syslog_line}\"";
+ fi;
+
+ if [ "${file}" -eq 1 ]; then
+ local file_line="${date} [${upper}] ${line}";
+
+ if [ -n "${custom_eval_prefix}" ]; then
+ file_line="$(eval "${custom_eval_prefix}")${file_line}";
+ fi;
+
+ echo -e "${file_line}" >> "${file_path}" \
+ || _log_exception "echo -e \"${file_line}\" >> \"${file_path}\"";
+ fi;
+
+ if [ "${json}" -eq 1 ]; then
+ local json_line="$(printf '{"timestamp":"%s","level":"%s","message":"%s"}' "${date_s}" "${level}" "${line}")";
+ echo -e "${json_line}" >> "${json_path}" \
+ || _log_exception "echo -e \"${json_line}\" >> \"${json_path}\"";
+ fi;
+
+ fi;
+
+ local colours_DEBUG='\033[34m' # Blue
+ local colours_INFO='\033[32m' # Green
+ local colours_NOTICE='' # Unused
+ local colours_WARN='\033[33m' # Yellow
+ local colours_ERROR='\033[31m' # Red
+ local colours_CRIT='' # Unused
+ local colours_ALERT='' # Unused
+ local colours_EMERG='' # Unused
+ local colours_DEFAULT='\033[0m' # Default
+
+ local norm="${colours_DEFAULT}";
+ local colour_var="colours_${upper}";
+ local colour="${!colour_var:-\033[31m}";
+
+ local std_line;
+ if [ "${debug_level}" -le 1 ]; then
+ std_line="${line}";
+ elif [ "${debug_level}" -ge 2 ]; then
+ std_line="${0}: ${line}";
+ fi;
+
+ if [ "${stdout_extra}" -eq 1 ]; then
+ std_line="${date} [${upper}] ${std_line}";
+ fi;
+
+ if [ -n "${custom_eval_prefix}" ]; then
+ std_line="$(eval "${custom_eval_prefix}")${std_line}";
+ fi;
+
+ if [ "${stdout_colours}" -eq 1 ]; then
+ std_line="${colour}${std_line}${norm}";
+ fi;
+
+ # Standard Output (Pretty)
+ case "${level}" in
+ 'info'|'warn')
+ echo -e "${std_line}";
+ ;;
+ 'debug')
+ if [ "${debug_level}" -gt 0 ]; then
+ # We are debugging to STDERR on purpose
+ # tofuenv relies on STDOUT between libexecs to function
+ echo -e "${std_line}" >&2;
+ fi;
+ ;;
+ 'error')
+ echo -e "${std_line}" >&2;
+ if [ "${debug_level}" -gt 1 ]; then
+ echo -e "Here's a shell for debugging the current environment. 'exit 0' to resume script from here. Non-zero exit code will abort - parent shell will terminate." >&2;
+ bash || exit "${?}";
+ else
+ exit 1;
+ fi;
+ ;;
+ *)
+ log 'error' "Undefined log level trying to log: $*";
+ ;;
+ esac
+};
+export -f log;
+
+declare prev_cmd="null";
+declare this_cmd="null";
+trap 'prev_cmd=${this_cmd:-null}; 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/libexec/tenv-help b/libexec/tenv-help
new file mode 100755
index 0000000..6b199f4
--- /dev/null
+++ b/libexec/tenv-help
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+set -uo pipefail;
+
+echo 'Usage: tenv []
+
+Commands:
+ install Install tenv dependencies (tfenv and tofuenv)
+ uninstall Uninstall tenv dependencies (tfenv and tofuenv)
+ tofu List of tofuenv commands
+ tf List of tfenv commands
+ version-name Print current version of tenv
+ help Show the tenv help
+';
+
+exit 0;
\ No newline at end of file
From 63dca1e979dd45c9446d943741944294fb5ff391 Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Sun, 7 Jan 2024 15:43:40 +0100
Subject: [PATCH 04/32] feat: add tenv init command
---
.gitignore | 1 +
libexec/tenv-help | 1 +
libexec/tenv-init | 5 +++++
3 files changed, 7 insertions(+)
create mode 100755 libexec/tenv-init
diff --git a/.gitignore b/.gitignore
index 3aa3cb7..82c671b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ bin/terraform-*
/use-gpgv
.*.swp
.idea
+.misc
\ No newline at end of file
diff --git a/libexec/tenv-help b/libexec/tenv-help
index 6b199f4..1d1d5aa 100755
--- a/libexec/tenv-help
+++ b/libexec/tenv-help
@@ -5,6 +5,7 @@ set -uo pipefail;
echo 'Usage: tenv []
Commands:
+ init Update environment to use 'tenv' correctly.
install Install tenv dependencies (tfenv and tofuenv)
uninstall Uninstall tenv dependencies (tfenv and tofuenv)
tofu List of tofuenv commands
diff --git a/libexec/tenv-init b/libexec/tenv-init
new file mode 100755
index 0000000..93554e1
--- /dev/null
+++ b/libexec/tenv-init
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+[ -n "${TENV_DEBUG}" ] && set -x;
+
+export PATH="${TENV_ROOT}/bin:${PATH}";
From 1b00adfd9c21d77b6b525d1772539915f3ddeff6 Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Sun, 7 Jan 2024 15:54:45 +0100
Subject: [PATCH 05/32] feat: add codebase
---
lib/tenv-helpers.sh | 164 +++++++++++++++++++++++++
libexec/tenv-exec | 75 ++++++++++++
libexec/tenv-tf | 7 ++
libexec/tenv-tofu | 5 +
libexec/tofuenv-install | 204 ++++++++++++++++++++++++++++++++
libexec/tofuenv-resolve-version | 168 ++++++++++++++++++++++++++
6 files changed, 623 insertions(+)
create mode 100755 lib/tenv-helpers.sh
create mode 100755 libexec/tenv-exec
create mode 100755 libexec/tenv-tf
create mode 100755 libexec/tenv-tofu
create mode 100755 libexec/tofuenv-install
create mode 100755 libexec/tofuenv-resolve-version
diff --git a/lib/tenv-helpers.sh b/lib/tenv-helpers.sh
new file mode 100755
index 0000000..1d215c2
--- /dev/null
+++ b/lib/tenv-helpers.sh
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+set -uo pipefail;
+
+if [ -z "${TOFUENV_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 "${target_file%/*}" || early_death "Failed to 'cd \$(${target_file%/*})' while trying to determine TOFUENV_ROOT";
+ file_name="${target_file##*/}" || early_death "Failed to '\"${target_file##*/}\"' while trying to determine TOFUENV_ROOT";
+ target_file="$(readlink "${file_name}")";
+ done;
+
+ echo "$(pwd -P)/${file_name}";
+ };
+ TOFUENV_SHIM=$(readlink_f "${0}")
+ TOFUENV_ROOT="${TOFUENV_SHIM%/*/*}";
+ [ -n "${TOFUENV_ROOT}" ] || early_death "Failed to determine TOFUENV_ROOT";
+else
+ TOFUENV_ROOT="${TOFUENV_ROOT%/}";
+fi;
+export TOFUENV_ROOT;
+
+if [ -z "${TOFUENV_CONFIG_DIR:-""}" ]; then
+ TOFUENV_CONFIG_DIR="$TOFUENV_ROOT";
+else
+ TOFUENV_CONFIG_DIR="${TOFUENV_CONFIG_DIR%/}";
+fi
+export TOFUENV_CONFIG_DIR;
+
+if [ "${TOFUENV_DEBUG:-0}" -gt 0 ]; then
+ # Only reset DEBUG if TOFUENV_DEBUG is set, and DEBUG is unset or already a number
+ if [[ "${DEBUG:-0}" =~ ^[0-9]+$ ]] && [ "${DEBUG:-0}" -gt "${TOFUENV_DEBUG:-0}" ]; then
+ export DEBUG="${TOFUENV_DEBUG:-0}";
+ fi;
+ if [[ "${TOFUENV_DEBUG}" -gt 2 ]]; then
+ export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] ';
+ set -x;
+ fi;
+fi;
+
+function load_bashlog () {
+ source "${TOFUENV_ROOT}/lib/tofuenv-bashlog.sh";
+};
+export -f load_bashlog;
+
+if [ "${TOFUENV_DEBUG:-0}" -gt 0 ] ; then
+ # our shim below cannot be used when debugging is enabled
+ load_bashlog;
+else
+ # Shim that understands to no-op for debug messages, and defers to
+ # full bashlog for everything else.
+ function log () {
+ if [ "$1" != 'debug' ] ; then
+ # Loading full bashlog will overwrite the `log` function
+ load_bashlog;
+ log "$@";
+ fi;
+ };
+ export -f log;
+fi;
+
+# 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;
+
+ if [[ ! -z "${TOFUENV_NETRC_PATH:-""}" ]]; then
+ NETRC_OPT="--netrc-file ${TOFUENV_NETRC_PATH}";
+ else
+ NETRC_OPT="";
+ fi;
+
+ if [[ ! -z "${TOFUENV_GITHUB_TOKEN:-""}" ]]; then
+ AUTHORIZATION_HEADER="Authorization: Bearer ${TOFUENV_GITHUB_TOKEN}";
+ else
+ AUTHORIZATION_HEADER="";
+ fi;
+ curl ${TLS_OPT} -H "${AUTHORIZATION_HEADER}" ${NETRC_OPT} "$@";
+};
+export -f curlw;
+
+function check_active_version() {
+ local v="${1}";
+ local maybe_chdir=;
+ if [ -n "${2:-}" ]; then
+ maybe_chdir="-chdir=${2}";
+ fi;
+
+ local active_version="$(${TOFUENV_ROOT}/bin/tofu ${maybe_chdir} version | grep '^OpenTofu')";
+
+ if ! grep -E "^OpenTofu v${v}((-dev)|( \([a-f0-9]+\)))?( is already installed)?\$" <(echo "${active_version}"); then
+ log 'debug' "Expected version ${v} but found ${active_version}";
+ return 1;
+ fi;
+
+ log 'debug' "Active version ${v} as expected";
+ return 0;
+};
+export -f check_active_version;
+
+function check_installed_version() {
+ local v="${1}";
+ local bin="${TOFUENV_CONFIG_DIR}/versions/${v}/tofu";
+ [ -n "$(${bin} version | grep -E "^OpenTofu v${v}((-dev)|( \([a-f0-9]+\)))?$")" ];
+};
+export -f check_installed_version;
+
+function check_default_version() {
+ local v="${1}";
+ local def="$(cat "${TOFUENV_CONFIG_DIR}/version")";
+ [ "${def}" == "${v}" ];
+};
+export -f check_default_version;
+
+function cleanup() {
+ log 'info' 'Performing cleanup';
+ local pwd="$(pwd)";
+ log 'debug' "Deleting ${pwd}/version";
+ rm -rf ./version;
+ log 'debug' "Deleting ${pwd}/versions";
+ rm -rf ./versions;
+ log 'debug' "Deleting ${pwd}/.opentofu-version";
+ rm -rf ./.opentofu-version;
+ log 'debug' "Deleting ${pwd}/latest_allowed.tf";
+ rm -rf ./latest_allowed.tf;
+ log 'debug' "Deleting ${pwd}/min_required.tf";
+ rm -rf ./min_required.tf;
+ log 'debug' "Deleting ${pwd}/chdir-dir";
+ rm -rf ./chdir-dir;
+};
+export -f cleanup;
+
+function error_and_proceed() {
+ errors+=("${1}");
+ log 'warn' "Test Failed: ${1}";
+};
+export -f error_and_proceed;
+
+function check_dependencies() {
+ if [[ $(uname) == 'Darwin' ]] && [ $(which brew) ]; then
+ if ! [ $(which ggrep) ]; then
+ log 'error' 'A metaphysical dichotomy has caused this unit to overload and shut down. GNU Grep is a requirement and your Mac does not have it. Consider "brew install grep"';
+ fi;
+
+ shopt -s expand_aliases;
+ alias grep=ggrep;
+ fi;
+};
+export -f check_dependencies;
+
+source "$TOFUENV_ROOT/lib/tofuenv-exec.sh";
+source "$TOFUENV_ROOT/lib/tofuenv-min-required.sh";
+source "$TOFUENV_ROOT/lib/tofuenv-version-file.sh";
+source "$TOFUENV_ROOT/lib/tofuenv-version-name.sh";
+
+export TOFUENV_HELPERS=1;
diff --git a/libexec/tenv-exec b/libexec/tenv-exec
new file mode 100755
index 0000000..8dd3d58
--- /dev/null
+++ b/libexec/tenv-exec
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+#
+# Summary: TODO
+#
+# Usage: tenv exec [arg1 arg2...]
+#
+# Runs an executable by first preparing PATH so that the selected OpenTofu
+# version's `bin' directory is at the front.
+#
+# For example, if the currently selected OpenTofu version is 1.6.0:
+# tofuenv exec plan
+#
+# is equivalent to:
+# PATH="$TENV_ROOT/versions/1.6.0/bin:$PATH" tofu plan
+
+set -uo pipefail;
+
+####################################
+# Ensure we can execute standalone #
+####################################
+
+function early_death() {
+ echo "[FATAL] ${0}: ${1}" >&2;
+ exit 1;
+};
+
+if [ -z "${TENV_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 TENV_ROOT";
+ file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TENV_ROOT";
+ target_file="$(readlink "${file_name}")";
+ done;
+
+ echo "$(pwd -P)/${file_name}";
+ };
+
+ TENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
+ [ -n "${TENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TENV_ROOT";
+else
+ TENV_ROOT="${TENV_ROOT%/}";
+fi;
+export TENV_ROOT;
+
+if [ -n "${TOFUENV_HELPERS:-""}" ]; then
+ log 'debug' 'TOFUENV_HELPERS is set, not sourcing helpers again';
+else
+ [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
+ if source "${TENV_ROOT}/lib/tenv-helpers.sh"; then
+ log 'debug' 'Helpers sourced successfully';
+ else
+ early_death "Failed to source helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
+ fi;
+fi;
+
+# Ensure libexec and bin are in $PATH
+for dir in libexec bin; do
+ case ":${PATH}:" in
+ *:${TENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TENV_ROOT}/${dir}', not adding it again";;
+ *)
+ log 'debug' "\$PATH does not contain '${TENV_ROOT}/${dir}', prepending and exporting it now";
+ export PATH="${TENV_ROOT}/${dir}:${PATH}";
+ ;;
+ esac;
+done;
+
+#####################
+# Begin Script Body #
+#####################
+
+tenv-exec "$@";
diff --git a/libexec/tenv-tf b/libexec/tenv-tf
new file mode 100755
index 0000000..cff7ce7
--- /dev/null
+++ b/libexec/tenv-tf
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+# Usage: tenv tf
+# Summary: TODO
+
+set -uo pipefail;
+
+tfenv
\ No newline at end of file
diff --git a/libexec/tenv-tofu b/libexec/tenv-tofu
new file mode 100755
index 0000000..5332b28
--- /dev/null
+++ b/libexec/tenv-tofu
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+# Usage: tofuenv version-file
+# Summary: Detect the file that sets the current tofuenv version
+
+set -uo pipefail;
\ No newline at end of file
diff --git a/libexec/tofuenv-install b/libexec/tofuenv-install
new file mode 100755
index 0000000..b033cfc
--- /dev/null
+++ b/libexec/tofuenv-install
@@ -0,0 +1,204 @@
+#!/usr/bin/env bash
+set -uo pipefail;
+
+####################################
+# Ensure we can execute standalone #
+####################################
+
+function early_death() {
+ echo "[FATAL] ${0}: ${1}" >&2;
+ exit 1;
+};
+
+if [ -z "${TENV_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 TENV_ROOT";
+ file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TENV_ROOT";
+ target_file="$(readlink "${file_name}")";
+ done;
+
+ echo "$(pwd -P)/${file_name}";
+ };
+
+ TENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
+ [ -n "${TENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TENV_ROOT";
+else
+ TENV_ROOT="${TENV_ROOT%/}";
+fi;
+export TENV_ROOT;
+
+if [ -n "${TENV_HELPERS:-""}" ]; then
+ log 'debug' 'TENV_HELPERS is set, not sourcing helpers again';
+else
+ [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
+ if source "${TENV_ROOT}/lib/tenv-helpers.sh"; then
+ log 'debug' 'Helpers sourced successfully';
+ else
+ early_death "Failed to source helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
+ fi;
+fi;
+
+# Ensure libexec and bin are in $PATH
+for dir in libexec bin; do
+ case ":${PATH}:" in
+ *:${TENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TENV_ROOT}/${dir}', not adding it again";;
+ *)
+ log 'debug' "\$PATH does not contain '${TENV_ROOT}/${dir}', prepending and exporting it now";
+ export PATH="${TENV_ROOT}/${dir}:${PATH}";
+ ;;
+ esac;
+done;
+
+#####################
+# Begin Script Body #
+#####################
+
+[ "${#}" -gt 1 ] && log 'error' 'usage: tenv install []';
+
+declare requested="${1:-""}";
+
+log debug "Resolving version with: tenv-resolve-version ${requested}";
+declare resolved;
+# TODO
+resolved="$(tenv-resolve-version ${requested})" || log 'error' "Failed to resolve ${requested} version";
+
+declare version="${resolved%%\:*}";
+declare regex="${resolved##*\:}";
+
+[ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest';
+
+log 'debug' "Processing install for version ${version}, using regex ${regex}";
+
+#if [[ ${version} =~ ${regex:-not} ]]; then
+# log 'debug' "Version and regex matched";
+#else
+# log 'debug' "Version and regex not matched";
+# if [ "${TOFUENV_SKIP_LIST_REMOTE:-0}" -eq 0 ]; then
+# remote_version="$(tofuenv-list-remote | grep -e "${regex}" | head -n 1)";
+# [ -n "${remote_version}" ] && version="${remote_version}" || log 'error' "No ${version} versions matching '${regex}' found in remote";
+# fi;
+#fi;
+
+# ???????
+dst_path="${TENV_CONFIG_DIR}/versions/${version}";
+if [ -f "${dst_path}/tofu" ]; then
+ echo "OpenTofu v${version} is already installed";
+ exit 0;
+fi;
+
+
+
+
+
+os="${kernel}_${TOFUENV_ARCH}"
+
+keybase_bin="$(command -v keybase 2>/dev/null)";
+shasum_bin="$(command -v shasum 2>/dev/null)";
+sha256sum_bin="$(command -v sha256sum 2>/dev/null)";
+
+TOFUENV_REMOTE="${TOFUENV_REMOTE:-https://github.com/opentofu/opentofu/releases}";
+version_url="${TOFUENV_REMOTE}/download/v${version}";
+
+tarball_name="tofu_${version}_${os}.zip";
+
+shasums_name="tofu_${version}_SHA256SUMS";
+shasums_sig="${shasums_name}.sig";
+
+log 'info' "Installing OpenTofu v${version}";
+
+# Create a local temporary directory for downloads
+tmpdir_arg="-t";
+
+if mktemp --help 2>&1 | grep -- '--tmpdir' >/dev/null; then
+ tmpdir_arg="--tmpdir";
+fi;
+
+download_tmp="$(mktemp -d ${tmpdir_arg} tofuenv_download.XXXXXX)" || log 'error' "Unable to create temporary download directory (mktemp -d ${tmpdir_arg} tofuenv_download.XXXXXX). Working Directory is: $(pwd)";
+
+# Clean it up in case of error
+trap "rm -rf ${download_tmp}" EXIT;
+
+declare curl_progress="";
+case "${TOFUENV_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' 'TOFUENV_CURL_OUTPUT specified, but not with a supported value ([0,1,2])';
+ ;;
+esac;
+
+log 'info' "Downloading release tarball from ${version_url}/${tarball_name}";
+
+status=$(curlw ${curl_progress} -w "%{http_code}" -f -L -o "${download_tmp}/${tarball_name}" "${version_url}/${tarball_name}");
+
+case "${status}" in
+ 200) log 'debug' "'${requested:-$version}' version download successfully" ;;
+ 403) log 'error' "GitHub Rate limits exceeded" ;;
+ 404) log 'error' "No versions matching '${requested:-$version}' found in remote" ;;
+ *) log 'error' "Unknown error, status code = ${status}" ;;
+esac;
+
+log 'info' "Downloading SHA hash file from ${version_url}/${shasums_name}";
+curlw -s -f -L -o "${download_tmp}/${shasums_name}" "${version_url}/${shasums_name}" || log 'error' 'SHA256 hashes download failed';
+
+download_signature() {
+ log 'info' "Downloading SHA hash signature file from ${version_url}/${shasums_sig}";
+ curlw -s -f -L \
+ -o "${download_tmp}/${shasums_sig}" \
+ "${version_url}/${shasums_sig}" \
+ && log 'debug' "SHA256SUMS signature file downloaded successfully to ${download_tmp}/${shasums_sig}" \
+ || log 'error' 'SHA256SUMS signature download failed';
+};
+
+# If on MacOS with Homebrew, use GNU grep
+# This allows keybase login detection to work on Mac,
+# and is required to be able to detect tofu version
+# from "required_version" setting in "*.tf" files
+check_dependencies;
+
+
+
+
+if [[ -n "${shasum_bin}" && -x "${shasum_bin}" ]]; then
+ (
+ cd "${download_tmp}";
+ "${shasum_bin}" \
+ -a 256 \
+ -s \
+ -c <(grep -F "${tarball_name}" "${shasums_name}")
+ ) || log 'error' 'SHA256 hash does not match!';
+elif [[ -n "${sha256sum_bin}" && -x "${sha256sum_bin}" ]]; then
+ (
+ cd "${download_tmp}";
+ "${sha256sum_bin}" \
+ -c <(grep -F "${tarball_name}" "${shasums_name}")
+ ) || log 'error' 'SHA256 hash does not match!';
+else
+ # Lack of shasum deserves a proper warning
+ log 'warn' 'No shasum tool available. Skipping SHA256 hash validation';
+fi;
+
+mkdir -p "${dst_path}" || log 'error' "Failed to make directory ${dst_path}";
+
+declare unzip_output;
+unzip_output="$(unzip -o "${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}");
+
+log 'info' "Installation of tofu v${version} successful. To make this your default version, run 'tofuenv use ${version}'";
diff --git a/libexec/tofuenv-resolve-version b/libexec/tofuenv-resolve-version
new file mode 100755
index 0000000..732cfcb
--- /dev/null
+++ b/libexec/tofuenv-resolve-version
@@ -0,0 +1,168 @@
+#!/usr/bin/env bash
+# Usage: tofuenv resolve-version []
+# Summary: Resolve the version to action based on the environment and optional input token
+
+set -uo pipefail;
+
+####################################
+# Ensure we can execute standalone #
+####################################
+
+function early_death() {
+ echo "[FATAL] ${0}: ${1}" >&2;
+ exit 1;
+};
+
+if [ -z "${TOFUENV_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 TOFUENV_ROOT";
+ file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TOFUENV_ROOT";
+ target_file="$(readlink "${file_name}")";
+ done;
+
+ echo "$(pwd -P)/${file_name}";
+ };
+
+ TOFUENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
+ [ -n "${TOFUENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TOFUENV_ROOT";
+else
+ TOFUENV_ROOT="${TOFUENV_ROOT%/}";
+fi;
+export TOFUENV_ROOT;
+
+if [ -n "${TOFUENV_HELPERS:-""}" ]; then
+ log 'debug' 'TOFUENV_HELPERS is set, not sourcing helpers again';
+else
+ [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TOFUENV_ROOT}/lib/tofuenv-helpers.sh";
+ if source "${TOFUENV_ROOT}/lib/tofuenv-helpers.sh"; then
+ log 'debug' 'Helpers sourced successfully';
+ else
+ early_death "Failed to source helpers from ${TOFUENV_ROOT}/lib/tofuenv-helpers.sh";
+ fi;
+fi;
+
+# Ensure libexec and bin are in $PATH
+for dir in libexec bin; do
+ case ":${PATH}:" in
+ *:${TOFUENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TOFUENV_ROOT}/${dir}', not adding it again";;
+ *)
+ log 'debug' "\$PATH does not contain '${TOFUENV_ROOT}/${dir}', prepending and exporting it now";
+ export PATH="${TOFUENV_ROOT}/${dir}:${PATH}";
+ ;;
+ esac;
+done;
+
+# If on MacOS with Homebrew, use GNU grep
+# This allows keybase login detection to work on Mac,
+# and is required to be able to detect tofu version
+# from "required_version" setting in "*.tf" files
+check_dependencies;
+
+#####################
+# Begin Script Body #
+#####################
+
+declare version_requested version regex min_required version_file;
+
+declare arg="${1:-""}";
+
+if [ -z "${arg}" -a -z "${TOFUENV_TOFU_VERSION:-""}" ]; then
+ version_file="$(tofuenv-version-file)";
+ log 'debug' "Version File: ${version_file}";
+
+ if [ "${version_file}" != "${TOFUENV_CONFIG_DIR}/version" ]; then
+ log 'debug' "Version File (${version_file}) is not the default \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/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 \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/version)";
+ version_requested="$(cat "${version_file}")" \
+ || log 'error' "Failed to open ${version_file}";
+
+ # Absolute fallback
+ if [ -z "${version_requested}" ]; then
+ log 'debug' 'Version file had no content. Falling back to "latest"';
+ version_requested='latest';
+ fi;
+
+ else
+ log 'debug' "Version File is the default \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/version) but it doesn't exist";
+ log 'debug' 'No version requested on the command line or in the version file search path. Installing "latest"';
+ version_requested='latest';
+ fi;
+elif [ -n "${TOFUENV_TOFU_VERSION:-""}" ]; then
+ version_requested="${TOFUENV_TOFU_VERSION}";
+ log 'debug' "TOFUENV_TOFU_VERSION is set: ${TOFUENV_TOFU_VERSION}";
+else
+ version_requested="${arg}";
+fi;
+
+[[ -n "${version_requested:-""}" ]] \
+ && log 'debug' "Version Requested: ${version_requested}" \
+ || log 'error' 'Version could not be resolved!';
+
+# Accept a v-prefixed version, but strip the v.
+if [[ "${version_requested}" =~ ^v.*$ ]]; then
+ log 'debug' "Version Requested is prefixed with a v. Stripping the v.";
+ version_requested="${version_requested#v*}";
+fi;
+
+if [[ "${version_requested}" =~ ^min-required$ ]]; then
+ log 'debug' 'Detecting minimum required version...';
+ min_required="$(tofuenv-min-required)" \
+ || log 'error' 'tofuenv-min-required failed';
+
+ if [ -z "${min_required}" ]; then
+ log 'debug' 'It was not possible to detect a minimum-required version. Do you have a required_version line present?';
+ exit;
+ fi;
+
+ log 'debug' "Minimum required version detected: ${min_required}";
+ version_requested="${min_required}";
+fi;
+
+if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
+ log 'debug' 'Detecting latest allowable version...';
+ version_spec="$(grep -h required_version "${TOFUENV_DIR:-$(pwd)}"/{*.tf,*.tf.json} 2>/dev/null | { IFS='"' read -r _ ver _; echo "${ver%%,*}"; })";
+ version_num="$(echo "${version_spec}" | sed -E 's/[^0-9.]+//')";
+ log 'debug' "Using ${version_num} from version spec: ${version_spec}";
+
+ case "${version_spec}" in
+ '>'*)
+ version_requested=latest;
+ ;;
+ '<='*)
+ version_requested="${version_num}";
+ ;;
+ '~>'*)
+ version_without_rightmost="${version_num%.*}";
+ version_requested="latest:^${version_without_rightmost}";
+ ;;
+ *)
+ log 'error' "Unsupported version spec: '${version_spec}', only >, >=, <=, and ~> are supported.";
+ ;;
+ esac;
+ log 'debug' "Determined the requested version to be: ${version_requested}";
+fi;
+
+if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
+ version="${version_requested%%\:*}";
+ regex="${version_requested##*\:}";
+ log 'debug' "Version uses latest keyword with regex: ${regex}";
+elif [[ "${version_requested}" =~ ^latest$ ]]; then
+ version="${version_requested}";
+ regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$";
+ log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}";
+else
+ version="${version_requested}";
+ regex="^${version_requested}$";
+ log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}";
+fi;
+
+echo "${version}:${regex}";
From fdf2120ffe170101754f42a42a5a869252ef712e Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 12:40:54 +0100
Subject: [PATCH 06/32] feat: migrate tfenv to golang
---
.github/workflows/build.yml | 54 ++++++++
.github/workflows/golangci-lint.yml | 28 ++++
.gitignore | 21 ++-
.golangci.yml | 38 ++++++
Dockerfile | 17 +++
LICENSE | 22 ---
Makefile | 23 ++++
cmd/init.go | 39 ++++++
cmd/root.go | 47 +++++++
cmd/tf.go | 39 ++++++
cmd/tofu.go | 39 ++++++
cmd/uninstallDeps.go | 39 ++++++
cmd/upgradeDeps.go | 54 ++++++++
go.mod | 10 ++
go.sum | 10 ++
lib/tenv-bashlog.sh | 175 ------------------------
lib/tenv-helpers.sh | 164 ----------------------
libexec/tenv-exec | 75 ----------
libexec/tenv-help | 17 ---
libexec/tenv-init | 5 -
libexec/tenv-tf | 7 -
libexec/tenv-tofu | 5 -
libexec/tofuenv-install | 204 ----------------------------
libexec/tofuenv-resolve-version | 168 -----------------------
main.go | 12 ++
pkg/utils/abc.go | 14 ++
pkg/utils/github.go | 79 +++++++++++
27 files changed, 562 insertions(+), 843 deletions(-)
create mode 100644 .github/workflows/build.yml
create mode 100644 .github/workflows/golangci-lint.yml
create mode 100644 .golangci.yml
create mode 100644 Dockerfile
create mode 100644 Makefile
create mode 100644 cmd/init.go
create mode 100644 cmd/root.go
create mode 100644 cmd/tf.go
create mode 100644 cmd/tofu.go
create mode 100644 cmd/uninstallDeps.go
create mode 100644 cmd/upgradeDeps.go
create mode 100644 go.mod
create mode 100644 go.sum
delete mode 100755 lib/tenv-bashlog.sh
delete mode 100755 lib/tenv-helpers.sh
delete mode 100755 libexec/tenv-exec
delete mode 100755 libexec/tenv-help
delete mode 100755 libexec/tenv-init
delete mode 100755 libexec/tenv-tf
delete mode 100755 libexec/tenv-tofu
delete mode 100755 libexec/tofuenv-install
delete mode 100755 libexec/tofuenv-resolve-version
create mode 100644 main.go
create mode 100644 pkg/utils/abc.go
create mode 100644 pkg/utils/github.go
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..e06bc84
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,54 @@
+name: pipeline
+
+on:
+ push:
+ branches:
+ - 'main'
+ - 'release/**'
+
+env:
+ registry_url: "registry.hub.docker.com"
+ image_repo: "kvendingoldo/aws-cognito-backup-lambda"
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ outputs:
+ version: ${{ steps.set_version.outputs.safe_version }}
+ steps:
+ -
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ -
+ name: Set up QEMU
+ uses: docker/setup-qemu-action@v1
+ with:
+ platforms: all
+ -
+ name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v1
+ -
+ name: Login to Docker registry
+ uses: docker/login-action@v1
+ with:
+ registry: ${{ env.registry_url }}
+ username: ${{ secrets.REGISTRY_USER }}
+ password: ${{ secrets.REGISTRY_PASSWORD }}
+ -
+ name: Set application version
+ id: set_version
+ uses: kvendingoldo/semver-action@v1.10
+ with:
+ primary_branch: 'main'
+ -
+ name: Build and push
+ uses: docker/build-push-action@v3
+ with:
+ push: true
+ tags: |
+ ${{ env.registry_url }}/${{ env.image_repo }}:latest
+ ${{ env.registry_url }}/${{ env.image_repo }}:${{ steps.set_version.outputs.safe_version }}
+ -
+ name: Print image version
+ run: echo ${{ steps.set_version.outputs.safe_version }}
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
new file mode 100644
index 0000000..4cbb88c
--- /dev/null
+++ b/.github/workflows/golangci-lint.yml
@@ -0,0 +1,28 @@
+name: golangci-lint
+on:
+ push:
+ tags:
+ - v*
+ branches:
+ - main
+ pull_request:
+permissions:
+ contents: read
+jobs:
+ golangci:
+ name: lint
+ runs-on: ubuntu-20.04
+ steps:
+ -
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18
+ -
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ -
+ name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ version: v1.48
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 82c671b..00951fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,23 @@ bin/terraform-*
/use-gpgv
.*.swp
.idea
-.misc
\ No newline at end of file
+.misc
+
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+.idea
+.DS_Store
\ No newline at end of file
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..1fa0476
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,38 @@
+run:
+ deadline: 1m
+ skip-files:
+ - ".*\\.pb\\.go"
+ skip-dirs:
+ - pkg/client
+ modules-download-mode: readonly
+
+linters:
+ enable-all: true
+ disable-all: false
+ disable:
+ - wsl
+ - wrapcheck
+ - goerr113
+ - gomnd
+ - exhaustruct
+ - nestif
+ - gofumpt
+ - gochecknoinits
+ - funlen
+ - cyclop
+ - goimports
+ - gci
+ - lll
+ - gocognit
+ - ifshort # deprecated
+ - maligned # deprecated
+ - golint # deprecated
+ - interfacer #deprecated
+ - scopelint # deprecated
+ - exhaustivestruct # deprecated
+
+linters-settings:
+ gosimple:
+ checks:
+ - all
+ - '-S1024' # disable the rule S1024
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..fbaa0d1
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,17 @@
+FROM golang:1.18 AS builder
+
+ENV CGO_ENABLED=0 \
+ GOOS=linux \
+ GOARCH=amd64 \
+ GIT_TERMINAL_PROMPT=1
+
+COPY ./internal ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda/internal
+COPY ./go.mod ./go.sum ./main.go ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda/
+WORKDIR ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda
+RUN go get ./
+RUN go build -ldflags="-s -w" -o lambda .
+
+FROM gcr.io/distroless/static:nonroot
+COPY --from=builder go/src/github.com/kvendingoldo/aws-cognito-backup-lambda/lambda /app/
+WORKDIR /app
+ENTRYPOINT ["/app/lambda"]
diff --git a/LICENSE b/LICENSE
index b27740f..e69de29 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,22 +0,0 @@
-
-The MIT License (MIT)
-
-Copyright (c) 2024 Nikolai Mishin, Alexander Sharov, Anastasiia Kozlova
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..100f5c0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+##@ General
+help: ## Display this help.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+fmt: ## Run go fmt against code.
+ go fmt ./...
+
+vet: ## Run go vet against code.
+ go vet ./...
+
+##@ Build
+build: fmt vet ## Build service binary.
+ go build -o bin/lambda main.go
+
+run: vet ## Run service from your laptop.
+ go run ./main.go
+
+##@ Test
+lint: ## Run Go linter
+ golangci-lint run ./...
+
+test: ## Run Go tests
+ go test ./...
\ No newline at end of file
diff --git a/cmd/init.go b/cmd/init.go
new file mode 100644
index 0000000..24c6442
--- /dev/null
+++ b/cmd/init.go
@@ -0,0 +1,39 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// initCmd represents the init command
+var initCmd = &cobra.Command{
+ Use: "init",
+ Short: "Update environment to use 'utils' correctly",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("init called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(initCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // initCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // initCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 0000000..48d61d7
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,47 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "os"
+
+ "github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+ Use: "utils",
+ Short: "A brief description of your application",
+ Long: `A longer description that spans multiple lines and likely contains
+examples and usage of using your application. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ // Uncomment the following line if your bare application
+ // has an action associated with it:
+ // Run: func(cmd *cobra.Command, args []string) { },
+ Version: `abc`,
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+ err := rootCmd.Execute()
+ if err != nil {
+ os.Exit(1)
+ }
+}
+
+func init() {
+ // Here you will define your flags and configuration settings.
+ // Cobra supports persistent flags, which, if defined here,
+ // will be global for your application.
+
+ // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.utils.yaml)")
+
+ // Cobra also supports local flags, which will only run
+ // when this action is called directly.
+ rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/tf.go b/cmd/tf.go
new file mode 100644
index 0000000..cb76260
--- /dev/null
+++ b/cmd/tf.go
@@ -0,0 +1,39 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// tfCmd represents the tf command
+var tfCmd = &cobra.Command{
+ Use: "tf",
+ Short: "A brief description of your command",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("tf called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(tfCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // tfCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // tfCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/tofu.go b/cmd/tofu.go
new file mode 100644
index 0000000..74b6e74
--- /dev/null
+++ b/cmd/tofu.go
@@ -0,0 +1,39 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// tofuCmd represents the tofu command
+var tofuCmd = &cobra.Command{
+ Use: "tofu",
+ Short: "A brief description of your command",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("tofu called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(tofuCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // tofuCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // tofuCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/uninstallDeps.go b/cmd/uninstallDeps.go
new file mode 100644
index 0000000..63fd035
--- /dev/null
+++ b/cmd/uninstallDeps.go
@@ -0,0 +1,39 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// uninstallDepsCmd represents the uninstallDeps command
+var uninstallDepsCmd = &cobra.Command{
+ Use: "uninstallDeps",
+ Short: "Uninstall utils dependencies (tfenv and tofuenv)",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("uninstallDeps called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(uninstallDepsCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // uninstallDepsCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // uninstallDepsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/upgradeDeps.go b/cmd/upgradeDeps.go
new file mode 100644
index 0000000..8d00cc2
--- /dev/null
+++ b/cmd/upgradeDeps.go
@@ -0,0 +1,54 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+// "github.com/opentofuutils/tenv/pkg/utils"
+
+// upgradeDepsCmd represents the upgradeDeps command
+var upgradeDepsCmd = &cobra.Command{
+ Use: "upgradeDeps",
+ Short: "Upgrade utils dependencies (tfenv and tofuenv)",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("upgradeDeps called")
+
+ //destFolder := "./data"
+
+ //tenv.CreateFolder(destFolder)
+
+ // Download the latest release
+ //if err := DownloadLatestRelease("tfutils", "tfenv", destFolder); err != nil {
+ // fmt.Println("Error:", err)
+ // return
+ //}
+
+ fmt.Println("Latest release downloaded successfully.")
+
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(upgradeDepsCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // upgradeDepsCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // upgradeDepsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..7eda496
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module github.com/opentofuutils/tenv
+
+go 1.21
+
+require github.com/spf13/cobra v1.8.0
+
+require (
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..d0e8c2c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,10 @@
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/lib/tenv-bashlog.sh b/lib/tenv-bashlog.sh
deleted file mode 100755
index 6ae2cbe..0000000
--- a/lib/tenv-bashlog.sh
+++ /dev/null
@@ -1,175 +0,0 @@
-#!/usr/bin/env bash
-
-set -uo pipefail;
-
-function _log_exception() {
- (
- BASHLOG_FILE=0;
- BASHLOG_JSON=0;
- BASHLOG_SYSLOG=0;
-
- log 'error' "Logging Exception: $*";
- );
-};
-export -f _log_exception;
-
-function log() {
- local syslog="${BASHLOG_SYSLOG:-0}";
- local file="${BASHLOG_FILE:-0}";
- local json="${BASHLOG_JSON:-0}";
- local stdout_extra="${BASHLOG_EXTRA:-0}";
-
- if [ "${file}" -eq 1 ] || [ "${json}" -eq 1 ] || [ "${stdout_extra}" -eq 1 ]; then
- local date_format="${BASHLOG_DATE_FORMAT:-+%F %T}";
- local date="$(date "${date_format}")";
- local date_s="$(date "+%s")";
- fi
- local file_path="${BASHLOG_FILE_PATH:-/tmp/${0##*/}.log}";
- local json_path="${BASHLOG_JSON_PATH:-/tmp/${0##*/}.log.json}";
-
- local tag="${BASHLOG_SYSLOG_TAG:-${0##*/})}";
- local facility="${BASHLOG_SYSLOG_FACILITY:-local0}";
- local pid="${$}";
-
- local level="${1}";
- local upper="$(echo "${level}" | awk '{print toupper($0)}')";
- local debug_level="${TOFUENV_DEBUG:-0}";
- local stdout_colours="${BASHLOG_COLOURS:-1}";
-
- local custom_eval_prefix="${BASHLOG_I_PROMISE_TO_BE_CAREFUL_CUSTOM_EVAL_PREFIX:-""}";
-
- shift 1;
-
- local line="$@";
-
- # RFC 5424
- #
- # Numerical Severity
- # Code
- #
- # 0 Emergency: system is unusable
- # 1 Alert: action must be taken immediately
- # 2 Critical: critical conditions
- # 3 Error: error conditions
- # 4 Warning: warning conditions
- # 5 Notice: normal but significant condition
- # 6 Informational: informational messages
- # 7 Debug: debug-level messages
-
- local severities_DEBUG=7;
- local severities_INFO=6;
- local severities_NOTICE=5; # Unused
- local severities_WARN=4;
- local severities_ERROR=3;
- local severities_CRIT=2; # Unused
- local severities_ALERT=1; # Unused
- local severities_EMERG=0; # Unused
-
- local severity_var="severities_${upper}";
- local severity="${!severity_var:-3}";
-
- if [ "${debug_level}" -gt 0 ] || [ "${severity}" -lt 7 ]; then
-
- if [ "${syslog}" -eq 1 ]; then
- local syslog_line="${upper}: ${line}";
-
- logger \
- --id="${pid}" \
- -t "${tag}" \
- -p "${facility}.${severity}" \
- "${syslog_line}" \
- || _log_exception "logger --id=\"${pid}\" -t \"${tag}\" -p \"${facility}.${severity}\" \"${syslog_line}\"";
- fi;
-
- if [ "${file}" -eq 1 ]; then
- local file_line="${date} [${upper}] ${line}";
-
- if [ -n "${custom_eval_prefix}" ]; then
- file_line="$(eval "${custom_eval_prefix}")${file_line}";
- fi;
-
- echo -e "${file_line}" >> "${file_path}" \
- || _log_exception "echo -e \"${file_line}\" >> \"${file_path}\"";
- fi;
-
- if [ "${json}" -eq 1 ]; then
- local json_line="$(printf '{"timestamp":"%s","level":"%s","message":"%s"}' "${date_s}" "${level}" "${line}")";
- echo -e "${json_line}" >> "${json_path}" \
- || _log_exception "echo -e \"${json_line}\" >> \"${json_path}\"";
- fi;
-
- fi;
-
- local colours_DEBUG='\033[34m' # Blue
- local colours_INFO='\033[32m' # Green
- local colours_NOTICE='' # Unused
- local colours_WARN='\033[33m' # Yellow
- local colours_ERROR='\033[31m' # Red
- local colours_CRIT='' # Unused
- local colours_ALERT='' # Unused
- local colours_EMERG='' # Unused
- local colours_DEFAULT='\033[0m' # Default
-
- local norm="${colours_DEFAULT}";
- local colour_var="colours_${upper}";
- local colour="${!colour_var:-\033[31m}";
-
- local std_line;
- if [ "${debug_level}" -le 1 ]; then
- std_line="${line}";
- elif [ "${debug_level}" -ge 2 ]; then
- std_line="${0}: ${line}";
- fi;
-
- if [ "${stdout_extra}" -eq 1 ]; then
- std_line="${date} [${upper}] ${std_line}";
- fi;
-
- if [ -n "${custom_eval_prefix}" ]; then
- std_line="$(eval "${custom_eval_prefix}")${std_line}";
- fi;
-
- if [ "${stdout_colours}" -eq 1 ]; then
- std_line="${colour}${std_line}${norm}";
- fi;
-
- # Standard Output (Pretty)
- case "${level}" in
- 'info'|'warn')
- echo -e "${std_line}";
- ;;
- 'debug')
- if [ "${debug_level}" -gt 0 ]; then
- # We are debugging to STDERR on purpose
- # tofuenv relies on STDOUT between libexecs to function
- echo -e "${std_line}" >&2;
- fi;
- ;;
- 'error')
- echo -e "${std_line}" >&2;
- if [ "${debug_level}" -gt 1 ]; then
- echo -e "Here's a shell for debugging the current environment. 'exit 0' to resume script from here. Non-zero exit code will abort - parent shell will terminate." >&2;
- bash || exit "${?}";
- else
- exit 1;
- fi;
- ;;
- *)
- log 'error' "Undefined log level trying to log: $*";
- ;;
- esac
-};
-export -f log;
-
-declare prev_cmd="null";
-declare this_cmd="null";
-trap 'prev_cmd=${this_cmd:-null}; 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/tenv-helpers.sh b/lib/tenv-helpers.sh
deleted file mode 100755
index 1d215c2..0000000
--- a/lib/tenv-helpers.sh
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env bash
-
-set -uo pipefail;
-
-if [ -z "${TOFUENV_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 "${target_file%/*}" || early_death "Failed to 'cd \$(${target_file%/*})' while trying to determine TOFUENV_ROOT";
- file_name="${target_file##*/}" || early_death "Failed to '\"${target_file##*/}\"' while trying to determine TOFUENV_ROOT";
- target_file="$(readlink "${file_name}")";
- done;
-
- echo "$(pwd -P)/${file_name}";
- };
- TOFUENV_SHIM=$(readlink_f "${0}")
- TOFUENV_ROOT="${TOFUENV_SHIM%/*/*}";
- [ -n "${TOFUENV_ROOT}" ] || early_death "Failed to determine TOFUENV_ROOT";
-else
- TOFUENV_ROOT="${TOFUENV_ROOT%/}";
-fi;
-export TOFUENV_ROOT;
-
-if [ -z "${TOFUENV_CONFIG_DIR:-""}" ]; then
- TOFUENV_CONFIG_DIR="$TOFUENV_ROOT";
-else
- TOFUENV_CONFIG_DIR="${TOFUENV_CONFIG_DIR%/}";
-fi
-export TOFUENV_CONFIG_DIR;
-
-if [ "${TOFUENV_DEBUG:-0}" -gt 0 ]; then
- # Only reset DEBUG if TOFUENV_DEBUG is set, and DEBUG is unset or already a number
- if [[ "${DEBUG:-0}" =~ ^[0-9]+$ ]] && [ "${DEBUG:-0}" -gt "${TOFUENV_DEBUG:-0}" ]; then
- export DEBUG="${TOFUENV_DEBUG:-0}";
- fi;
- if [[ "${TOFUENV_DEBUG}" -gt 2 ]]; then
- export PS4='+ [${BASH_SOURCE##*/}:${LINENO}] ';
- set -x;
- fi;
-fi;
-
-function load_bashlog () {
- source "${TOFUENV_ROOT}/lib/tofuenv-bashlog.sh";
-};
-export -f load_bashlog;
-
-if [ "${TOFUENV_DEBUG:-0}" -gt 0 ] ; then
- # our shim below cannot be used when debugging is enabled
- load_bashlog;
-else
- # Shim that understands to no-op for debug messages, and defers to
- # full bashlog for everything else.
- function log () {
- if [ "$1" != 'debug' ] ; then
- # Loading full bashlog will overwrite the `log` function
- load_bashlog;
- log "$@";
- fi;
- };
- export -f log;
-fi;
-
-# 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;
-
- if [[ ! -z "${TOFUENV_NETRC_PATH:-""}" ]]; then
- NETRC_OPT="--netrc-file ${TOFUENV_NETRC_PATH}";
- else
- NETRC_OPT="";
- fi;
-
- if [[ ! -z "${TOFUENV_GITHUB_TOKEN:-""}" ]]; then
- AUTHORIZATION_HEADER="Authorization: Bearer ${TOFUENV_GITHUB_TOKEN}";
- else
- AUTHORIZATION_HEADER="";
- fi;
- curl ${TLS_OPT} -H "${AUTHORIZATION_HEADER}" ${NETRC_OPT} "$@";
-};
-export -f curlw;
-
-function check_active_version() {
- local v="${1}";
- local maybe_chdir=;
- if [ -n "${2:-}" ]; then
- maybe_chdir="-chdir=${2}";
- fi;
-
- local active_version="$(${TOFUENV_ROOT}/bin/tofu ${maybe_chdir} version | grep '^OpenTofu')";
-
- if ! grep -E "^OpenTofu v${v}((-dev)|( \([a-f0-9]+\)))?( is already installed)?\$" <(echo "${active_version}"); then
- log 'debug' "Expected version ${v} but found ${active_version}";
- return 1;
- fi;
-
- log 'debug' "Active version ${v} as expected";
- return 0;
-};
-export -f check_active_version;
-
-function check_installed_version() {
- local v="${1}";
- local bin="${TOFUENV_CONFIG_DIR}/versions/${v}/tofu";
- [ -n "$(${bin} version | grep -E "^OpenTofu v${v}((-dev)|( \([a-f0-9]+\)))?$")" ];
-};
-export -f check_installed_version;
-
-function check_default_version() {
- local v="${1}";
- local def="$(cat "${TOFUENV_CONFIG_DIR}/version")";
- [ "${def}" == "${v}" ];
-};
-export -f check_default_version;
-
-function cleanup() {
- log 'info' 'Performing cleanup';
- local pwd="$(pwd)";
- log 'debug' "Deleting ${pwd}/version";
- rm -rf ./version;
- log 'debug' "Deleting ${pwd}/versions";
- rm -rf ./versions;
- log 'debug' "Deleting ${pwd}/.opentofu-version";
- rm -rf ./.opentofu-version;
- log 'debug' "Deleting ${pwd}/latest_allowed.tf";
- rm -rf ./latest_allowed.tf;
- log 'debug' "Deleting ${pwd}/min_required.tf";
- rm -rf ./min_required.tf;
- log 'debug' "Deleting ${pwd}/chdir-dir";
- rm -rf ./chdir-dir;
-};
-export -f cleanup;
-
-function error_and_proceed() {
- errors+=("${1}");
- log 'warn' "Test Failed: ${1}";
-};
-export -f error_and_proceed;
-
-function check_dependencies() {
- if [[ $(uname) == 'Darwin' ]] && [ $(which brew) ]; then
- if ! [ $(which ggrep) ]; then
- log 'error' 'A metaphysical dichotomy has caused this unit to overload and shut down. GNU Grep is a requirement and your Mac does not have it. Consider "brew install grep"';
- fi;
-
- shopt -s expand_aliases;
- alias grep=ggrep;
- fi;
-};
-export -f check_dependencies;
-
-source "$TOFUENV_ROOT/lib/tofuenv-exec.sh";
-source "$TOFUENV_ROOT/lib/tofuenv-min-required.sh";
-source "$TOFUENV_ROOT/lib/tofuenv-version-file.sh";
-source "$TOFUENV_ROOT/lib/tofuenv-version-name.sh";
-
-export TOFUENV_HELPERS=1;
diff --git a/libexec/tenv-exec b/libexec/tenv-exec
deleted file mode 100755
index 8dd3d58..0000000
--- a/libexec/tenv-exec
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env bash
-#
-# Summary: TODO
-#
-# Usage: tenv exec [arg1 arg2...]
-#
-# Runs an executable by first preparing PATH so that the selected OpenTofu
-# version's `bin' directory is at the front.
-#
-# For example, if the currently selected OpenTofu version is 1.6.0:
-# tofuenv exec plan
-#
-# is equivalent to:
-# PATH="$TENV_ROOT/versions/1.6.0/bin:$PATH" tofu plan
-
-set -uo pipefail;
-
-####################################
-# Ensure we can execute standalone #
-####################################
-
-function early_death() {
- echo "[FATAL] ${0}: ${1}" >&2;
- exit 1;
-};
-
-if [ -z "${TENV_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 TENV_ROOT";
- file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TENV_ROOT";
- target_file="$(readlink "${file_name}")";
- done;
-
- echo "$(pwd -P)/${file_name}";
- };
-
- TENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
- [ -n "${TENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TENV_ROOT";
-else
- TENV_ROOT="${TENV_ROOT%/}";
-fi;
-export TENV_ROOT;
-
-if [ -n "${TOFUENV_HELPERS:-""}" ]; then
- log 'debug' 'TOFUENV_HELPERS is set, not sourcing helpers again';
-else
- [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
- if source "${TENV_ROOT}/lib/tenv-helpers.sh"; then
- log 'debug' 'Helpers sourced successfully';
- else
- early_death "Failed to source helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
- fi;
-fi;
-
-# Ensure libexec and bin are in $PATH
-for dir in libexec bin; do
- case ":${PATH}:" in
- *:${TENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TENV_ROOT}/${dir}', not adding it again";;
- *)
- log 'debug' "\$PATH does not contain '${TENV_ROOT}/${dir}', prepending and exporting it now";
- export PATH="${TENV_ROOT}/${dir}:${PATH}";
- ;;
- esac;
-done;
-
-#####################
-# Begin Script Body #
-#####################
-
-tenv-exec "$@";
diff --git a/libexec/tenv-help b/libexec/tenv-help
deleted file mode 100755
index 1d1d5aa..0000000
--- a/libexec/tenv-help
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-set -uo pipefail;
-
-echo 'Usage: tenv []
-
-Commands:
- init Update environment to use 'tenv' correctly.
- install Install tenv dependencies (tfenv and tofuenv)
- uninstall Uninstall tenv dependencies (tfenv and tofuenv)
- tofu List of tofuenv commands
- tf List of tfenv commands
- version-name Print current version of tenv
- help Show the tenv help
-';
-
-exit 0;
\ No newline at end of file
diff --git a/libexec/tenv-init b/libexec/tenv-init
deleted file mode 100755
index 93554e1..0000000
--- a/libexec/tenv-init
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-[ -n "${TENV_DEBUG}" ] && set -x;
-
-export PATH="${TENV_ROOT}/bin:${PATH}";
diff --git a/libexec/tenv-tf b/libexec/tenv-tf
deleted file mode 100755
index cff7ce7..0000000
--- a/libexec/tenv-tf
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-# Usage: tenv tf
-# Summary: TODO
-
-set -uo pipefail;
-
-tfenv
\ No newline at end of file
diff --git a/libexec/tenv-tofu b/libexec/tenv-tofu
deleted file mode 100755
index 5332b28..0000000
--- a/libexec/tenv-tofu
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-# Usage: tofuenv version-file
-# Summary: Detect the file that sets the current tofuenv version
-
-set -uo pipefail;
\ No newline at end of file
diff --git a/libexec/tofuenv-install b/libexec/tofuenv-install
deleted file mode 100755
index b033cfc..0000000
--- a/libexec/tofuenv-install
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/usr/bin/env bash
-set -uo pipefail;
-
-####################################
-# Ensure we can execute standalone #
-####################################
-
-function early_death() {
- echo "[FATAL] ${0}: ${1}" >&2;
- exit 1;
-};
-
-if [ -z "${TENV_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 TENV_ROOT";
- file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TENV_ROOT";
- target_file="$(readlink "${file_name}")";
- done;
-
- echo "$(pwd -P)/${file_name}";
- };
-
- TENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
- [ -n "${TENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TENV_ROOT";
-else
- TENV_ROOT="${TENV_ROOT%/}";
-fi;
-export TENV_ROOT;
-
-if [ -n "${TENV_HELPERS:-""}" ]; then
- log 'debug' 'TENV_HELPERS is set, not sourcing helpers again';
-else
- [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
- if source "${TENV_ROOT}/lib/tenv-helpers.sh"; then
- log 'debug' 'Helpers sourced successfully';
- else
- early_death "Failed to source helpers from ${TENV_ROOT}/lib/tenv-helpers.sh";
- fi;
-fi;
-
-# Ensure libexec and bin are in $PATH
-for dir in libexec bin; do
- case ":${PATH}:" in
- *:${TENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TENV_ROOT}/${dir}', not adding it again";;
- *)
- log 'debug' "\$PATH does not contain '${TENV_ROOT}/${dir}', prepending and exporting it now";
- export PATH="${TENV_ROOT}/${dir}:${PATH}";
- ;;
- esac;
-done;
-
-#####################
-# Begin Script Body #
-#####################
-
-[ "${#}" -gt 1 ] && log 'error' 'usage: tenv install []';
-
-declare requested="${1:-""}";
-
-log debug "Resolving version with: tenv-resolve-version ${requested}";
-declare resolved;
-# TODO
-resolved="$(tenv-resolve-version ${requested})" || log 'error' "Failed to resolve ${requested} version";
-
-declare version="${resolved%%\:*}";
-declare regex="${resolved##*\:}";
-
-[ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest';
-
-log 'debug' "Processing install for version ${version}, using regex ${regex}";
-
-#if [[ ${version} =~ ${regex:-not} ]]; then
-# log 'debug' "Version and regex matched";
-#else
-# log 'debug' "Version and regex not matched";
-# if [ "${TOFUENV_SKIP_LIST_REMOTE:-0}" -eq 0 ]; then
-# remote_version="$(tofuenv-list-remote | grep -e "${regex}" | head -n 1)";
-# [ -n "${remote_version}" ] && version="${remote_version}" || log 'error' "No ${version} versions matching '${regex}' found in remote";
-# fi;
-#fi;
-
-# ???????
-dst_path="${TENV_CONFIG_DIR}/versions/${version}";
-if [ -f "${dst_path}/tofu" ]; then
- echo "OpenTofu v${version} is already installed";
- exit 0;
-fi;
-
-
-
-
-
-os="${kernel}_${TOFUENV_ARCH}"
-
-keybase_bin="$(command -v keybase 2>/dev/null)";
-shasum_bin="$(command -v shasum 2>/dev/null)";
-sha256sum_bin="$(command -v sha256sum 2>/dev/null)";
-
-TOFUENV_REMOTE="${TOFUENV_REMOTE:-https://github.com/opentofu/opentofu/releases}";
-version_url="${TOFUENV_REMOTE}/download/v${version}";
-
-tarball_name="tofu_${version}_${os}.zip";
-
-shasums_name="tofu_${version}_SHA256SUMS";
-shasums_sig="${shasums_name}.sig";
-
-log 'info' "Installing OpenTofu v${version}";
-
-# Create a local temporary directory for downloads
-tmpdir_arg="-t";
-
-if mktemp --help 2>&1 | grep -- '--tmpdir' >/dev/null; then
- tmpdir_arg="--tmpdir";
-fi;
-
-download_tmp="$(mktemp -d ${tmpdir_arg} tofuenv_download.XXXXXX)" || log 'error' "Unable to create temporary download directory (mktemp -d ${tmpdir_arg} tofuenv_download.XXXXXX). Working Directory is: $(pwd)";
-
-# Clean it up in case of error
-trap "rm -rf ${download_tmp}" EXIT;
-
-declare curl_progress="";
-case "${TOFUENV_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' 'TOFUENV_CURL_OUTPUT specified, but not with a supported value ([0,1,2])';
- ;;
-esac;
-
-log 'info' "Downloading release tarball from ${version_url}/${tarball_name}";
-
-status=$(curlw ${curl_progress} -w "%{http_code}" -f -L -o "${download_tmp}/${tarball_name}" "${version_url}/${tarball_name}");
-
-case "${status}" in
- 200) log 'debug' "'${requested:-$version}' version download successfully" ;;
- 403) log 'error' "GitHub Rate limits exceeded" ;;
- 404) log 'error' "No versions matching '${requested:-$version}' found in remote" ;;
- *) log 'error' "Unknown error, status code = ${status}" ;;
-esac;
-
-log 'info' "Downloading SHA hash file from ${version_url}/${shasums_name}";
-curlw -s -f -L -o "${download_tmp}/${shasums_name}" "${version_url}/${shasums_name}" || log 'error' 'SHA256 hashes download failed';
-
-download_signature() {
- log 'info' "Downloading SHA hash signature file from ${version_url}/${shasums_sig}";
- curlw -s -f -L \
- -o "${download_tmp}/${shasums_sig}" \
- "${version_url}/${shasums_sig}" \
- && log 'debug' "SHA256SUMS signature file downloaded successfully to ${download_tmp}/${shasums_sig}" \
- || log 'error' 'SHA256SUMS signature download failed';
-};
-
-# If on MacOS with Homebrew, use GNU grep
-# This allows keybase login detection to work on Mac,
-# and is required to be able to detect tofu version
-# from "required_version" setting in "*.tf" files
-check_dependencies;
-
-
-
-
-if [[ -n "${shasum_bin}" && -x "${shasum_bin}" ]]; then
- (
- cd "${download_tmp}";
- "${shasum_bin}" \
- -a 256 \
- -s \
- -c <(grep -F "${tarball_name}" "${shasums_name}")
- ) || log 'error' 'SHA256 hash does not match!';
-elif [[ -n "${sha256sum_bin}" && -x "${sha256sum_bin}" ]]; then
- (
- cd "${download_tmp}";
- "${sha256sum_bin}" \
- -c <(grep -F "${tarball_name}" "${shasums_name}")
- ) || log 'error' 'SHA256 hash does not match!';
-else
- # Lack of shasum deserves a proper warning
- log 'warn' 'No shasum tool available. Skipping SHA256 hash validation';
-fi;
-
-mkdir -p "${dst_path}" || log 'error' "Failed to make directory ${dst_path}";
-
-declare unzip_output;
-unzip_output="$(unzip -o "${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}");
-
-log 'info' "Installation of tofu v${version} successful. To make this your default version, run 'tofuenv use ${version}'";
diff --git a/libexec/tofuenv-resolve-version b/libexec/tofuenv-resolve-version
deleted file mode 100755
index 732cfcb..0000000
--- a/libexec/tofuenv-resolve-version
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env bash
-# Usage: tofuenv resolve-version []
-# Summary: Resolve the version to action based on the environment and optional input token
-
-set -uo pipefail;
-
-####################################
-# Ensure we can execute standalone #
-####################################
-
-function early_death() {
- echo "[FATAL] ${0}: ${1}" >&2;
- exit 1;
-};
-
-if [ -z "${TOFUENV_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 TOFUENV_ROOT";
- file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TOFUENV_ROOT";
- target_file="$(readlink "${file_name}")";
- done;
-
- echo "$(pwd -P)/${file_name}";
- };
-
- TOFUENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
- [ -n "${TOFUENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TOFUENV_ROOT";
-else
- TOFUENV_ROOT="${TOFUENV_ROOT%/}";
-fi;
-export TOFUENV_ROOT;
-
-if [ -n "${TOFUENV_HELPERS:-""}" ]; then
- log 'debug' 'TOFUENV_HELPERS is set, not sourcing helpers again';
-else
- [ "${TOFUENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TOFUENV_ROOT}/lib/tofuenv-helpers.sh";
- if source "${TOFUENV_ROOT}/lib/tofuenv-helpers.sh"; then
- log 'debug' 'Helpers sourced successfully';
- else
- early_death "Failed to source helpers from ${TOFUENV_ROOT}/lib/tofuenv-helpers.sh";
- fi;
-fi;
-
-# Ensure libexec and bin are in $PATH
-for dir in libexec bin; do
- case ":${PATH}:" in
- *:${TOFUENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TOFUENV_ROOT}/${dir}', not adding it again";;
- *)
- log 'debug' "\$PATH does not contain '${TOFUENV_ROOT}/${dir}', prepending and exporting it now";
- export PATH="${TOFUENV_ROOT}/${dir}:${PATH}";
- ;;
- esac;
-done;
-
-# If on MacOS with Homebrew, use GNU grep
-# This allows keybase login detection to work on Mac,
-# and is required to be able to detect tofu version
-# from "required_version" setting in "*.tf" files
-check_dependencies;
-
-#####################
-# Begin Script Body #
-#####################
-
-declare version_requested version regex min_required version_file;
-
-declare arg="${1:-""}";
-
-if [ -z "${arg}" -a -z "${TOFUENV_TOFU_VERSION:-""}" ]; then
- version_file="$(tofuenv-version-file)";
- log 'debug' "Version File: ${version_file}";
-
- if [ "${version_file}" != "${TOFUENV_CONFIG_DIR}/version" ]; then
- log 'debug' "Version File (${version_file}) is not the default \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/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 \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/version)";
- version_requested="$(cat "${version_file}")" \
- || log 'error' "Failed to open ${version_file}";
-
- # Absolute fallback
- if [ -z "${version_requested}" ]; then
- log 'debug' 'Version file had no content. Falling back to "latest"';
- version_requested='latest';
- fi;
-
- else
- log 'debug' "Version File is the default \${TOFUENV_CONFIG_DIR}/version (${TOFUENV_CONFIG_DIR}/version) but it doesn't exist";
- log 'debug' 'No version requested on the command line or in the version file search path. Installing "latest"';
- version_requested='latest';
- fi;
-elif [ -n "${TOFUENV_TOFU_VERSION:-""}" ]; then
- version_requested="${TOFUENV_TOFU_VERSION}";
- log 'debug' "TOFUENV_TOFU_VERSION is set: ${TOFUENV_TOFU_VERSION}";
-else
- version_requested="${arg}";
-fi;
-
-[[ -n "${version_requested:-""}" ]] \
- && log 'debug' "Version Requested: ${version_requested}" \
- || log 'error' 'Version could not be resolved!';
-
-# Accept a v-prefixed version, but strip the v.
-if [[ "${version_requested}" =~ ^v.*$ ]]; then
- log 'debug' "Version Requested is prefixed with a v. Stripping the v.";
- version_requested="${version_requested#v*}";
-fi;
-
-if [[ "${version_requested}" =~ ^min-required$ ]]; then
- log 'debug' 'Detecting minimum required version...';
- min_required="$(tofuenv-min-required)" \
- || log 'error' 'tofuenv-min-required failed';
-
- if [ -z "${min_required}" ]; then
- log 'debug' 'It was not possible to detect a minimum-required version. Do you have a required_version line present?';
- exit;
- fi;
-
- log 'debug' "Minimum required version detected: ${min_required}";
- version_requested="${min_required}";
-fi;
-
-if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
- log 'debug' 'Detecting latest allowable version...';
- version_spec="$(grep -h required_version "${TOFUENV_DIR:-$(pwd)}"/{*.tf,*.tf.json} 2>/dev/null | { IFS='"' read -r _ ver _; echo "${ver%%,*}"; })";
- version_num="$(echo "${version_spec}" | sed -E 's/[^0-9.]+//')";
- log 'debug' "Using ${version_num} from version spec: ${version_spec}";
-
- case "${version_spec}" in
- '>'*)
- version_requested=latest;
- ;;
- '<='*)
- version_requested="${version_num}";
- ;;
- '~>'*)
- version_without_rightmost="${version_num%.*}";
- version_requested="latest:^${version_without_rightmost}";
- ;;
- *)
- log 'error' "Unsupported version spec: '${version_spec}', only >, >=, <=, and ~> are supported.";
- ;;
- esac;
- log 'debug' "Determined the requested version to be: ${version_requested}";
-fi;
-
-if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
- version="${version_requested%%\:*}";
- regex="${version_requested##*\:}";
- log 'debug' "Version uses latest keyword with regex: ${regex}";
-elif [[ "${version_requested}" =~ ^latest$ ]]; then
- version="${version_requested}";
- regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$";
- log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}";
-else
- version="${version_requested}";
- regex="^${version_requested}$";
- log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}";
-fi;
-
-echo "${version}:${regex}";
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..2fa68e3
--- /dev/null
+++ b/main.go
@@ -0,0 +1,12 @@
+/*
+Copyright © 2024 NAME HERE
+*/
+package main
+
+import (
+ "github.com/opentofuutils/tenv/cmd"
+)
+
+func main() {
+ cmd.Execute()
+}
diff --git a/pkg/utils/abc.go b/pkg/utils/abc.go
new file mode 100644
index 0000000..d680916
--- /dev/null
+++ b/pkg/utils/abc.go
@@ -0,0 +1,14 @@
+package main
+
+import "os"
+
+func CreateFolder(name string) error {
+ // Check if the destination folder exists, create it if not
+ if _, err := os.Stat(name); os.IsNotExist(err) {
+ if err := os.Mkdir(name, 0755); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/utils/github.go b/pkg/utils/github.go
new file mode 100644
index 0000000..ac2d124
--- /dev/null
+++ b/pkg/utils/github.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "os/exec"
+)
+
+func DownloadLatestRelease(owner, repo, destFolder string) error {
+ // Use GitHub API to get the latest release information
+ apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
+ resp, err := http.Get(apiURL)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("failed to fetch release information, status code: %d", resp.StatusCode)
+ }
+
+ // Parse the response JSON to get the download URL for the latest release
+ var releaseInfo map[string]interface{}
+ if err := json.NewDecoder(resp.Body).Decode(&releaseInfo); err != nil {
+ return err
+ }
+
+ assets, ok := releaseInfo["assets"].([]interface{})
+ if !ok || len(assets) == 0 {
+ return fmt.Errorf("no assets found for the latest release")
+ }
+
+ latestAsset := assets[0].(map[string]interface{})
+ latestAssetName := latestAsset["name"].(string)
+ //latestAssetURL := latestAsset["browser_download_url"].(string)
+
+ // Download the latest release zip file
+ downloadPath := fmt.Sprintf("%s/%s", destFolder, latestAssetName)
+ //if err := downloadFile(latestAssetURL, downloadPath); err != nil {
+ // return err
+ //}
+
+ // Unzip the downloaded file to the destination folder
+ if err := unzipFile(downloadPath, destFolder); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+//func downloadFile(url, dest string) error {
+// resp, err := http.Get(url)
+// if err != nil {
+// return err
+// }
+// defer resp.Body.Close()
+//
+// if resp.StatusCode != http.StatusOK {
+// return fmt.Errorf("failed to download file, status code: %d", resp.StatusCode)
+// }
+//
+// out, err := os.Create(dest)
+// if err != nil {
+// return err
+// }
+// defer out.Close()
+//
+// _, err = io.Copy(out, resp.Body)
+// return err
+//}
+
+func unzipFile(src, dest string) error {
+ cmd := exec.Command("unzip", "-o", src, "-d", dest)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
From 32b458f20b755eefb37bb9f812d9d74924c81667 Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 15:09:25 +0100
Subject: [PATCH 07/32] feat: implement new commands
---
.gitignore | 6 +-
cmd/init.go | 2 +-
cmd/root.go | 4 +-
cmd/tofu.go | 32 ++-
cmd/uninstallDeps.go | 17 +-
cmd/upgradeDeps.go | 29 +--
go.mod | 30 +++
go.sum | 307 ++++++++++++++++++++++++++
pkg/github/main.go | 59 +++++
pkg/misc/arhive.go | 74 +++++++
pkg/misc/env.go | 14 ++
pkg/{utils/abc.go => misc/folders.go} | 17 +-
pkg/misc/prepare.go | 44 ++++
pkg/utils/github.go | 79 -------
14 files changed, 611 insertions(+), 103 deletions(-)
create mode 100644 pkg/github/main.go
create mode 100644 pkg/misc/arhive.go
create mode 100644 pkg/misc/env.go
rename pkg/{utils/abc.go => misc/folders.go} (54%)
create mode 100644 pkg/misc/prepare.go
delete mode 100644 pkg/utils/github.go
diff --git a/.gitignore b/.gitignore
index 00951fe..2e06a68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,8 @@ bin/terraform-*
# vendor/
.idea
-.DS_Store
\ No newline at end of file
+.DS_Store
+./main
+./bin
+./root
+main
\ No newline at end of file
diff --git a/cmd/init.go b/cmd/init.go
index 24c6442..a143223 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -12,7 +12,7 @@ import (
// initCmd represents the init command
var initCmd = &cobra.Command{
Use: "init",
- Short: "Update environment to use 'utils' correctly",
+ Short: "Update environment to use 'github' correctly",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
diff --git a/cmd/root.go b/cmd/root.go
index 48d61d7..1bd153e 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -11,7 +11,7 @@ import (
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
- Use: "utils",
+ Use: "github",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
@@ -39,7 +39,7 @@ func init() {
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
- // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.utils.yaml)")
+ // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.github.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
diff --git a/cmd/tofu.go b/cmd/tofu.go
index 74b6e74..ad91d2a 100644
--- a/cmd/tofu.go
+++ b/cmd/tofu.go
@@ -4,9 +4,11 @@ Copyright © 2024 NAME HERE
package cmd
import (
+ "bytes"
"fmt"
-
+ "github.com/opentofuutils/tenv/pkg/misc"
"github.com/spf13/cobra"
+ "os/exec"
)
// tofuCmd represents the tofu command
@@ -20,7 +22,33 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
- fmt.Println("tofu called")
+ //fmt.Println("tofu called")
+
+ rootDir := misc.GetEnv(misc.RootEnv, "")
+ binDir := fmt.Sprintf("%s/bin", rootDir)
+ tofuExec := fmt.Sprintf("%s/tofu/bin/tofuenv", binDir)
+ tofuExec = "/Users/asharov/go/src/github.com/opentofuutils/tenv/root/bin/tofuenv/tofuutils-tofuenv-e0bec88/bin/tofuenv"
+ //fmt.Println(tofuExec)
+
+ exec := exec.Command(tofuExec, args...)
+ var out bytes.Buffer
+ var stderr bytes.Buffer
+ exec.Stdout = &out
+ exec.Stderr = &stderr
+ err := exec.Run()
+
+ if err != nil {
+ fmt.Println(stderr.String())
+ return
+ }
+ fmt.Println("Result: " + out.String())
+
+ //out, _ := exec.Command(tofuExec, args...).Output()
+ ////if err != nil {
+ //// fmt.Println(err)
+ ////}
+ //fmt.Println(string(out))
+
},
}
diff --git a/cmd/uninstallDeps.go b/cmd/uninstallDeps.go
index 63fd035..981a7a7 100644
--- a/cmd/uninstallDeps.go
+++ b/cmd/uninstallDeps.go
@@ -5,6 +5,9 @@ package cmd
import (
"fmt"
+ "github.com/opentofuutils/tenv/pkg/misc"
+ log "github.com/sirupsen/logrus"
+ "os"
"github.com/spf13/cobra"
)
@@ -12,7 +15,7 @@ import (
// uninstallDepsCmd represents the uninstallDeps command
var uninstallDepsCmd = &cobra.Command{
Use: "uninstallDeps",
- Short: "Uninstall utils dependencies (tfenv and tofuenv)",
+ Short: "Uninstall tenv dependencies (tfenv and tofuenv)",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
@@ -20,7 +23,17 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
- fmt.Println("uninstallDeps called")
+ log.Info("Starting to uninstall tenv tools")
+
+ rootDir := misc.GetEnv(misc.RootEnv, "")
+ binDir := fmt.Sprintf("%s/bin", rootDir)
+
+ err := os.RemoveAll(binDir)
+ if err != nil {
+ log.Error("Error removing bin directory:", err)
+ }
+
+ log.Info("tenv tools uninstalled successfully")
},
}
diff --git a/cmd/upgradeDeps.go b/cmd/upgradeDeps.go
index 8d00cc2..6722d30 100644
--- a/cmd/upgradeDeps.go
+++ b/cmd/upgradeDeps.go
@@ -4,17 +4,17 @@ Copyright © 2024 NAME HERE
package cmd
import (
- "fmt"
-
+ "github.com/opentofuutils/tenv/pkg/misc"
+ log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
-// "github.com/opentofuutils/tenv/pkg/utils"
+// "github.com/opentofuutils/tenv/pkg/github"
// upgradeDepsCmd represents the upgradeDeps command
var upgradeDepsCmd = &cobra.Command{
Use: "upgradeDeps",
- Short: "Upgrade utils dependencies (tfenv and tofuenv)",
+ Short: "Upgrade github dependencies (tfenv and tofuenv)",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
@@ -22,20 +22,21 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
- fmt.Println("upgradeDeps called")
-
- //destFolder := "./data"
+ log.Info("Starting to upgrade tenv tools")
- //tenv.CreateFolder(destFolder)
+ rootDir := misc.GetEnv(misc.RootEnv, "")
- // Download the latest release
- //if err := DownloadLatestRelease("tfutils", "tfenv", destFolder); err != nil {
- // fmt.Println("Error:", err)
- // return
- //}
+ err := misc.PrepareTool("tfutils", "tfenv", rootDir)
+ if err != nil {
+ return
+ }
- fmt.Println("Latest release downloaded successfully.")
+ err = misc.PrepareTool("opentofuutils", "tofuenv", rootDir)
+ if err != nil {
+ return
+ }
+ log.Info("tenv tools upgraded successfully")
},
}
diff --git a/go.mod b/go.mod
index 7eda496..ac2c659 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,36 @@ go 1.21
require github.com/spf13/cobra v1.8.0
require (
+ github.com/andybalholm/brotli v1.0.6 // indirect
+ github.com/arduino/go-paths-helper v1.2.0 // indirect
+ github.com/bodgit/plumbing v1.2.0 // indirect
+ github.com/bodgit/sevenzip v1.3.0 // indirect
+ github.com/bodgit/windows v1.0.0 // indirect
+ github.com/codeclysm/extract/v3 v3.1.1 // indirect
+ github.com/connesc/cipherio v0.2.1 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
+ github.com/golang/snappy v0.0.4 // indirect
+ github.com/h2non/filetype v1.1.3 // indirect
+ github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 // indirect
+ github.com/klauspost/compress v1.17.4 // indirect
+ github.com/klauspost/pgzip v1.2.6 // indirect
+ github.com/mholt/archiver/v3 v3.5.1 // indirect
+ github.com/nwaples/rardecode v1.1.3 // indirect
+ github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
+ github.com/pierrec/lz4/v4 v4.1.19 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
+ github.com/stretchr/testify v1.7.1 // indirect
+ github.com/therootcompany/xz v1.0.1 // indirect
+ github.com/ulikunitz/xz v0.5.11 // indirect
+ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
+ go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+ golang.org/x/text v0.3.8 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index d0e8c2c..d0c691f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,317 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
+github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
+github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/arduino/go-paths-helper v1.2.0 h1:qDW93PR5IZUN/jzO4rCtexiwF8P4OIcOmcSgAYLZfY4=
+github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
+github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
+github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
+github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
+github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=
+github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
+github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/codeclysm/extract/v3 v3.1.1 h1:iHZtdEAwSTqPrd+1n4jfhr1qBhUWtHlMTjT90+fJVXg=
+github.com/codeclysm/extract/v3 v3.1.1/go.mod h1:ZJi80UG2JtfHqJI+lgJSCACttZi++dHxfWuPaMhlOfQ=
+github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
+github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
+github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
+github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
+github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok=
+github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
+github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
+github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
+github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
+github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
+github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
+github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
+github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
+github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
+github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
+github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
+github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
+github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
+github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
+github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
+github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
+github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
+github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
+github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
+go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/pkg/github/main.go b/pkg/github/main.go
new file mode 100644
index 0000000..c4e0701
--- /dev/null
+++ b/pkg/github/main.go
@@ -0,0 +1,59 @@
+package github
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+)
+
+func DownloadLatestRelease(owner, repo, destPath string) error {
+ // Use GitHub API to get the latest release information
+ apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
+ resp, err := http.Get(apiURL)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("failed to fetch release information, status code: %d", resp.StatusCode)
+ }
+
+ // Parse the response JSON to get the download URL for the latest release
+ var releaseInfo map[string]interface{}
+ if err := json.NewDecoder(resp.Body).Decode(&releaseInfo); err != nil {
+ return err
+ }
+
+ zipball_url := releaseInfo["tarball_url"].(string)
+
+ // Download the latest release zip file
+ if err := DownloadFile(zipball_url, destPath); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func DownloadFile(url, dest string) error {
+ resp, err := http.Get(url)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("failed to download file, status code: %d", resp.StatusCode)
+ }
+
+ out, err := os.Create(dest)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, resp.Body)
+ return err
+}
diff --git a/pkg/misc/arhive.go b/pkg/misc/arhive.go
new file mode 100644
index 0000000..f3d4577
--- /dev/null
+++ b/pkg/misc/arhive.go
@@ -0,0 +1,74 @@
+package misc
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func UnTarGz(source, destination string) error {
+ // Open the source file
+ file, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ // Create a gzip reader
+ gzipReader, err := gzip.NewReader(file)
+ if err != nil {
+ return err
+ }
+ defer gzipReader.Close()
+
+ // Create a tar reader
+ tarReader := tar.NewReader(gzipReader)
+
+ // Iterate through the tar archive and extract files
+ for {
+ header, err := tarReader.Next()
+
+ if err == io.EOF {
+ // End of tar archive
+ break
+ }
+
+ if err != nil {
+ return err
+ }
+
+ // Construct the destination path for the current file without the top-level directory
+ target := filepath.Join(destination, strings.TrimPrefix(header.Name, filepath.Base(source)+"/"))
+
+ switch header.Typeflag {
+ case tar.TypeDir:
+ // Create directories
+ if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
+ return err
+ }
+
+ case tar.TypeReg, tar.TypeRegA:
+ // Create regular files
+ file, err := os.Create(target)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ // Copy file contents
+ if _, err := io.Copy(file, tarReader); err != nil {
+ return err
+ }
+
+ // Set file permissions
+ if err := os.Chmod(target, os.FileMode(header.Mode)); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/misc/env.go b/pkg/misc/env.go
new file mode 100644
index 0000000..ea92b7b
--- /dev/null
+++ b/pkg/misc/env.go
@@ -0,0 +1,14 @@
+package misc
+
+import "os"
+
+const (
+ RootEnv = "TENV_ROOT_PATH"
+)
+
+func GetEnv(key, fallback string) string {
+ if value, ok := os.LookupEnv(key); ok {
+ return value
+ }
+ return fallback
+}
diff --git a/pkg/utils/abc.go b/pkg/misc/folders.go
similarity index 54%
rename from pkg/utils/abc.go
rename to pkg/misc/folders.go
index d680916..ddf3b9f 100644
--- a/pkg/utils/abc.go
+++ b/pkg/misc/folders.go
@@ -1,6 +1,9 @@
-package main
+package misc
-import "os"
+import (
+ "fmt"
+ "os"
+)
func CreateFolder(name string) error {
// Check if the destination folder exists, create it if not
@@ -12,3 +15,13 @@ func CreateFolder(name string) error {
return nil
}
+
+func DeleteFolder(path string) error {
+ err := os.RemoveAll(path)
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Deleted folder: %s\n", path)
+ return nil
+}
diff --git a/pkg/misc/prepare.go b/pkg/misc/prepare.go
new file mode 100644
index 0000000..d94d9f4
--- /dev/null
+++ b/pkg/misc/prepare.go
@@ -0,0 +1,44 @@
+package misc
+
+import (
+ "fmt"
+ "github.com/opentofuutils/tenv/pkg/github"
+ log "github.com/sirupsen/logrus"
+ "os"
+)
+
+func PrepareTool(owner, repo, rootDir string) error {
+ binDir := fmt.Sprintf("%s/bin", rootDir)
+ miscDir := fmt.Sprintf("%s/misc", rootDir)
+
+ // Create temporary directory where tarballs will be stored
+ err := CreateFolder(miscDir)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ // Clean up: Remove the temporary directory when done
+ err := os.RemoveAll(miscDir)
+ if err != nil {
+ log.Error("Error removing temporary directory:", err)
+ }
+ }()
+
+ tarballPath := fmt.Sprintf("%s/%s-%s", miscDir, owner, repo)
+ if err := github.DownloadLatestRelease(owner, repo, tarballPath); err != nil {
+ fmt.Println("Error:", err)
+ return err
+ }
+ log.Info(fmt.Sprintf("Latest %s release owned by %s downloaded successfully", repo, owner))
+
+ err = UnTarGz(tarballPath, fmt.Sprintf("%s/%s", binDir, repo))
+ if err != nil {
+ log.Warn("Error:", err)
+ } else {
+ log.Info("Archive untarred successfully.")
+ }
+
+ log.Info(fmt.Sprintf("Latest %s release owned by %s prepared successfully", repo, owner))
+
+ return nil
+}
diff --git a/pkg/utils/github.go b/pkg/utils/github.go
deleted file mode 100644
index ac2d124..0000000
--- a/pkg/utils/github.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "net/http"
- "os"
- "os/exec"
-)
-
-func DownloadLatestRelease(owner, repo, destFolder string) error {
- // Use GitHub API to get the latest release information
- apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
- resp, err := http.Get(apiURL)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("failed to fetch release information, status code: %d", resp.StatusCode)
- }
-
- // Parse the response JSON to get the download URL for the latest release
- var releaseInfo map[string]interface{}
- if err := json.NewDecoder(resp.Body).Decode(&releaseInfo); err != nil {
- return err
- }
-
- assets, ok := releaseInfo["assets"].([]interface{})
- if !ok || len(assets) == 0 {
- return fmt.Errorf("no assets found for the latest release")
- }
-
- latestAsset := assets[0].(map[string]interface{})
- latestAssetName := latestAsset["name"].(string)
- //latestAssetURL := latestAsset["browser_download_url"].(string)
-
- // Download the latest release zip file
- downloadPath := fmt.Sprintf("%s/%s", destFolder, latestAssetName)
- //if err := downloadFile(latestAssetURL, downloadPath); err != nil {
- // return err
- //}
-
- // Unzip the downloaded file to the destination folder
- if err := unzipFile(downloadPath, destFolder); err != nil {
- return err
- }
-
- return nil
-}
-
-//func downloadFile(url, dest string) error {
-// resp, err := http.Get(url)
-// if err != nil {
-// return err
-// }
-// defer resp.Body.Close()
-//
-// if resp.StatusCode != http.StatusOK {
-// return fmt.Errorf("failed to download file, status code: %d", resp.StatusCode)
-// }
-//
-// out, err := os.Create(dest)
-// if err != nil {
-// return err
-// }
-// defer out.Close()
-//
-// _, err = io.Copy(out, resp.Body)
-// return err
-//}
-
-func unzipFile(src, dest string) error {
- cmd := exec.Command("unzip", "-o", src, "-d", dest)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
-}
From 528056b1ab83e30b046570ab64c28a4f8e765a8a Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 15:14:40 +0100
Subject: [PATCH 08/32] feat: add LICENCE
---
LICENSE | 21 +++++++++++++++++++++
cmd/init.go | 2 +-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/LICENSE b/LICENSE
index e69de29..5601e82 100644
--- a/LICENSE
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2024 Nikolai Mishin, Alexander Sharov, Anastasiia Kozlova
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/cmd/init.go b/cmd/init.go
index a143223..00daffa 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -12,7 +12,7 @@ import (
// initCmd represents the init command
var initCmd = &cobra.Command{
Use: "init",
- Short: "Update environment to use 'github' correctly",
+ Short: "Update environment to use 'tenv' correctly",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
From 69a9b0fe89fae5283f48e4e02d98c60ed1157c9f Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 16:58:26 +0100
Subject: [PATCH 09/32] feat: implement CheckToolInstalled function
---
.gitignore | 1 +
cmd/tofu.go | 13 +++++++----
main.go | 38 ++++++++++++++++++++++++++++++++
pkg/misc/arhive.go | 12 ++++++++--
pkg/misc/path.go | 32 +++++++++++++++++++++++++++
pkg/misc/{prepare.go => tool.go} | 15 ++++++++++---
6 files changed, 102 insertions(+), 9 deletions(-)
create mode 100644 pkg/misc/path.go
rename pkg/misc/{prepare.go => tool.go} (78%)
diff --git a/.gitignore b/.gitignore
index 2e06a68..4e7747d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,4 +31,5 @@ bin/terraform-*
./main
./bin
./root
+root/
main
\ No newline at end of file
diff --git a/cmd/tofu.go b/cmd/tofu.go
index ad91d2a..184b831 100644
--- a/cmd/tofu.go
+++ b/cmd/tofu.go
@@ -7,6 +7,7 @@ import (
"bytes"
"fmt"
"github.com/opentofuutils/tenv/pkg/misc"
+ log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os/exec"
)
@@ -22,12 +23,16 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
+
+ if !misc.CheckToolInstalled("tofuenv") {
+ log.Error("tofuenv is not installed. Please, execute 'tenv upgrade-deps' to use 'tenv tofu' commands")
+ return
+ }
+
//fmt.Println("tofu called")
- rootDir := misc.GetEnv(misc.RootEnv, "")
- binDir := fmt.Sprintf("%s/bin", rootDir)
- tofuExec := fmt.Sprintf("%s/tofu/bin/tofuenv", binDir)
- tofuExec = "/Users/asharov/go/src/github.com/opentofuutils/tenv/root/bin/tofuenv/tofuutils-tofuenv-e0bec88/bin/tofuenv"
+ tofuExec := misc.GetPath("tofuenv_exec")
+ fmt.Println(tofuExec)
//fmt.Println(tofuExec)
exec := exec.Command(tofuExec, args...)
diff --git a/main.go b/main.go
index 2fa68e3..55b4193 100644
--- a/main.go
+++ b/main.go
@@ -5,8 +5,46 @@ package main
import (
"github.com/opentofuutils/tenv/cmd"
+ log "github.com/sirupsen/logrus"
+ "os"
)
+func init() {
+ log.SetReportCaller(false)
+
+ var formatter log.Formatter
+
+ if formatterType, ok := os.LookupEnv("FORMATTER_TYPE"); ok {
+ if formatterType == "JSON" {
+ formatter = &log.JSONFormatter{PrettyPrint: false}
+ }
+
+ if formatterType == "TEXT" {
+ formatter = &log.TextFormatter{DisableColors: false}
+ }
+ }
+
+ if formatter == nil {
+ formatter = &log.TextFormatter{DisableColors: false}
+ }
+
+ log.SetFormatter(formatter)
+
+ var logLevel log.Level
+ var err error
+
+ if ll, ok := os.LookupEnv("LOG_LEVEL"); ok {
+ logLevel, err = log.ParseLevel(ll)
+ if err != nil {
+ logLevel = log.DebugLevel
+ }
+ } else {
+ logLevel = log.DebugLevel
+ }
+
+ log.SetLevel(logLevel)
+}
+
func main() {
cmd.Execute()
}
diff --git a/pkg/misc/arhive.go b/pkg/misc/arhive.go
index f3d4577..c4431b9 100644
--- a/pkg/misc/arhive.go
+++ b/pkg/misc/arhive.go
@@ -9,7 +9,7 @@ import (
"strings"
)
-func UnTarGz(source, destination string) error {
+func ExtractTarGz(source, destination string) error {
// Open the source file
file, err := os.Open(source)
if err != nil {
@@ -27,6 +27,9 @@ func UnTarGz(source, destination string) error {
// Create a tar reader
tarReader := tar.NewReader(gzipReader)
+ // Track the top-level directory name
+ var topLevelDir string
+
// Iterate through the tar archive and extract files
for {
header, err := tarReader.Next()
@@ -40,8 +43,13 @@ func UnTarGz(source, destination string) error {
return err
}
+ // If this is the first entry, capture the top-level directory name
+ if topLevelDir == "" && header.Name != "pax_global_header" {
+ topLevelDir = filepath.Base(header.Name)
+ }
+
// Construct the destination path for the current file without the top-level directory
- target := filepath.Join(destination, strings.TrimPrefix(header.Name, filepath.Base(source)+"/"))
+ target := filepath.Join(destination, strings.TrimPrefix(header.Name, topLevelDir+"/"))
switch header.Typeflag {
case tar.TypeDir:
diff --git a/pkg/misc/path.go b/pkg/misc/path.go
new file mode 100644
index 0000000..2dfa7a5
--- /dev/null
+++ b/pkg/misc/path.go
@@ -0,0 +1,32 @@
+package misc
+
+import (
+ "fmt"
+ log "github.com/sirupsen/logrus"
+)
+
+func GetPath(name string) string {
+ rootDir := GetEnv(RootEnv, "")
+
+ switch name {
+ case "root_dir":
+ return rootDir
+ case "bin_dir":
+ return fmt.Sprintf("%s/bin", rootDir)
+ case "misc_dir":
+ return fmt.Sprintf("%s/misc", rootDir)
+ case "tfenv_dir":
+ return fmt.Sprintf("%s/bin/tfenv", rootDir)
+ case "tofuenv_dir":
+ return fmt.Sprintf("%s/bin/tofuenv", rootDir)
+ case "tfenv_exec":
+ return fmt.Sprintf("%s/bin/tfenv/bin/tfenv", rootDir)
+ case "tofuenv_exec":
+ return fmt.Sprintf("%s/bin/tofuenv/bin/tofuenv", rootDir)
+
+ default:
+ log.Warn("Unknown day")
+ return ""
+ }
+
+}
diff --git a/pkg/misc/prepare.go b/pkg/misc/tool.go
similarity index 78%
rename from pkg/misc/prepare.go
rename to pkg/misc/tool.go
index d94d9f4..f112e0e 100644
--- a/pkg/misc/prepare.go
+++ b/pkg/misc/tool.go
@@ -4,12 +4,21 @@ import (
"fmt"
"github.com/opentofuutils/tenv/pkg/github"
log "github.com/sirupsen/logrus"
+
"os"
)
+func CheckToolInstalled(name string) bool {
+
+ path := GetPath("tofuenv_exec")
+ _, err := os.Stat(path)
+
+ return !os.IsExist(err)
+}
+
func PrepareTool(owner, repo, rootDir string) error {
- binDir := fmt.Sprintf("%s/bin", rootDir)
- miscDir := fmt.Sprintf("%s/misc", rootDir)
+ binDir := GetPath("bin_dir")
+ miscDir := GetPath("misc_dir")
// Create temporary directory where tarballs will be stored
err := CreateFolder(miscDir)
@@ -31,7 +40,7 @@ func PrepareTool(owner, repo, rootDir string) error {
}
log.Info(fmt.Sprintf("Latest %s release owned by %s downloaded successfully", repo, owner))
- err = UnTarGz(tarballPath, fmt.Sprintf("%s/%s", binDir, repo))
+ err = ExtractTarGz(tarballPath, fmt.Sprintf("%s/%s", binDir, repo))
if err != nil {
log.Warn("Error:", err)
} else {
From e7cff10a2ce26eefb5100ac0dd1fc6d503695456 Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 17:32:22 +0100
Subject: [PATCH 10/32] feat: add github actions
---
.github/.goreleaser.yml | 12 ++++++
.github/ISSUE_TEMPLATE/bug_report.md | 30 +++++++++++++
.github/ISSUE_TEMPLATE/custom.md | 0
.github/ISSUE_TEMPLATE/feature_request.md | 19 +++++++++
.github/dependabot.yml | 12 ++++++
.github/pull_request_template.md | 3 ++
.github/workflows/codespell.yml | 27 ++++++++++++
.github/workflows/release.yml | 52 +++++++++++++++++++++++
cmd/init.go | 2 +-
cmd/root.go | 2 +-
cmd/tf.go | 27 ++++++++++--
cmd/tofu.go | 20 ++-------
cmd/uninstallDeps.go | 14 ++----
cmd/upgradeDeps.go | 2 +-
pkg/github/main.go | 3 ++
pkg/misc/arhive.go | 3 ++
pkg/misc/env.go | 3 ++
pkg/misc/folders.go | 7 ++-
pkg/misc/tool.go | 4 +-
19 files changed, 206 insertions(+), 36 deletions(-)
create mode 100644 .github/.goreleaser.yml
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/custom.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
create mode 100644 .github/dependabot.yml
create mode 100644 .github/pull_request_template.md
create mode 100644 .github/workflows/codespell.yml
create mode 100644 .github/workflows/release.yml
diff --git a/.github/.goreleaser.yml b/.github/.goreleaser.yml
new file mode 100644
index 0000000..50be17e
--- /dev/null
+++ b/.github/.goreleaser.yml
@@ -0,0 +1,12 @@
+version: 1
+
+builds:
+ - skip: true
+ env:
+ - CGO_ENABLED=0
+
+changelog:
+ sort: asc
+ filters:
+ exclude:
+ - '^test:'
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..17fb538
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,30 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: 'issue: bug'
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Environment (please complete the following information):**
+- OS: [e.g. iOS]
+- tofuenv version [e.g. 0.8.0]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md
new file mode 100644
index 0000000..e69de29
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..01a471e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,19 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the issue is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..8a92f46
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+---
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ timezone: "Europe/London"
+ time: "09:00"
+ day: "monday"
+ commit-message:
+ prefix: "ci:"
\ No newline at end of file
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..6f25749
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
new file mode 100644
index 0000000..646fa70
--- /dev/null
+++ b/.github/workflows/codespell.yml
@@ -0,0 +1,27 @@
+---
+name: Codespell
+
+on:
+ push:
+ branches:
+ - 'main'
+ pull_request:
+ branches:
+ - 'main'
+ workflow_dispatch:
+
+jobs:
+ codespell:
+ name: 'Check for spelling errors'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Codespell
+ uses: codespell-project/actions-codespell@v2
+ with:
+ skip: .git,*.png,*.woff,*.woff2,*.eot,*.ttf,*.jpg,*.ico,*.svg,*.gpg,.*asc
+ check_filenames: true
+ check_hidden: true
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..2bf4f04
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,52 @@
+---
+name: 'Release'
+
+on:
+ push:
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+*'
+
+permissions:
+ contents: write
+
+jobs:
+ goreleaser:
+ name: 'Release the latest tag'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ - name: Run GoReleaser
+ uses: goreleaser/goreleaser-action@v5
+ with:
+ distribution: goreleaser
+ version: latest
+ args: release --clean -f ${{ vars.GORELEASER_CONFIG_PATH }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.TOFUENV_GITHUB_TOKEN }}
+ - name: Download artifacts
+ uses: robinraju/release-downloader@v1.8
+ with:
+ tag: ${{ github.ref_name }}
+ tarBall: true
+ zipBall: true
+ - name: Check contents
+ run: ls -la
+ - name: Generate checksum
+ uses: jmgilman/actions-generate-checksum@v1
+ with:
+ patterns: |
+ *.zip
+ *.tar.gz
+ method: sha256
+ output: checksums_${{ github.ref_name }}.txt
+ - name: Upload checksum to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.TOFUENV_GITHUB_TOKEN }}
+ file: checksums_${{ github.ref_name }}.txt
+ tag: ${{ github.ref_name }}
diff --git a/cmd/init.go b/cmd/init.go
index 00daffa..d171eac 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -1,5 +1,5 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
diff --git a/cmd/root.go b/cmd/root.go
index 1bd153e..1aefda6 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -1,5 +1,5 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
diff --git a/cmd/tf.go b/cmd/tf.go
index cb76260..3df4e59 100644
--- a/cmd/tf.go
+++ b/cmd/tf.go
@@ -1,12 +1,15 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
import (
+ "bytes"
"fmt"
-
+ "github.com/opentofuutils/tenv/pkg/misc"
+ log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
+ "os/exec"
)
// tfCmd represents the tf command
@@ -20,7 +23,25 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
- fmt.Println("tf called")
+ if !misc.CheckToolInstalled("tfenv") {
+ log.Error("tfenv is not installed. Please, execute 'tenv upgrade-deps' to use 'tenv tf' commands")
+ return
+ }
+
+ toolExec := misc.GetPath("tfenv_exec")
+
+ exec := exec.Command(toolExec, args...)
+ var out bytes.Buffer
+ var stderr bytes.Buffer
+ exec.Stdout = &out
+ exec.Stderr = &stderr
+ err := exec.Run()
+
+ if err != nil {
+ fmt.Println(stderr.String())
+ return
+ }
+ fmt.Println(out.String())
},
}
diff --git a/cmd/tofu.go b/cmd/tofu.go
index 184b831..d723970 100644
--- a/cmd/tofu.go
+++ b/cmd/tofu.go
@@ -1,5 +1,5 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
@@ -23,19 +23,14 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
-
if !misc.CheckToolInstalled("tofuenv") {
log.Error("tofuenv is not installed. Please, execute 'tenv upgrade-deps' to use 'tenv tofu' commands")
return
}
- //fmt.Println("tofu called")
-
- tofuExec := misc.GetPath("tofuenv_exec")
- fmt.Println(tofuExec)
- //fmt.Println(tofuExec)
+ toolExec := misc.GetPath("tofuenv_exec")
- exec := exec.Command(tofuExec, args...)
+ exec := exec.Command(toolExec, args...)
var out bytes.Buffer
var stderr bytes.Buffer
exec.Stdout = &out
@@ -46,14 +41,7 @@ to quickly create a Cobra application.`,
fmt.Println(stderr.String())
return
}
- fmt.Println("Result: " + out.String())
-
- //out, _ := exec.Command(tofuExec, args...).Output()
- ////if err != nil {
- //// fmt.Println(err)
- ////}
- //fmt.Println(string(out))
-
+ fmt.Println(out.String())
},
}
diff --git a/cmd/uninstallDeps.go b/cmd/uninstallDeps.go
index 981a7a7..2c32ac1 100644
--- a/cmd/uninstallDeps.go
+++ b/cmd/uninstallDeps.go
@@ -1,14 +1,11 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
import (
- "fmt"
"github.com/opentofuutils/tenv/pkg/misc"
log "github.com/sirupsen/logrus"
- "os"
-
"github.com/spf13/cobra"
)
@@ -25,15 +22,12 @@ to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
log.Info("Starting to uninstall tenv tools")
- rootDir := misc.GetEnv(misc.RootEnv, "")
- binDir := fmt.Sprintf("%s/bin", rootDir)
-
- err := os.RemoveAll(binDir)
+ err := misc.DeleteFolder(misc.GetPath("bin_dir"))
if err != nil {
- log.Error("Error removing bin directory:", err)
+ log.Error("Error removing dependencies directory:", err)
}
- log.Info("tenv tools uninstalled successfully")
+ log.Info("tenv dependencies have been uninstalled successfully")
},
}
diff --git a/cmd/upgradeDeps.go b/cmd/upgradeDeps.go
index 6722d30..bd47636 100644
--- a/cmd/upgradeDeps.go
+++ b/cmd/upgradeDeps.go
@@ -1,5 +1,5 @@
/*
-Copyright © 2024 NAME HERE
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package cmd
diff --git a/pkg/github/main.go b/pkg/github/main.go
index c4e0701..263a1c6 100644
--- a/pkg/github/main.go
+++ b/pkg/github/main.go
@@ -1,3 +1,6 @@
+/*
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+*/
package github
import (
diff --git a/pkg/misc/arhive.go b/pkg/misc/arhive.go
index c4431b9..a9130a8 100644
--- a/pkg/misc/arhive.go
+++ b/pkg/misc/arhive.go
@@ -1,3 +1,6 @@
+/*
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+*/
package misc
import (
diff --git a/pkg/misc/env.go b/pkg/misc/env.go
index ea92b7b..0a785e5 100644
--- a/pkg/misc/env.go
+++ b/pkg/misc/env.go
@@ -1,3 +1,6 @@
+/*
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+*/
package misc
import "os"
diff --git a/pkg/misc/folders.go b/pkg/misc/folders.go
index ddf3b9f..621de1d 100644
--- a/pkg/misc/folders.go
+++ b/pkg/misc/folders.go
@@ -1,7 +1,10 @@
+/*
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+*/
package misc
import (
- "fmt"
+ log "github.com/sirupsen/logrus"
"os"
)
@@ -22,6 +25,6 @@ func DeleteFolder(path string) error {
return err
}
- fmt.Printf("Deleted folder: %s\n", path)
+ log.Debug("Deleted folder: %s\n", path)
return nil
}
diff --git a/pkg/misc/tool.go b/pkg/misc/tool.go
index f112e0e..a952688 100644
--- a/pkg/misc/tool.go
+++ b/pkg/misc/tool.go
@@ -25,9 +25,9 @@ func PrepareTool(owner, repo, rootDir string) error {
if err != nil {
return err
}
+
defer func() {
- // Clean up: Remove the temporary directory when done
- err := os.RemoveAll(miscDir)
+ err = DeleteFolder(miscDir)
if err != nil {
log.Error("Error removing temporary directory:", err)
}
From 05f55c60b55d2dd0cebf8c9af0d355f58875c12a Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 17:38:28 +0100
Subject: [PATCH 11/32] feat: improve dockerfile
---
Dockerfile | 18 +++++++++++-------
README.md | 6 ++++++
cmd/init.go | 2 +-
cmd/root.go | 2 +-
cmd/upgradeDeps.go | 2 +-
5 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index fbaa0d1..abc522d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,17 +1,21 @@
-FROM golang:1.18 AS builder
+#
+# Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+#
+FROM golang:1.21 AS builder
ENV CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
GIT_TERMINAL_PROMPT=1
-COPY ./internal ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda/internal
-COPY ./go.mod ./go.sum ./main.go ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda/
-WORKDIR ${GOPATH}/src/github.com/kvendingoldo/aws-cognito-backup-lambda
+COPY ./pkg ${GOPATH}/src/github.com/opentofuutils/tenv/pkg
+COPY ./cmd ${GOPATH}/src/github.com/opentofuutils/tenv/cmd
+COPY ./go.mod ./go.sum ./main.go ${GOPATH}/src/github.com/opentofuutils/tenv/
+WORKDIR ${GOPATH}/src/github.com/opentofuutils/tenv
RUN go get ./
-RUN go build -ldflags="-s -w" -o lambda .
+RUN go build -ldflags="-s -w" -o tenv .
FROM gcr.io/distroless/static:nonroot
-COPY --from=builder go/src/github.com/kvendingoldo/aws-cognito-backup-lambda/lambda /app/
+COPY --from=builder go/src/github.com/opentofuutils/tenv/tenv /app/
WORKDIR /app
-ENTRYPOINT ["/app/lambda"]
+ENTRYPOINT ["/app/tenv"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 1097ee8..f233fc3 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,12 @@ Currently, tenv supports the following operating systems:
## Installation
WIP
+## Environment variables
+TENV_ROOT_PATH
+
+TENV_TOFUENV_VERSION
+TENV_TFENV_VERSION
+
## LICENSE
- [tenv inself](https://github.com/tofuutils/tenv/blob/main/LICENSE)
- [tofuenv](https://github.com/tofuutils/tofuenv/blob/main/LICENSE)
diff --git a/cmd/init.go b/cmd/init.go
index d171eac..6e3611b 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -12,7 +12,7 @@ import (
// initCmd represents the init command
var initCmd = &cobra.Command{
Use: "init",
- Short: "Update environment to use 'tenv' correctly",
+ Short: "Update environment to use tenv correctly",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
diff --git a/cmd/root.go b/cmd/root.go
index 1aefda6..908a1c9 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -11,7 +11,7 @@ import (
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
- Use: "github",
+ Use: "tenv",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
diff --git a/cmd/upgradeDeps.go b/cmd/upgradeDeps.go
index bd47636..7f6a33c 100644
--- a/cmd/upgradeDeps.go
+++ b/cmd/upgradeDeps.go
@@ -14,7 +14,7 @@ import (
// upgradeDepsCmd represents the upgradeDeps command
var upgradeDepsCmd = &cobra.Command{
Use: "upgradeDeps",
- Short: "Upgrade github dependencies (tfenv and tofuenv)",
+ Short: "Upgrade tenv dependencies (tfenv and tofuenv)",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
From b2f4a7e88f806c143b15b1e0c1e9e260544a064a Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 17:54:26 +0100
Subject: [PATCH 12/32] feat: update docs
---
cmd/init.go | 19 +++--------------
cmd/root.go | 23 ++++++++++++--------
cmd/tf.go | 10 +++------
cmd/tofu.go | 10 +++------
cmd/uninstallDeps.go | 8 ++-----
cmd/upgradeDeps.go | 8 ++-----
pkg/consts/text/text.go | 47 +++++++++++++++++++++++++++++++++++++++++
7 files changed, 74 insertions(+), 51 deletions(-)
create mode 100644 pkg/consts/text/text.go
diff --git a/cmd/init.go b/cmd/init.go
index 6e3611b..c71b9e9 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -5,6 +5,7 @@ package cmd
import (
"fmt"
+ "github.com/opentofuutils/tenv/pkg/consts/text"
"github.com/spf13/cobra"
)
@@ -13,27 +14,13 @@ import (
var initCmd = &cobra.Command{
Use: "init",
Short: "Update environment to use tenv correctly",
- Long: `A longer description that spans multiple lines and likely contains examples
-and usage of using your command. For example:
-
-Cobra is a CLI library for Go that empowers applications.
-This application is a tool to generate the needed files
-to quickly create a Cobra application.`,
+ Long: text.InitCmdLongText + text.SubCmdHelpText,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("init called")
+ //export PATH="${TOFUENV_ROOT}/bin:${PATH}";
},
}
func init() {
rootCmd.AddCommand(initCmd)
-
- // Here you will define your flags and configuration settings.
-
- // Cobra supports Persistent Flags which will work for this command
- // and all subcommands, e.g.:
- // initCmd.PersistentFlags().String("foo", "", "A help for foo")
-
- // Cobra supports local flags which will only run when this command
- // is called directly, e.g.:
- // initCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
diff --git a/cmd/root.go b/cmd/root.go
index 908a1c9..c3bbbd0 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -4,25 +4,30 @@ Copyright © 2024 Alexander Sharov , Nikolai Mishin , Nikolai Mishin , Nikolai Mishin
Date: Mon, 8 Jan 2024 19:12:39 +0100
Subject: [PATCH 13/32] ref: make a small refactoring
---
.github/workflows/build.yml | 4 ++--
.gitignore | 6 +-----
README.md | 1 -
cmd/init.go | 5 ++---
cmd/root.go | 12 ++++++++---
cmd/tf.go | 7 ++++---
cmd/tofu.go | 7 ++++---
cmd/uninstallDeps.go | 4 ++--
cmd/upgradeDeps.go | 9 +++++----
main.go | 2 +-
pkg/consts/text/text.go | 12 +++++------
pkg/{misc => tool}/tool.go | 20 ++++++++++---------
.../arhive.go => utils/archive/archive.go} | 2 +-
pkg/{misc => utils/env}/env.go | 2 +-
pkg/{misc => utils/fs}/folders.go | 2 +-
pkg/{misc => utils/fs}/path.go | 5 +++--
.../main.go => utils/github/github.go} | 4 ++--
17 files changed, 55 insertions(+), 49 deletions(-)
rename pkg/{misc => tool}/tool.go (66%)
rename pkg/{misc/arhive.go => utils/archive/archive.go} (99%)
rename pkg/{misc => utils/env}/env.go (96%)
rename pkg/{misc => utils/fs}/folders.go (97%)
rename pkg/{misc => utils/fs}/path.go (86%)
rename pkg/{github/main.go => utils/github/github.go} (92%)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e06bc84..80f2c57 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,11 +8,11 @@ on:
env:
registry_url: "registry.hub.docker.com"
- image_repo: "kvendingoldo/aws-cognito-backup-lambda"
+ image_repo: "tofuutils/tenv"
jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
outputs:
version: ${{ steps.set_version.outputs.safe_version }}
steps:
diff --git a/.gitignore b/.gitignore
index 4e7747d..73ff7df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ bin/terraform-*
/use-gpgv
.*.swp
.idea
+.DS_Store
.misc
# Binaries for programs and plugins
@@ -23,11 +24,6 @@ bin/terraform-*
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# Dependency directories (remove the comment below to include it)
-# vendor/
-
-.idea
-.DS_Store
./main
./bin
./root
diff --git a/README.md b/README.md
index f233fc3..6e74fa1 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,6 @@ WIP
## Environment variables
TENV_ROOT_PATH
-
TENV_TOFUENV_VERSION
TENV_TFENV_VERSION
diff --git a/cmd/init.go b/cmd/init.go
index c71b9e9..787503c 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -4,9 +4,8 @@ Copyright © 2024 Alexander Sharov , Nikolai Mishin
+Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
package main
diff --git a/pkg/consts/text/text.go b/pkg/consts/text/text.go
index cf72c53..1feb267 100644
--- a/pkg/consts/text/text.go
+++ b/pkg/consts/text/text.go
@@ -3,18 +3,18 @@ package text
const RootLongText = `A compact CLI that manages OpenTofu / Terraform version via tfenv/tofuenv wrappers
Authors: Alexander Sharov (kvendingoldo@gmail.com), Nikolai Mishin (sanduku.default@gmail.com), Anastasiia Kozlova (anastasiia.kozlova245@gmail.com)
-Contributed at https://github.com/tfutils/tfenv
+Contributed at https://github.com/tofuutils/tenv
`
const EmptyArgsText = `Error: please use --help|-h to explore tenv commands`
const AdditionalText = `A compact CLI that manages OpenTofu / Terraform version via tfenv/tofuenv wrappers
-Authors: Alexander Sharov (kvendingoldo@gmail.com), Nikolai Mishin (sanduku.default@gmail.com(, Anastasiia Kozlova (anastasiia.kozlova245@gmail.com)
+Authors: Alexander Sharov (kvendingoldo@gmail.com), Nikolai Mishin (sanduku.default@gmail.com), Anastasiia Kozlova (anastasiia.kozlova245@gmail.com)
Contributed at https://github.com/tfutils/tfenv
Usages:
- tenv TODO
- tenv TODO
+ tenv tf
+ tenv init
Flags:
--help display command help & instructions`
@@ -37,11 +37,11 @@ const TofuCmdLongText = `This command let you manage OpenTofu version via tofuen
Usages:
tenv tofu list-remote
`
-const UninstallDepsCmdLongText = `This command uninstall all tenv dependencies
+const UninstallDepsCmdLongText = `This command uninstall all tenv dependencies (tfenv and tofuenv)
Usages:
tenv uninstallDeps
`
-const UpgradeDepsCmdLongText = `This command upgrade all tenv dependencies
+const UpgradeDepsCmdLongText = `This command upgrade all tenv dependencies: download tfenv, tofuenv and configure their environment variables
Usages:
tenv upgradeDeps
`
diff --git a/pkg/misc/tool.go b/pkg/tool/tool.go
similarity index 66%
rename from pkg/misc/tool.go
rename to pkg/tool/tool.go
index a952688..3c0ae4f 100644
--- a/pkg/misc/tool.go
+++ b/pkg/tool/tool.go
@@ -1,8 +1,10 @@
-package misc
+package tool
import (
"fmt"
- "github.com/opentofuutils/tenv/pkg/github"
+ "github.com/opentofuutils/tenv/pkg/utils/archive"
+ "github.com/opentofuutils/tenv/pkg/utils/fs"
+ "github.com/opentofuutils/tenv/pkg/utils/github"
log "github.com/sirupsen/logrus"
"os"
@@ -10,24 +12,24 @@ import (
func CheckToolInstalled(name string) bool {
- path := GetPath("tofuenv_exec")
+ path := fs.GetPath("tofuenv_exec")
_, err := os.Stat(path)
return !os.IsExist(err)
}
func PrepareTool(owner, repo, rootDir string) error {
- binDir := GetPath("bin_dir")
- miscDir := GetPath("misc_dir")
+ binDir := fs.GetPath("bin_dir")
+ miscDir := fs.GetPath("misc_dir")
// Create temporary directory where tarballs will be stored
- err := CreateFolder(miscDir)
+ err := fs.CreateFolder(miscDir)
if err != nil {
return err
}
defer func() {
- err = DeleteFolder(miscDir)
+ err = fs.DeleteFolder(miscDir)
if err != nil {
log.Error("Error removing temporary directory:", err)
}
@@ -35,12 +37,12 @@ func PrepareTool(owner, repo, rootDir string) error {
tarballPath := fmt.Sprintf("%s/%s-%s", miscDir, owner, repo)
if err := github.DownloadLatestRelease(owner, repo, tarballPath); err != nil {
- fmt.Println("Error:", err)
+ log.Error("Error:", err)
return err
}
log.Info(fmt.Sprintf("Latest %s release owned by %s downloaded successfully", repo, owner))
- err = ExtractTarGz(tarballPath, fmt.Sprintf("%s/%s", binDir, repo))
+ err = archive.ExtractTarGz(tarballPath, fmt.Sprintf("%s/%s", binDir, repo))
if err != nil {
log.Warn("Error:", err)
} else {
diff --git a/pkg/misc/arhive.go b/pkg/utils/archive/archive.go
similarity index 99%
rename from pkg/misc/arhive.go
rename to pkg/utils/archive/archive.go
index a9130a8..c54310a 100644
--- a/pkg/misc/arhive.go
+++ b/pkg/utils/archive/archive.go
@@ -1,7 +1,7 @@
/*
Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
-package misc
+package archive
import (
"archive/tar"
diff --git a/pkg/misc/env.go b/pkg/utils/env/env.go
similarity index 96%
rename from pkg/misc/env.go
rename to pkg/utils/env/env.go
index 0a785e5..88349d1 100644
--- a/pkg/misc/env.go
+++ b/pkg/utils/env/env.go
@@ -1,7 +1,7 @@
/*
Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
-package misc
+package env
import "os"
diff --git a/pkg/misc/folders.go b/pkg/utils/fs/folders.go
similarity index 97%
rename from pkg/misc/folders.go
rename to pkg/utils/fs/folders.go
index 621de1d..72f28b3 100644
--- a/pkg/misc/folders.go
+++ b/pkg/utils/fs/folders.go
@@ -1,7 +1,7 @@
/*
Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
*/
-package misc
+package fs
import (
log "github.com/sirupsen/logrus"
diff --git a/pkg/misc/path.go b/pkg/utils/fs/path.go
similarity index 86%
rename from pkg/misc/path.go
rename to pkg/utils/fs/path.go
index 2dfa7a5..d50162f 100644
--- a/pkg/misc/path.go
+++ b/pkg/utils/fs/path.go
@@ -1,12 +1,13 @@
-package misc
+package fs
import (
"fmt"
+ "github.com/opentofuutils/tenv/pkg/utils/env"
log "github.com/sirupsen/logrus"
)
func GetPath(name string) string {
- rootDir := GetEnv(RootEnv, "")
+ rootDir := env.GetEnv(env.RootEnv, "")
switch name {
case "root_dir":
diff --git a/pkg/github/main.go b/pkg/utils/github/github.go
similarity index 92%
rename from pkg/github/main.go
rename to pkg/utils/github/github.go
index 263a1c6..1a7a873 100644
--- a/pkg/github/main.go
+++ b/pkg/utils/github/github.go
@@ -30,10 +30,10 @@ func DownloadLatestRelease(owner, repo, destPath string) error {
return err
}
- zipball_url := releaseInfo["tarball_url"].(string)
+ tarball_url := releaseInfo["tarball_url"].(string)
// Download the latest release zip file
- if err := DownloadFile(zipball_url, destPath); err != nil {
+ if err := DownloadFile(tarball_url, destPath); err != nil {
return err
}
From fc771be3682b5e6cf8ed9da3282b68aa68f1004d Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Mon, 8 Jan 2024 19:20:37 +0100
Subject: [PATCH 14/32] feat: add docs
---
.github/CONTRIBUTING.md | 1 +
CHANGELOG.md | 0
CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++
README.md | 19 +++++++++-
assets/logo.jpeg | Bin 0 -> 45411 bytes
cmd/root.go | 8 +++--
6 files changed, 101 insertions(+), 3 deletions(-)
create mode 100644 .github/CONTRIBUTING.md
create mode 100644 CHANGELOG.md
create mode 100644 CODE_OF_CONDUCT.md
create mode 100644 assets/logo.jpeg
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..a48c585
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1 @@
+See [contributing quick start](todo) on our website.
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..2b49986
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at info@golangci.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
\ No newline at end of file
diff --git a/README.md b/README.md
index 6e74fa1..7e1501b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# tenv
+
+
+
tenv
+ Terraform/OpenTofu version manager
+
+
[tenv](https://github.com/tofuutils/tenv) version manager that build on top of [tofuenv](https://github.com/tofuutils/tofuenv) and [tfenv](https://github.com/tfutils/tfenv) and manages [Terraform](https://www.terraform.io/) and [OpenTofu](https://opentofu.org/) binaries
@@ -22,6 +27,18 @@ TENV_ROOT_PATH
TENV_TOFUENV_VERSION
TENV_TFENV_VERSION
+## Contributors
+
+This project exists thanks to all the people who contribute. [How to contribute](todo).
+
+### Core Team
+
+
+About core team
+
+The tenv Core Team is a group of contributors that have demonstrated a lasting enthusiasm for the project and community.
+The tenv Core Team has GitHub admin privileges on the repo.
+
## LICENSE
- [tenv inself](https://github.com/tofuutils/tenv/blob/main/LICENSE)
- [tofuenv](https://github.com/tofuutils/tofuenv/blob/main/LICENSE)
diff --git a/assets/logo.jpeg b/assets/logo.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..203f2a3cdee2086eca4f4c11fea6fb5033732eea
GIT binary patch
literal 45411
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
Date: Mon, 8 Jan 2024 18:21:33 +0000
Subject: [PATCH 15/32] ci: bump docker/setup-buildx-action from 1 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v3)
---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80f2c57..bc2c312 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,7 +27,7 @@ jobs:
platforms: all
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
+ uses: docker/setup-buildx-action@v3
-
name: Login to Docker registry
uses: docker/login-action@v1
From 4966769228058f39a049394fdb82ce79e16d372d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Jan 2024 18:21:35 +0000
Subject: [PATCH 16/32] ci: bump kvendingoldo/semver-action from 1.10 to 1.19
Bumps [kvendingoldo/semver-action](https://github.com/kvendingoldo/semver-action) from 1.10 to 1.19.
- [Release notes](https://github.com/kvendingoldo/semver-action/releases)
- [Commits](https://github.com/kvendingoldo/semver-action/compare/v1.10...v1.19)
---
updated-dependencies:
- dependency-name: kvendingoldo/semver-action
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80f2c57..e7fc67d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -38,7 +38,7 @@ jobs:
-
name: Set application version
id: set_version
- uses: kvendingoldo/semver-action@v1.10
+ uses: kvendingoldo/semver-action@v1.19
with:
primary_branch: 'main'
-
From 50c13237f2d2f4165a3c2282ef16b53acd21717d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Jan 2024 18:21:38 +0000
Subject: [PATCH 17/32] ci: bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/build.yml | 2 +-
.github/workflows/golangci-lint.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80f2c57..270f001 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,7 +17,7 @@ jobs:
version: ${{ steps.set_version.outputs.safe_version }}
steps:
-
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
-
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index 4cbb88c..1f5b725 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -18,7 +18,7 @@ jobs:
with:
go-version: 1.18
-
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
-
From f906db6554266be959066cbd875da1d712d11342 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Jan 2024 18:21:41 +0000
Subject: [PATCH 18/32] ci: bump docker/setup-qemu-action from 1 to 3
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v1...v3)
---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80f2c57..52c9e86 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,7 +22,7 @@ jobs:
fetch-depth: 0
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v1
+ uses: docker/setup-qemu-action@v3
with:
platforms: all
-
From 7000795a7bbd02683b4bc157ce31b5de88513009 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 8 Jan 2024 18:21:44 +0000
Subject: [PATCH 19/32] ci: bump docker/login-action from 1 to 3
Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1...v3)
---
updated-dependencies:
- dependency-name: docker/login-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80f2c57..cc18a9b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
uses: docker/setup-buildx-action@v1
-
name: Login to Docker registry
- uses: docker/login-action@v1
+ uses: docker/login-action@v3
with:
registry: ${{ env.registry_url }}
username: ${{ secrets.REGISTRY_USER }}
From a6a33bc04e29e63eb7867d1905916e0e6dd06705 Mon Sep 17 00:00:00 2001
From: anastasiiakozlova245
Date: Tue, 9 Jan 2024 21:43:31 +0100
Subject: [PATCH 20/32] feat: update goreleaser settings for release ci
---
.github/.goreleaser.yml | 41 ++++++++++++++++++++++++++++++++---
.github/workflows/release.yml | 22 -------------------
2 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/.github/.goreleaser.yml b/.github/.goreleaser.yml
index 50be17e..33e4318 100644
--- a/.github/.goreleaser.yml
+++ b/.github/.goreleaser.yml
@@ -1,12 +1,47 @@
version: 1
+before:
+ hooks:
+ # You may remove this if you don't use go modules.
+ - go get ./
+
builds:
- - skip: true
- env:
+ - env:
- CGO_ENABLED=0
+ binary: tenv
+ ldflags:
+ - -s -w
+ goos:
+ - linux
+ - windows
+ - darwin
+ ignore:
+ - goos: darwin
+ goarch: 386
+
+archives:
+ - format: tar.gz
+ # this name template makes the OS and Arch compatible with the results of `uname`.
+ name_template: >-
+ {{ .ProjectName }}_
+ {{ .Tag }}_
+ {{- title .Os }}_
+ {{- if eq .Arch "amd64" }}x86_64
+ {{- else if eq .Arch "386" }}i386
+ {{- else }}{{ .Arch }}{{ end }}
+ {{- if .Arm }}v{{ .Arm }}{{ end }}
+ # use zip for windows archives
+ format_overrides:
+ - goos: windows
+ format: zip
changelog:
sort: asc
filters:
exclude:
- - '^test:'
+ - "^docs:"
+ - "^test:"
+
+checksum:
+ name_template: "{{ .ProjectName }}_{{ .Tag }}_checksums.txt"
+ algorithm: sha256
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2bf4f04..7f4d754 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,25 +28,3 @@ jobs:
args: release --clean -f ${{ vars.GORELEASER_CONFIG_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.TOFUENV_GITHUB_TOKEN }}
- - name: Download artifacts
- uses: robinraju/release-downloader@v1.8
- with:
- tag: ${{ github.ref_name }}
- tarBall: true
- zipBall: true
- - name: Check contents
- run: ls -la
- - name: Generate checksum
- uses: jmgilman/actions-generate-checksum@v1
- with:
- patterns: |
- *.zip
- *.tar.gz
- method: sha256
- output: checksums_${{ github.ref_name }}.txt
- - name: Upload checksum to release
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.TOFUENV_GITHUB_TOKEN }}
- file: checksums_${{ github.ref_name }}.txt
- tag: ${{ github.ref_name }}
From 5d14a4ce401ac86bfa5c68dc3e6b80495df1d49a Mon Sep 17 00:00:00 2001
From: anastasiiakozlova245
Date: Tue, 9 Jan 2024 22:08:04 +0100
Subject: [PATCH 21/32] feat: update secret name
---
.github/workflows/release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7f4d754..86c552e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,4 +27,4 @@ jobs:
version: latest
args: release --clean -f ${{ vars.GORELEASER_CONFIG_PATH }}
env:
- GITHUB_TOKEN: ${{ secrets.TOFUENV_GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.TENV_GITHUB_TOKEN }}
From 285a5b9d4e7810a63535afd813af6f209fb12f27 Mon Sep 17 00:00:00 2001
From: anastasiiakozlova245
Date: Tue, 9 Jan 2024 22:14:25 +0100
Subject: [PATCH 22/32] feat: remove docs prefix from changelog ignore
---
.github/.goreleaser.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/.goreleaser.yml b/.github/.goreleaser.yml
index 33e4318..0d5e46c 100644
--- a/.github/.goreleaser.yml
+++ b/.github/.goreleaser.yml
@@ -39,7 +39,6 @@ changelog:
sort: asc
filters:
exclude:
- - "^docs:"
- "^test:"
checksum:
From cf1f4ba4972c1ed3d505df6c5ca05252a55d1f7d Mon Sep 17 00:00:00 2001
From: Alexander Sharov
Date: Thu, 11 Jan 2024 17:44:11 +0100
Subject: [PATCH 23/32] feat: add basic CLI layout for tenv
---
Dockerfile | 14 ++-
LICENSE | 222 +++++++++++++++++++++++++++++++----
Makefile | 16 +++
cmd/init.go | 25 ----
cmd/root.go | 62 ----------
main.go => cmd/tenv/main.go | 26 +++-
cmd/tenv/root.go | 60 ++++++++++
cmd/tenv/root_tf.go | 55 +++++++++
cmd/tenv/root_tofu.go | 55 +++++++++
cmd/tenv/tf_install.go | 47 ++++++++
cmd/tenv/tf_list.go | 47 ++++++++
cmd/tenv/tf_list_remote.go | 47 ++++++++
cmd/tenv/tf_pin.go | 47 ++++++++
cmd/tenv/tf_uninstall.go | 47 ++++++++
cmd/tenv/tf_use.go | 47 ++++++++
cmd/tenv/tofu_install.go | 47 ++++++++
cmd/tenv/tofu_list.go | 47 ++++++++
cmd/tenv/tofu_list_remote.go | 47 ++++++++
cmd/tenv/tofu_pin.go | 47 ++++++++
cmd/tenv/tofu_uninstall.go | 47 ++++++++
cmd/tenv/tofu_use.go | 47 ++++++++
cmd/terraform/terraform.go | 18 +++
cmd/tf.go | 57 ---------
cmd/tofu.go | 57 ---------
cmd/tofu/tofu.tf.go | 18 +++
cmd/uninstallDeps.go | 42 -------
cmd/upgradeDeps.go | 52 --------
pkg/consts/text/text.go | 17 +++
pkg/tool/tool.go | 17 +++
pkg/utils/archive/archive.go | 18 ++-
pkg/utils/env/env.go | 18 ++-
pkg/utils/fs/folders.go | 18 ++-
pkg/utils/fs/path.go | 17 +++
pkg/utils/github/github.go | 18 ++-
34 files changed, 1137 insertions(+), 329 deletions(-)
delete mode 100644 cmd/init.go
delete mode 100644 cmd/root.go
rename main.go => cmd/tenv/main.go (52%)
create mode 100644 cmd/tenv/root.go
create mode 100644 cmd/tenv/root_tf.go
create mode 100644 cmd/tenv/root_tofu.go
create mode 100644 cmd/tenv/tf_install.go
create mode 100644 cmd/tenv/tf_list.go
create mode 100644 cmd/tenv/tf_list_remote.go
create mode 100644 cmd/tenv/tf_pin.go
create mode 100644 cmd/tenv/tf_uninstall.go
create mode 100644 cmd/tenv/tf_use.go
create mode 100644 cmd/tenv/tofu_install.go
create mode 100644 cmd/tenv/tofu_list.go
create mode 100644 cmd/tenv/tofu_list_remote.go
create mode 100644 cmd/tenv/tofu_pin.go
create mode 100644 cmd/tenv/tofu_uninstall.go
create mode 100644 cmd/tenv/tofu_use.go
create mode 100644 cmd/terraform/terraform.go
delete mode 100644 cmd/tf.go
delete mode 100644 cmd/tofu.go
create mode 100644 cmd/tofu/tofu.tf.go
delete mode 100644 cmd/uninstallDeps.go
delete mode 100644 cmd/upgradeDeps.go
diff --git a/Dockerfile b/Dockerfile
index abc522d..c26ce9c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,17 @@
#
-# Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
+# Copyright 2024 opentofuutils authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
FROM golang:1.21 AS builder
diff --git a/LICENSE b/LICENSE
index 5601e82..2b8f6d5 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,201 @@
-The MIT License (MIT)
-
-Copyright (c) 2024 Nikolai Mishin, Alexander Sharov, Anastasiia Kozlova
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2024 opentofuutils authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 100f5c0..5bc0e9d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,19 @@
+#
+# Copyright 2024 opentofuutils authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
##@ General
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
diff --git a/cmd/init.go b/cmd/init.go
deleted file mode 100644
index 787503c..0000000
--- a/cmd/init.go
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-Copyright © 2024 Alexander Sharov , Nikolai Mishin , Anastasiia Kozlova
-*/
-package cmd
-
-import (
- "github.com/opentofuutils/tenv/pkg/consts/text"
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
-)
-
-// initCmd represents the init command
-var initCmd = &cobra.Command{
- Use: "init",
- Short: "Update environment to use tenv correctly",
- Long: text.InitCmdLongText + text.SubCmdHelpText,
- Run: func(cmd *cobra.Command, args []string) {
- log.Info("Starting to init tenv")
- //export PATH="${TOFUENV_ROOT}/bin:${PATH}";
- },
-}
-
-func init() {
- rootCmd.AddCommand(initCmd)
-}
diff --git a/cmd/root.go b/cmd/root.go
deleted file mode 100644
index 53996e3..0000000
--- a/cmd/root.go
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-Copyright © 2024 Alexander Sharov