prvng_core/cli/tools-install
Jesús Pérez 85ce530733
feat: update provisioning core CLI, libraries, and plugins
Update core components including CLI, Nushell libraries, plugins system,
and utility scripts for the provisioning system.

CLI Updates:
- Command implementations
- CLI utilities and dispatching
- Help system improvements
- Command validation

Library Updates:
- Configuration management system
- Infrastructure validation
- Extension system improvements
- Secrets management
- Workspace operations
- Cache management system

Plugin System:
- Interactive form plugin (inquire)
- KCL integration plugin
- Performance optimization plugins
- Plugin registration system

Utilities:
- Build and distribution scripts
- Installation procedures
- Testing utilities
- Development tools

Documentation:
- Library module documentation
- Extension API guides
- Plugin usage guides
- Service management documentation

All changes are backward compatible. No breaking changes.
2025-12-11 21:57:05 +00:00

443 lines
16 KiB
Bash
Executable File

#!/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