Skip to content

Instantly share code, notes, and snippets.

@OleksandrKucherenko
Created August 17, 2022 08:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OleksandrKucherenko/700f3bf15509c21d78a2a9714e3e2770 to your computer and use it in GitHub Desktop.
Save OleksandrKucherenko/700f3bf15509c21d78a2a9714e3e2770 to your computer and use it in GitHub Desktop.
How to verify BASH script dependencies
#!/usr/bin/env bash
# shellcheck disable=SC2034
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1090 source=commons.sh
source "$SCRIPT_DIR/commons.sh"
logger dependencies "$@" # register own debug tag & logger functions
#set -x # Uncomment to DEBUG
# shellcheck disable=SC2001,SC2155,SC2046,SC2116
function isDebug() {
local args=("$@")
if [[ "${args[*]}" =~ "--debug" ]]; then echo true; else echo false; fi
}
function isExec() {
local args=("$@")
if [[ "${args[*]}" =~ "--exec" ]]; then echo true; else echo false; fi
}
function isOptional() {
local args=("$@")
if [[ "${args[*]}" =~ "--optional" ]]; then echo true; else echo false; fi
}
function isSilent() {
local args=("$@")
if [[ "${args[*]}" =~ "--silent" ]]; then echo true; else echo false; fi
}
# shellcheck disable=SC2001,SC2155,SC2086
function dependency() {
local tool_name=$1
local tool_version_pattern=$2
local tool_fallback=${3:-"No details. Please google it."}
local tool_version_flag=${4:-"--version"}
local is_exec=$(isExec "$@")
local is_optional=$(isOptional "$@")
configDebugDependencies "$@" # refresh debug flags
# escape symbols: & / . { }, remove end of line, replace * by expectation from 1 to 4 digits
local tool_version=$(sed -e 's#[&\\/\.{}]#\\&#g; s#$#\\#' -e '$s#\\$##' -e 's#*#[0-9]\\{1,4\\}#g' <<<$tool_version_pattern)
# try to find tool
local WHICH_TOOL=$(command -v $tool_name)
if [ -z "$WHICH_TOOL" ]; then
printfDependencies "which : %s\npattern: %s, sed: \"s#.*\(%s\).*#\1#g\"\n-------\n" \
"${WHICH_TOOL:-"command -v $tool_name"}" "$tool_version_pattern" "$tool_version"
if $is_optional; then
echo "Optional [${cl_red}NO${cl_reset}]: \`$tool_name\` - ${cl_red}not found${cl_reset}! Try: ${cl_purple}$tool_fallback${cl_reset}"
return 0
else
echo "${cl_red}Error: dependency \`$tool_name\` not found."
echo "${cl_reset} Hint. To install tool use the command below: "
echo " $> $tool_fallback"
exit 1
fi
fi
local VERSION_MSG=$($tool_name $tool_version_flag 2>&1)
local VERSION_CLEAN=$(echo "'$VERSION_MSG'" | sed -n "s#.*\($tool_version\).*#\1#p")
printfDependencies "which : %s\nversion: %s\npattern: %s, sed: \"s#.*\(%s\).*#\1#g\"\nver. : %s\n-------\n" \
"$WHICH_TOOL" "$VERSION_MSG" "$tool_version_pattern" "$tool_version" "$VERSION_CLEAN"
if [ "$VERSION_CLEAN" == "" ]; then
if $is_optional; then
echo "Optional [${cl_red}NO${cl_reset}]: \`$tool_name\` - ${cl_red}wrong version${cl_reset}! Try: ${cl_purple}$tool_fallback${cl_reset}"
return 0
else
echo "${cl_red}Error: dependency version \`$tool_name\` is wrong."
echo " Extracted: \`$VERSION_CLEAN\`"
echo " Expected : \`$tool_version_pattern\`${cl_reset}"
if $is_exec; then
# shellcheck disable=SC2006
echo " Executing: ${cl_yellow}$($tool_fallback)${cl_reset}"
echo ""
eval $tool_fallback
else
echo ""
echo " Hint. To install tool use the command below: "
echo " $> $tool_fallback"
exit 1
fi
fi
else
if $is_optional; then echo -n "Optional "; else echo -n "Dependency "; fi
echo "[${cl_green}OK${cl_reset}]: \`$tool_name\` - version: $VERSION_CLEAN"
fi
}
function optional() {
local args=("$@")
# remove all flags from call
local del=("--debug" "--exec" "--silent" "--optional")
for value in "${del[@]}"; do
for i in "${!args[@]}"; do
if [[ ${args[i]} == "${value}" ]]; then unset 'args[i]'; fi
done
done
# inject default parameters
if [ "${#args[@]}" == "2" ]; then
args+=("No details. Please google it." "--version")
elif [ "${#args[@]}" == "3" ]; then
args+=("--version")
fi
# recover flags
if [ "$(isExec "$@")" == "true" ]; then args+=("--exec"); fi
if [ "$(isSilent "$@")" == "true" ]; then args+=("--silent"); fi
if [ "$(isDebug "$@")" == "true" ]; then args+=("--debug"); fi
args+=("--optional")
# we should expand any number of input arguments to required 4 + extra flags
dependency "${args[@]}"
}
# Tests:
#dependency bash "5.0.18(1)-release" "brew install bash" "--version"
#dependency bash "5.0.[0-9]{2}(1)-release" "brew install bash" "--version"
#dependency bash "5.0.*(1)-release" "brew install bash" "--version"
#dependency bash "5.*.*(1)-release" "brew install bash" "--version"
#dependency bash "5.*.*" "brew install bash" "--version" --debug # print debug info
#dependency bash "5.*.*" "brew install bash" "--version" 0 # ignore $5 parameter
#dependency git "2.*.*" "brew install git" "--version"
#dependency bazelisk "4.*.*" "brew install bazel" "--version"
#dependency yq "4.13.2" "brew install yq" "-V"
#dependency jq "1.6" "brew install jq"
#dependency bash "[45].*.*" "brew install bash" # allow 4.xx and 5.xx versions
#dependency go "1.17.*" "brew install go" "version"
#dependency buildozer "redacted" "go get github.com/bazelbuild/buildtools/buildozer" "-version" 1
#dependency buildozer "redacted" "go get github.com/bazelbuild/buildtools/buildozer"
#dependency go "1.17.*" "brew install go && (echo 'export GOPATH=\$HOME/go; export PATH=\$GOPATH/bin:\$PATH;' >> ~/.zshrc)" "version"
#dependency go "2.17.*" "echo 'export GOPATH=\$HOME/go; export PATH=\$GOPATH/bin:\$PATH;'" "version" --exec
#dependency go "2.17.*" "echo 'export GOPATH=\$HOME/go; export PATH=\$GOPATH/bin:\$PATH;' >> ~/.zshrc" "version" --debug
# ref:
# https://docs.gradle.org/current/userguide/single_versions.html
# https://github.com/qzb/sh-semver
# https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash
@OleksandrKucherenko
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment