diff --git a/lib/helpers.sh b/lib/helpers.sh index c1eabcb..62d6697 100755 --- a/lib/helpers.sh +++ b/lib/helpers.sh @@ -183,23 +183,37 @@ function error_and_proceed() { }; export -f error_and_proceed; -function check_dependencies() { - if [[ "$(uname)" == 'Darwin' ]]; then - # Prefer ggrep (typically from Homebrew) if available - if command -v ggrep >/dev/null 2>&1; then - shopt -s expand_aliases; - alias grep=ggrep; - return; - fi; +# Probes the two grep behaviors tfenv depends on beyond POSIX: +# `\+` repetition in a BRE, per the 'latest' regex in lib/tfenv-version-name.sh +# `-o` reporting every match on a line, per libexec/tfenv-list-remote +function grep_is_sufficient() { + local grep_bin="${1}"; + local bre_result; + local multi_match_result; - # Fall back to checking if the system grep is GNU grep - # (e.g. installed via nix or manually) - if grep --version 2>&1 | grep -q 'GNU grep'; then - return; - fi; + bre_result="$("${grep_bin}" -e '^[0-9]\+$' <<< '42' 2>/dev/null)"; + [ "${bre_result}" = '42' ] || return 1; + + multi_match_result="$("${grep_bin}" -o -E '[0-9]+' <<< '1 2' 2>/dev/null | tr '\n' ' ')"; + [ "${multi_match_result}" = '1 2 ' ] || return 1; + + return 0; +}; +export -f grep_is_sufficient; - log 'error' 'GNU Grep is a requirement and your Mac does not have it. Consider "brew install grep" or "nix profile install nixpkgs#gnugrep"'; +function check_dependencies() { + # ggrep, typically from Homebrew, takes precedence when it is capable + if command -v ggrep >/dev/null 2>&1 && grep_is_sufficient ggrep; then + shopt -s expand_aliases; + alias grep=ggrep; + return; + fi; + + if grep_is_sufficient grep; then + return; fi; + + log 'error' 'The grep on PATH does not support `\+` in basic expressions or `-o` multi-match, both of which tfenv requires. Consider "brew install grep" or "nix profile install nixpkgs#gnugrep"'; }; export -f check_dependencies; diff --git a/test/test_dependencies.sh b/test/test_dependencies.sh new file mode 100755 index 0000000..ae261ee --- /dev/null +++ b/test/test_dependencies.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# Source common test setup +source "$(dirname "${0}")/test_common.sh"; + +##################### +# Begin Script Body # +##################### + +declare -a errors=(); + +log 'info' '### Test Suite: dependencies'; + +log 'info' '## grep_is_sufficient: accepts the grep already on PATH'; +( + grep_is_sufficient grep || exit 1; + exit 0; +) && log 'info' '## grep_is_sufficient: system grep accepted' \ + || error_and_proceed 'system grep rejected; tfenv needs only `\+` in a BRE and `-o` multi-match'; + +log 'info' '## grep_is_sufficient: rejects a grep whose -o returns only the first match'; +( + # Satisfies the BRE probe but truncates -o output, so only the multi-match check rejects it + declare stub_dir; + stub_dir="$(mktemp -d)" || exit 1; + trap 'rm -rf "${stub_dir}"' EXIT; + cat > "${stub_dir}/grep" <<'STUB'; +#!/usr/bin/env bash +if [ "${1}" = '-o' ]; then + /usr/bin/grep "${@}" | head -n 1; +else + /usr/bin/grep "${@}"; +fi; +STUB + chmod +x "${stub_dir}/grep" || exit 1; + grep_is_sufficient "${stub_dir}/grep" && exit 1; + exit 0; +) && log 'info' '## grep_is_sufficient: truncating grep rejected' \ + || error_and_proceed 'grep_is_sufficient accepted a grep whose -o returns only the first match'; + +log 'info' '## grep_is_sufficient: rejects a grep that cannot be run'; +( + grep_is_sufficient /nonexistent/grep && exit 1; + exit 0; +) && log 'info' '## grep_is_sufficient: missing binary rejected' \ + || error_and_proceed 'grep_is_sufficient accepted a grep binary that does not exist'; + +log 'info' '## check_dependencies: succeeds with a capable grep on PATH'; +( + check_dependencies || exit 1; + exit 0; +) && log 'info' '## check_dependencies: passed' \ + || error_and_proceed 'check_dependencies failed despite a capable grep being on PATH'; + +finish_tests 'dependencies'; + +exit 0;