#!/bin/bash # Info: Script to install tools # Author: JesusPerezLorenzo # Release: 1.0 # Date: 12-11-2023 [ "$DEBUG" == "-x" ] && set -x USAGE="install-tools [ tool-name: providers tera k9s, etc | all] [--update] As alternative use environment var TOOL_TO_INSTALL with a list-of-tools (separeted with spaces) Versions are set in ./versions file This can be called by directly with an argumet or from an other srcipt " ORG=$(pwd) function _install_cmds { OS="$(uname | tr '[:upper:]' '[:lower:]')" local has_cmd for cmd in $CMDS_PROVISIONING do has_cmd=$(type -P $cmd) if [ -z "$has_cmd" ] ; then case "$OS" in darwin) brew install $cmd ;; linux) sudo apt install $cmd ;; *) echo "Install $cmd in your PATH" ;; esac fi done } function _install_providers { local match=$1 shift local options local info_keys options="$*" info_keys="info version site" if [ -z "$match" ] || [ "$match" == "all" ] || [ "$match" == "-" ]; then match="all" fi for prov in $(ls $PROVIDERS_PATH | grep -v "^_" ) do prov_name=$(basename "$prov") [ ! -d "$PROVIDERS_PATH/$prov_name/templates" ] && continue if [ "$match" == "all" ] || [ "$prov_name" == "$match" ] ; then [ -x "$PROVIDERS_PATH/$prov_name/bin/install.sh" ] && $PROVIDERS_PATH/$prov_name/bin/install.sh $options elif [ "$match" == "?" ] ; then [ -n "$options" ] && [ -z "$(echo "$options" | grep ^$prov_name)" ] && continue if [ -r "$PROVIDERS_PATH/$prov_name/provisioning.yaml" ] ; then echo "-------------------------------------------------------" for key in $info_keys do echo -n "$key:" [ "$key" != "version" ] && echo -ne "\t" echo " $(grep "^$key:" "$PROVIDERS_PATH/$prov_name/provisioning.yaml" | sed "s/$key: //g")" done [ -n "$options" ] && echo "________________________________________________________" else echo "$prov_name" fi fi done [ "$match" == "?" ] && [ -z "$options" ] && echo "________________________________________________________" } function _install_tools { local match=$1 shift local options options="$*" # local has_jq # local jq_version # local has_yq # local yq_version local has_nu local nu_version local has_kcl local kcl_version local has_tera local tera_version local has_k9s local k9s_version local has_age local age_version local has_sops local sops_version OS="$(uname | tr '[:upper:]' '[:lower:]')" ORG_OS=$(uname) ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" ORG_ARCH="$(uname -m)" if [ -z "$CHECK_ONLY" ] && [ "$match" == "all" ] ; then _install_cmds fi # if [ -n "$JQ_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "jq" ] ; then # has_jq=$(type -P jq) # num_version="0" # [ -n "$has_jq" ] && jq_version=$(jq -V | sed 's/jq-//g') && num_version=${jq_version//\./} # expected_version_num=${JQ_VERSION//\./} # if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then # curl -fsSLO "https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-${OS}-${ARCH}" && # chmod +x "jq-${OS}-${ARCH}" && # sudo mv "jq-${OS}-${ARCH}" /usr/local/bin/jq && # printf "%s\t%s\n" "jq" "installed $JQ_VERSION" # elif [ -n "$CHECK_ONLY" ] ; then # printf "%s\t%s\t%s\n" "jq" "$jq_version" "expected $JQ_VERSION" # else # printf "%s\t%s\n" "jq" "already $JQ_VERSION" # fi # fi # if [ -n "$YQ_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "yq" ] ; then # has_yq=$(type -P yq) # num_version="0" # [ -n "$has_yq" ] && yq_version=$(yq -V | cut -f4 -d" " | sed 's/v//g') && num_version=${yq_version//\./} # expected_version_num=${YQ_VERSION//\./} # if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then # curl -fsSLO "https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_${OS}_${ARCH}.tar.gz" && # tar -xzf "yq_${OS}_${ARCH}.tar.gz" && # sudo mv "yq_${OS}_${ARCH}" /usr/local/bin/yq && # sudo ./install-man-page.sh && # rm -f install-man-page.sh yq.1 "yq_${OS}_${ARCH}.tar.gz" && # printf "%s\t%s\n" "yq" "installed $YQ_VERSION" # elif [ -n "$CHECK_ONLY" ] ; then # printf "%s\t%s\t%s\n" "yq" "$yq_version" "expected $YQ_VERSION" # else # printf "%s\t%s\n" "yq" "already $YQ_VERSION" # fi # fi if [ -n "$NU_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "nu" ] ; then has_nu=$(type -P nu) num_version="0" [ -n "$has_nu" ] && nu_version=$(nu -v) && num_version=${nu_version//\./} && num_version=${num_version//0/} expected_version_num=${NU_VERSION//\./} expected_version_num=${expected_version_num//0/} [ -z "$num_version" ] && num_version=0 if [ -z "$num_version" ] && [ "$num_version" -lt "$expected_version_num" ] ; then printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION require installation" elif [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION require installation" elif [ -n "$CHECK_ONLY" ] ; then printf "%s\t%s\t%s\n" "nu" "$nu_version" "expected $NU_VERSION" else printf "%s\t%s\n" "nu" "already $NU_VERSION" fi fi if [ -n "$KCL_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "kcl" ] ; then has_kcl=$(type -P kcl) num_version=0 [ -n "$has_kcl" ] && kcl_version=$(kcl -v | cut -f3 -d" " | sed 's/ //g') && num_version=${kcl_version//\./} expected_version_num=${KCL_VERSION//\./} [ -z "$num_version" ] && num_version=0 if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then curl -fsSLO "https://github.com/kcl-lang/cli/releases/download/v${KCL_VERSION}/kcl-v${KCL_VERSION}-${OS}-${ARCH}.tar.gz" && tar -xzf "kcl-v${KCL_VERSION}-${OS}-${ARCH}.tar.gz" && sudo mv kcl /usr/local/bin/kcl && rm -f "kcl-v${KCL_VERSION}-${OS}-${ARCH}.tar.gz" && printf "%s\t%s\n" "kcl" "installed $KCL_VERSION" elif [ -n "$CHECK_ONLY" ] ; then printf "%s\t%s\t%s\n" "kcl" "$kcl_version" "expected $KCL_VERSION" else printf "%s\t%s\n" "kcl" "already $KCL_VERSION" fi fi #if [ -n "$TERA_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "tera" ] ; then # has_tera=$(type -P tera) # num_version="0" # [ -n "$has_tera" ] && tera_version=$(tera -V | cut -f2 -d" " | sed 's/teracli//g') && num_version=${tera_version//\./} # expected_version_num=${TERA_VERSION//\./} # [ -z "$num_version" ] && num_version=0 # if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then # if [ -x "$(dirname "$0")/../tools/tera_${OS}_${ARCH}" ] ; then # sudo cp "$(dirname "$0")/../tools/tera_${OS}_${ARCH}" /usr/local/bin/tera && printf "%s\t%s\n" "tera" "installed $TERA_VERSION" # else # echo "Error: $(dirname "$0")/../tools/tera_${OS}_${ARCH} not found !!" # exit 2 # fi # elif [ -n "$CHECK_ONLY" ] ; then # printf "%s\t%s\t%s\n" "tera" "$tera_version" "expected $TERA_VERSION" # else # printf "%s\t%s\n" "tera" "already $TERA_VERSION" # fi #fi if [ -n "$K9S_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "k9s" ] ; then has_k9s=$(type -P k9s) num_version="0" [ -n "$has_k9s" ] && k9s_version="$( k9s version | grep Version | cut -f2 -d"v" | sed 's/ //g')" && num_version=${k9s_version//\./} expected_version_num=${K9S_VERSION//\./} [ -z "$num_version" ] && num_version=0 if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then mkdir -p k9s && cd k9s && curl -fsSLO https://github.com/derailed/k9s/releases/download/v${K9S_VERSION}/k9s_${ORG_OS}_${ARCH}.tar.gz && tar -xzf "k9s_${ORG_OS}_${ARCH}.tar.gz" && sudo mv k9s /usr/local/bin && cd "$ORG" && rm -rf /tmp/k9s "/k9s_${ORG_OS}_${ARCH}.tar.gz" && printf "%s\t%s\n" "k9s" "installed $K9S_VERSION" elif [ -n "$CHECK_ONLY" ] ; then printf "%s\t%s\t%s\n" "k9s" "$k9s_version" "expected $K9S_VERSION" else printf "%s\t%s\n" "k9s" "already $K9S_VERSION" fi fi if [ -n "$AGE_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "age" ] ; then has_age=$(type -P age) num_version="0" [ -n "$has_age" ] && age_version="${AGE_VERSION}" && num_version=${age_version//\./} expected_version_num=${AGE_VERSION//\./} if [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then curl -fsSLO https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz && tar -xzf age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz && sudo mv age/age /usr/local/bin && sudo mv age/age-keygen /usr/local/bin && rm -rf age "age-v${AGE_VERSION}-${OS}-${ARCH}.tar.gz" && printf "%s\t%s\n" "age" "installed $AGE_VERSION" elif [ -n "$CHECK_ONLY" ] ; then printf "%s\t%s\t%s\n" "age" "$age_version" "expected $AGE_VERSION" else printf "%s\t%s\n" "age" "already $AGE_VERSION" fi fi if [ -n "$SOPS_VERSION" ] && [ "$match" == "all" ] || [ "$match" == "sops" ] ; then has_sops=$(type -P sops) num_version="0" [ -n "$has_sops" ] && sops_version="$(sops -v | grep ^sops | cut -f2 -d" " | sed 's/ //g')" && num_version=${sops_version//\./} expected_version_num=${SOPS_VERSION//\./} [ -z "$num_version" ] && num_version=0 if [ -z "$expected_version_num" ] ; then printf "%s\t%s\t%s\n" "sops" "$sops_version" "expected $SOPS_VERSION" elif [ -z "$CHECK_ONLY" ] && [ "$num_version" -lt "$expected_version_num" ] ; then mkdir -p sops && cd sops && curl -fsSLO https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.${OS}.${ARCH} && mv sops-v${SOPS_VERSION}.${OS}.${ARCH} sops && chmod +x sops && sudo mv sops /usr/local/bin && rm -f sops-v${SOPS_VERSION}.${OS}.${ARCH} sops && printf "%s\t%s\n" "sops" "installed $SOPS_VERSION" elif [ -n "$CHECK_ONLY" ] ; then printf "%s\t%s\t%s\n" "sops" "$sops_version" "expected $SOPS_VERSION" else printf "%s\t%s\n" "sops" "already $SOPS_VERSION" fi fi } function _detect_tool_version { local tool=$1 # Try to detect version using tool-specific commands case "$tool" in hcloud) hcloud version 2>/dev/null | grep -o 'hcloud [0-9.]*' | awk '{print $2}' || echo "" ;; upctl) upctl version 2>/dev/null | grep -i "Version" | head -1 | sed 's/.*Version:\s*//' | sed 's/[[:space:]]*$//' || echo "" ;; aws) aws --version 2>/dev/null | cut -d' ' -f1 | sed 's/aws-cli\///' || echo "" ;; nu | nushell) nu -v 2>/dev/null | head -1 || echo "" ;; kcl) kcl -v 2>/dev/null | grep "kcl version" | sed 's/.*version\s*//' || echo "" ;; sops) sops -v 2>/dev/null | head -1 | awk '{print $2}' || echo "" ;; age) age --version 2>/dev/null | head -1 | sed 's/^v//' || echo "" ;; k9s) k9s version 2>/dev/null | grep -o "Version\s*v[0-9.]*" | sed 's/.*v//' || echo "" ;; *) # Fallback: try tool version $tool version 2>/dev/null | head -1 || echo "" ;; esac } function _normalize_version { local version=$1 # Remove any 'v' prefix and return just major.minor echo "$version" | sed 's/^v//' | cut -d. -f1-2 } function _should_install_tool { local tool=$1 local target_version=$2 local force_update=$3 # Always install if --update flag is used if [ "$force_update" = "yes" ] ; then return 0 fi # Get installed version local installed_version=$(_detect_tool_version "$tool") # If tool not installed, install it if [ -z "$installed_version" ] ; then return 0 fi # Normalize versions for comparison (handle 0.109.0 vs 0.109) local norm_installed=$(_normalize_version "$installed_version") local norm_target=$(_normalize_version "$target_version") # If installed version equals target version, skip installation if [ "$norm_installed" = "$norm_target" ] ; then printf "%s\t%s\t%s\n" "$tool" "$installed_version" "already up to date" return 1 fi # If versions differ, install (update) return 0 } function _try_install_provider_tool { local tool=$1 local options=$2 local force_update=$3 # Look for the tool in provider kcl/version.k files (KCL is single source of truth) for prov in $(ls $PROVIDERS_PATH 2>/dev/null | grep -v "^_" ) do if [ -r "$PROVIDERS_PATH/$prov/kcl/version.k" ] ; then # Compile KCL file to JSON and extract version data (single source of truth) local kcl_file="$PROVIDERS_PATH/$prov/kcl/version.k" local kcl_output="" local tool_version="" local tool_name="" # Compile KCL to JSON and capture output kcl_output=$(kcl run "$kcl_file" --format json 2>/dev/null) # Extract tool name and version from JSON tool_name=$(echo "$kcl_output" | grep -o '"name": "[^"]*"' | head -1 | sed 's/"name": "//;s/"$//') tool_version=$(echo "$kcl_output" | grep -o '"current": "[^"]*"' | head -1 | sed 's/"current": "//;s/"$//') # If this is the tool we're looking for if [ "$tool_name" == "$tool" ] && [ -n "$tool_version" ] ; then # Check if installation is needed if ! _should_install_tool "$tool" "$tool_version" "$force_update" ; then return 0 fi if [ -x "$PROVIDERS_PATH/$prov/bin/install.sh" ] ; then # Set environment variables for the provider's install.sh script # Provider scripts have different interfaces, so set env vars for all of them if [ "$prov" = "upcloud" ] ; then # UpCloud expects: tool name as param, UPCLOUD_UPCTL_VERSION env var for version export UPCLOUD_UPCTL_VERSION="$tool_version" $PROVIDERS_PATH/$prov/bin/install.sh "$tool_name" $options elif [ "$prov" = "hetzner" ] ; then # Hetzner expects: version as param (from kcl/version.k) $PROVIDERS_PATH/$prov/bin/install.sh "$tool_version" $options elif [ "$prov" = "aws" ] ; then # AWS format - set env var and pass tool name export AWS_AWS_VERSION="$tool_version" $PROVIDERS_PATH/$prov/bin/install.sh "$tool_name" $options else # Generic: try version as parameter first $PROVIDERS_PATH/$prov/bin/install.sh "$tool_version" $options fi return 0 fi fi fi done return 1 } function _on_tools { local tools_list=$1 shift # Remove first argument # Check if --update flag is present local force_update="no" for arg in "$@" do if [ "$arg" = "--update" ] ; then force_update="yes" break fi done [ -z "$tools_list" ] || [[ "$tools_list" == -* ]] && tools_list=${TOOL_TO_INSTALL:-all} case $tools_list in "all") _install_tools "all" "$@" _install_providers "all" "$@" ;; "providers" | "prov" | "p") _install_providers "$@" ;; *) for tool in $tools_list do [[ "$tool" == -* ]] && continue # First try to find and install as provider tool if _try_install_provider_tool "$tool" "" "$force_update" ; then continue fi # Otherwise try core system tools _install_tools "$tool" "$@" done esac } set -o allexport ## shellcheck disable=SC1090 [ -n "$PROVISIONING_ENV" ] && [ -r "$PROVISIONING_ENV" ] && source "$PROVISIONING_ENV" [ -r "../env-provisioning" ] && source ../env-provisioning [ -r "env-provisioning" ] && source ./env-provisioning #[ -r ".env" ] && source .env set set +o allexport export PROVISIONING=${PROVISIONING:-/usr/local/provisioning} if [ -r "$(dirname "$0")/../versions" ] ; then . "$(dirname "$0")"/../versions elif [ -r "$(dirname "$0")/versions" ] ; then . "$(dirname "$0")"/versions fi export CMDS_PROVISIONING=${CMDS_PROVISIONING:-"tree"} PROVIDERS_PATH=${PROVIDERS_PATH:-"$PROVISIONING/extensions/providers"} if [ -z "$1" ] ; then CHECK_ONLY="yes" _on_tools all else [ "$1" == "-h" ] && echo "$USAGE" && shift [ "$1" == "check" ] && CHECK_ONLY="yes" && shift [ -n "$1" ] && cd /tmp && _on_tools "$@" fi exit 0