2017-08-28 17:41:32 +02:00
|
|
|
#!/usr/bin/env bash
|
2018-09-11 16:48:32 +02:00
|
|
|
## source: https://github.com/kdabir/has
|
|
|
|
|
2019-07-24 14:26:00 +02:00
|
|
|
## To add more tools, search for "__detect" function in this file
|
|
|
|
|
2017-08-28 17:41:32 +02:00
|
|
|
## Important so that version is not extracted for failed commands (not found)
|
|
|
|
set -o pipefail
|
|
|
|
|
2019-04-23 08:38:38 +02:00
|
|
|
readonly BINARY_NAME="has"
|
2020-06-20 16:46:48 +02:00
|
|
|
readonly VERSION="v1.5.0"
|
2019-04-23 08:38:38 +02:00
|
|
|
|
2019-07-24 14:26:00 +02:00
|
|
|
## constants - symbols for success failure
|
Verify version for each command (#43)
Thanks to contributions by @sdolenc
* docker files
* list tools for testing
* test packages
* github actions
* documentation
* ensure expected command count
* output count of individual tools tested
* test: eb rg podman subl brunch grunt gulp heroku netlify sls java brew gor, heroku, mvn, netlify, netlifyctl, php5, gcloud, hub, coreutils, brunch, code, kotlin, sbt, go, ant, gem, rake, autojump, groovy, aws, perl6, sbt, scala, lein
2020-04-25 13:18:24 +02:00
|
|
|
if [[ -z $TERM ]]; then
|
2020-08-13 07:01:12 +02:00
|
|
|
TERM="xterm"
|
Verify version for each command (#43)
Thanks to contributions by @sdolenc
* docker files
* list tools for testing
* test packages
* github actions
* documentation
* ensure expected command count
* output count of individual tools tested
* test: eb rg podman subl brunch grunt gulp heroku netlify sls java brew gor, heroku, mvn, netlify, netlifyctl, php5, gcloud, hub, coreutils, brunch, code, kotlin, sbt, go, ant, gem, rake, autojump, groovy, aws, perl6, sbt, scala, lein
2020-04-25 13:18:24 +02:00
|
|
|
fi
|
|
|
|
readonly txtreset="$(tput -T $TERM sgr0)"
|
|
|
|
readonly txtbold="$(tput -T $TERM bold)"
|
|
|
|
readonly txtblack="$(tput -T $TERM setaf 0)"
|
|
|
|
readonly txtred="$(tput -T $TERM setaf 1)"
|
|
|
|
readonly txtgreen="$(tput -T $TERM setaf 2)"
|
|
|
|
readonly txtyellow="$(tput -T $TERM setaf 3)"
|
|
|
|
readonly txtblue="$(tput -T $TERM setaf 4)"
|
|
|
|
readonly txtpurple="$(tput -T $TERM setaf 5)"
|
|
|
|
readonly txtcyan="$(tput -T $TERM setaf 6)"
|
|
|
|
readonly txtwhite="$(tput -T $TERM setaf 7)"
|
2019-04-23 08:38:38 +02:00
|
|
|
# unicode "✗"
|
2019-05-21 06:24:41 +02:00
|
|
|
readonly fancyx='\342\234\227'
|
2019-04-23 08:38:38 +02:00
|
|
|
# unicode "✓"
|
2019-05-21 06:24:41 +02:00
|
|
|
readonly checkmark='\342\234\223'
|
2019-04-23 08:38:38 +02:00
|
|
|
# PASS="\e[1m\e[38;5;2m✔\e[m"
|
|
|
|
# FAIL="\e[1m\e[38;5;1m✘\e[m"
|
|
|
|
readonly PASS="${txtbold}${txtgreen}${checkmark}${txtreset}"
|
|
|
|
readonly FAIL="${txtbold}${txtred}${fancyx}${txtreset}"
|
2017-08-28 17:41:32 +02:00
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
## These variables are used to keep track of passed and failed commands
|
2017-08-29 05:44:08 +02:00
|
|
|
OK=0
|
|
|
|
KO=0
|
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
## Regex to extract simple version - extracts numeric sem-ver style versions
|
2017-08-28 17:41:32 +02:00
|
|
|
REGEX_SIMPLE_VERSION="([[:digit:]]+\.?){2,3}"
|
|
|
|
|
2019-07-24 14:26:00 +02:00
|
|
|
## name of RC file that can contain list of tools to be checked
|
2018-11-16 11:31:07 +01:00
|
|
|
RC_FILE=".hasrc"
|
2017-10-14 08:47:01 +02:00
|
|
|
|
2019-04-23 08:38:38 +02:00
|
|
|
# try to extract version by executing "${1}" with "${2}" arg
|
2019-07-24 14:26:00 +02:00
|
|
|
# command name to be executed is dynamically passed to this function as ${1}
|
|
|
|
# arg to ${1} is passed in ${2}
|
2017-08-28 17:41:32 +02:00
|
|
|
__dynamic_detect(){
|
2019-04-23 08:38:38 +02:00
|
|
|
cmd="${1}"
|
|
|
|
params="${2}"
|
2019-05-21 06:24:41 +02:00
|
|
|
version=$( eval "${cmd}" "${params}" "2>&1" | grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
2017-08-28 17:41:32 +02:00
|
|
|
status=$?
|
|
|
|
}
|
|
|
|
|
|
|
|
# commands that use `--version` flag
|
|
|
|
__dynamic_detect--version(){
|
2019-04-23 08:38:38 +02:00
|
|
|
__dynamic_detect "${1}" "--version"
|
2017-08-28 17:41:32 +02:00
|
|
|
}
|
|
|
|
|
2018-11-16 11:31:07 +01:00
|
|
|
# commands that use `-version` flag
|
2017-08-28 17:41:32 +02:00
|
|
|
__dynamic_detect-version(){
|
2019-04-23 08:38:38 +02:00
|
|
|
__dynamic_detect "${1}" "-version"
|
2017-08-28 17:41:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# commands that use `-v` flag
|
|
|
|
__dynamic_detect-v(){
|
2019-04-23 08:38:38 +02:00
|
|
|
__dynamic_detect "${1}" "-v"
|
2017-08-28 17:41:32 +02:00
|
|
|
}
|
|
|
|
|
2019-05-21 06:24:41 +02:00
|
|
|
# commands that use `-V` flag
|
|
|
|
__dynamic_detect-V(){
|
|
|
|
__dynamic_detect "${1}" "-V"
|
|
|
|
}
|
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
# commands that use `version` argument
|
|
|
|
__dynamic_detect-arg_version(){
|
2019-04-23 08:38:38 +02:00
|
|
|
__dynamic_detect "${1}" "version"
|
2017-10-14 08:47:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
## the main function
|
2017-08-28 17:41:32 +02:00
|
|
|
__detect(){
|
2019-04-23 08:38:38 +02:00
|
|
|
name="${1}"
|
2017-08-28 17:41:32 +02:00
|
|
|
|
2019-05-21 06:24:41 +02:00
|
|
|
# setup aliases - maps commonly used name to exact command name
|
2017-08-28 17:41:32 +02:00
|
|
|
case ${name} in
|
2019-05-21 06:24:41 +02:00
|
|
|
golang ) command="go" ;;
|
|
|
|
jre ) command="java" ;;
|
|
|
|
jdk ) command="javac" ;;
|
|
|
|
nodejs ) command="node" ;;
|
|
|
|
goreplay ) command="gor" ;;
|
|
|
|
httpie ) command="http" ;;
|
|
|
|
homebrew ) command="brew" ;;
|
|
|
|
awsebcli ) command="eb" ;;
|
|
|
|
awscli ) command="aws" ;;
|
2019-07-24 14:19:48 +02:00
|
|
|
postgresql ) command="psql" ;;
|
2019-05-21 06:24:41 +02:00
|
|
|
*coreutils|linux*utils) command="gnu_coreutils" ;;
|
|
|
|
* ) command=${name} ;;
|
2017-08-28 17:41:32 +02:00
|
|
|
esac
|
|
|
|
|
2019-04-23 08:38:38 +02:00
|
|
|
case "${command}" in
|
2017-08-29 08:31:11 +02:00
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
# commands that need --version flag
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## Shells
|
2019-04-23 08:38:38 +02:00
|
|
|
bash|zsh) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## VCS
|
2019-04-23 08:38:38 +02:00
|
|
|
git|hg|svn|bzr) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## Http
|
2019-04-23 08:38:38 +02:00
|
|
|
curl|wget|http) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## Editors
|
|
|
|
vim|emacs|nano) __dynamic_detect--version "${command}" ;;
|
|
|
|
subl|code) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
|
|
|
## File system search and navigation
|
|
|
|
jq) __dynamic_detect--version "${command}" ;;
|
|
|
|
ag|ack|rg) __dynamic_detect--version "${command}" ;;
|
|
|
|
tree|autojump) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
2020-08-31 05:50:38 +02:00
|
|
|
## Package managers
|
2019-05-21 06:24:41 +02:00
|
|
|
apt|apt-get|aptitude) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
brew) __dynamic_detect--version "${command}" ;;
|
2020-08-31 05:50:38 +02:00
|
|
|
composer) __dynamic_detect-V "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## System tools
|
2019-05-21 06:24:41 +02:00
|
|
|
sed|awk|grep|file|sudo) __dynamic_detect--version "${command}" ;;
|
|
|
|
gzip|xz|unar|bzip2) __dynamic_detect--version "${command}" ;;
|
|
|
|
tar|pv) __dynamic_detect--version "${command}" ;;
|
2019-04-23 08:38:38 +02:00
|
|
|
|
2020-03-18 07:19:15 +01:00
|
|
|
# Container runtimes
|
|
|
|
docker|podman) __dynamic_detect--version "${command}" ;;
|
2020-08-31 05:50:38 +02:00
|
|
|
|
2019-07-24 14:19:48 +02:00
|
|
|
## Database CLI
|
|
|
|
psql) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
|
|
|
|
## Scripting Language / runtime
|
|
|
|
ruby|R|python|python3) __dynamic_detect--version "${command}" ;;
|
2019-04-23 08:38:38 +02:00
|
|
|
perl|perl6|php|php5) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
groovy|node) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
|
|
|
## Compile
|
|
|
|
gcc|make|bats) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
|
|
|
## Build tools
|
|
|
|
lein|gradle|mvn) __dynamic_detect--version "${command}" ;;
|
|
|
|
grunt|brunch) __dynamic_detect--version "${command}" ;;
|
|
|
|
gem|rake|bundle) __dynamic_detect--version "${command}" ;;
|
|
|
|
npm|yarn) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
|
|
|
## Cloud Tools
|
2019-04-23 08:38:38 +02:00
|
|
|
aws|eb|sls|gcloud) __dynamic_detect--version "${command}" ;;
|
2019-07-24 09:04:13 +02:00
|
|
|
heroku) __dynamic_detect--version "${command}" ;;
|
|
|
|
netlify) __dynamic_detect--version "${command}" ;;
|
|
|
|
netlifyctl) __dynamic_detect-arg_version "${command}" ;;
|
|
|
|
|
2020-08-31 05:50:54 +02:00
|
|
|
## GPG Tools
|
|
|
|
gpg|gpgconf|gpg-agent|gpg-connect-agent) __dynamic_detect--version "${command}" ;;
|
|
|
|
gpgsm) __dynamic_detect--version "${command}" ;;
|
|
|
|
|
2020-07-16 21:31:20 +02:00
|
|
|
## Hashicorp Tools
|
|
|
|
terraform|packer) __dynamic_detect--version "${command}" ;;
|
|
|
|
vagrant|consul) __dynamic_detect--version "${command}" ;;
|
|
|
|
nomad) __dynamic_detect--version "${command}" ;;
|
2017-08-29 08:31:11 +02:00
|
|
|
|
2019-05-21 06:24:41 +02:00
|
|
|
# commands that need -v flag
|
|
|
|
unzip) __dynamic_detect-v "${command}" ;;
|
|
|
|
|
|
|
|
# commands that need -V flag
|
|
|
|
ab) __dynamic_detect-V "${command}" ;;
|
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
# commands that need -version flag
|
2019-07-24 09:04:13 +02:00
|
|
|
ant) __dynamic_detect-version "${command}" ;;
|
|
|
|
java|javac|scala|kotlin) __dynamic_detect-version "${command}" ;;
|
2017-08-29 08:31:11 +02:00
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
# commands that need version arg
|
2019-05-21 06:24:41 +02:00
|
|
|
go|hugo) __dynamic_detect-arg_version "${command}" ;;
|
2017-08-29 08:31:11 +02:00
|
|
|
|
|
|
|
## Example of commands that need custom processing
|
2017-08-28 17:41:32 +02:00
|
|
|
|
2017-10-14 09:26:16 +02:00
|
|
|
gulp)
|
2020-08-31 05:50:54 +02:00
|
|
|
version=$( gulp --version 2>&1| grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
2017-08-28 17:41:32 +02:00
|
|
|
status=$?
|
|
|
|
;;
|
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
## gor returns version but does not return normal status code, hence needs custom processing
|
2017-08-28 17:41:32 +02:00
|
|
|
gor)
|
2019-05-21 06:24:41 +02:00
|
|
|
version=$( gor version 2>&1 | grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
2019-10-08 05:30:12 +02:00
|
|
|
if [ $? -eq 1 ]; then status=0; else status=127; fi
|
2017-08-28 17:41:32 +02:00
|
|
|
;;
|
|
|
|
|
2017-10-14 12:12:54 +02:00
|
|
|
sbt)
|
2019-05-21 06:24:41 +02:00
|
|
|
version=$( sbt about 2>&1 | grep -Eo "([[:digit:]]{1,4}\.){2}[[:digit:]]{1,4}" | head -1)
|
|
|
|
status=$?
|
|
|
|
;;
|
|
|
|
|
|
|
|
## use 'readlink' to test for GNU coreutils
|
|
|
|
# readlink (GNU coreutils) 8.28
|
|
|
|
gnu_coreutils) __dynamic_detect--version readlink ;;
|
|
|
|
|
|
|
|
## hub uses --version but version string is on second line, or third if HUB_VERBOSE set
|
|
|
|
hub)
|
|
|
|
version=$( HUB_VERBOSE='' hub --version 2>&1 | sed -n 2p | grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
|
|
|
status=$?
|
|
|
|
;;
|
|
|
|
|
|
|
|
## zip uses -v but version string is on second line
|
|
|
|
zip)
|
|
|
|
version=$( zip -v 2>&1 | sed -n 2p | grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
2019-04-23 08:38:38 +02:00
|
|
|
status=$?
|
2017-10-14 12:12:54 +02:00
|
|
|
;;
|
|
|
|
|
2017-12-27 13:16:01 +01:00
|
|
|
has)
|
2019-05-21 06:24:41 +02:00
|
|
|
version=$( has 2>&1 | grep -Eo "${REGEX_SIMPLE_VERSION}" | head -1)
|
2017-12-27 13:16:01 +01:00
|
|
|
status=$?
|
|
|
|
;;
|
|
|
|
|
2017-08-28 17:41:32 +02:00
|
|
|
*)
|
2017-10-14 08:47:01 +02:00
|
|
|
## Can allow dynamic checking here, i.e. checking commands that are not listed above
|
2017-08-28 17:41:32 +02:00
|
|
|
if [[ "${HAS_ALLOW_UNSAFE}" == "y" ]]; then
|
2019-04-23 08:38:38 +02:00
|
|
|
__dynamic_detect--version "${command}"
|
2017-10-14 08:47:01 +02:00
|
|
|
## fallback checking based on status!=127 (127 means command not found)
|
|
|
|
## TODO can check other type of supported version-checks if status was not 127
|
2017-08-28 17:41:32 +02:00
|
|
|
else
|
2017-10-14 08:47:01 +02:00
|
|
|
## -1 is special way to tell command is not supported/whitelisted by `has`
|
|
|
|
status="-1"
|
2017-08-28 17:41:32 +02:00
|
|
|
fi
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
|
2017-10-14 08:47:01 +02:00
|
|
|
if [ "$status" -eq "-1" ]; then ## When unsafe processing is not allowed, the -1 signifies
|
2020-06-20 16:46:48 +02:00
|
|
|
printf '%b %s not understood\n' "${FAIL}" "${command}" > "$OUTPUT"
|
2019-05-21 06:24:41 +02:00
|
|
|
KO=$(( KO+1 ))
|
2017-10-14 08:47:01 +02:00
|
|
|
|
|
|
|
elif [ ${status} -eq 127 ]; then ## command not installed
|
2020-06-20 16:46:48 +02:00
|
|
|
printf '%b %s\n' "${FAIL}" "${command}" > "$OUTPUT"
|
2019-05-21 06:24:41 +02:00
|
|
|
KO=$(( KO+1 ))
|
2017-10-14 08:47:01 +02:00
|
|
|
|
2017-10-14 09:53:50 +02:00
|
|
|
elif [ ${status} -eq 0 ] || [ ${status} -eq 141 ]; then ## successfully executed
|
2020-06-20 16:46:48 +02:00
|
|
|
printf "%b %s %b\n" "${PASS}" "${command}" "${txtbold}${txtyellow}${version}${txtreset}" > "$OUTPUT"
|
2019-05-21 06:24:41 +02:00
|
|
|
OK=$(( OK+1 ))
|
2017-10-14 08:47:01 +02:00
|
|
|
|
|
|
|
else ## as long as its not 127, command is there, but we might not have been able to extract version
|
2020-06-20 16:46:48 +02:00
|
|
|
printf '%b %s\n' "${PASS}" "${command}" > "$OUTPUT"
|
2019-05-21 06:24:41 +02:00
|
|
|
OK=$(( OK+1 ))
|
2017-08-28 17:41:32 +02:00
|
|
|
fi
|
2019-04-23 08:38:38 +02:00
|
|
|
} #end __detect
|
2018-11-16 11:31:07 +01:00
|
|
|
|
2020-06-20 16:46:48 +02:00
|
|
|
OPTIND=1
|
|
|
|
OUTPUT=/dev/stdout
|
|
|
|
|
|
|
|
while getopts "q" opt; do
|
|
|
|
case "$opt" in
|
|
|
|
q) QUIET="true"
|
|
|
|
OUTPUT=/dev/null
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
shift $((OPTIND-1))
|
|
|
|
|
2018-11-16 11:31:07 +01:00
|
|
|
if [ -s "${RC_FILE}" ]; then
|
|
|
|
HASRC="true"
|
|
|
|
fi
|
|
|
|
|
2018-11-17 06:35:14 +01:00
|
|
|
# if HASRC is not set AND no arguments passed to script
|
|
|
|
if [[ -z "${HASRC}" ]] && [ "$#" -eq 0 ]; then
|
2017-08-28 17:41:32 +02:00
|
|
|
# print help
|
2020-06-20 16:46:48 +02:00
|
|
|
printf '%s %s\n' "${BINARY_NAME}" "${VERSION}" > "$OUTPUT"
|
|
|
|
printf 'USAGE:\t %s [-q] <command-names>..\n' "${BINARY_NAME}" > "$OUTPUT"
|
|
|
|
printf 'EXAMPLE: %s git curl node\n' "${BINARY_NAME}" > "$OUTPUT"
|
2017-08-28 17:41:32 +02:00
|
|
|
|
|
|
|
else
|
2018-11-16 11:31:07 +01:00
|
|
|
# for each cli-arg
|
2017-08-28 17:41:32 +02:00
|
|
|
for cmd in "$@"; do
|
2019-04-23 08:38:38 +02:00
|
|
|
__detect "${cmd}"
|
2017-08-28 17:41:32 +02:00
|
|
|
done
|
|
|
|
|
2018-11-16 07:42:37 +01:00
|
|
|
## display found / total
|
2018-11-16 03:27:56 +01:00
|
|
|
# echo ${OK} / $(($OK+$KO))
|
2018-11-16 07:42:37 +01:00
|
|
|
|
2018-11-17 06:35:14 +01:00
|
|
|
## if HASRC has been set
|
|
|
|
if [[ -n "${HASRC}" ]]; then
|
2018-11-16 11:31:07 +01:00
|
|
|
## for all
|
2019-04-23 08:38:38 +02:00
|
|
|
while read -r line; do
|
|
|
|
__detect "${line}"
|
2019-05-21 06:24:41 +02:00
|
|
|
done <<<"$( grep -Ev "^\s*(#|$)" "${RC_FILE}" )"
|
2018-11-16 11:31:07 +01:00
|
|
|
fi
|
|
|
|
|
2018-11-16 07:42:37 +01:00
|
|
|
## max status code that can be returned
|
|
|
|
MAX_STATUS_CODE=126
|
|
|
|
|
2019-05-21 06:24:41 +02:00
|
|
|
if [[ "${KO}" -gt "${MAX_STATUS_CODE}" ]]; then
|
2018-11-16 07:42:37 +01:00
|
|
|
exit "${MAX_STATUS_CODE}"
|
2018-11-16 03:27:56 +01:00
|
|
|
else
|
|
|
|
exit "${KO}"
|
|
|
|
fi
|
2018-11-15 06:09:17 +01:00
|
|
|
|
2018-11-16 03:27:56 +01:00
|
|
|
fi
|