diff --git a/lib/functions/installs.bash b/lib/functions/installs.bash index 036c1dab9..d24afcdc2 100644 --- a/lib/functions/installs.bash +++ b/lib/functions/installs.bash @@ -79,7 +79,6 @@ install_local_tool_versions() { local search_path search_path=$PWD - local some_tools_installed local some_plugin_not_installed local tool_versions_path @@ -102,6 +101,7 @@ install_local_tool_versions() { fi # Locate all the plugins defined in the versions file. + # This just checks the current directory local tools_file if [ -f "$tool_versions_path" ]; then tools_file=$(strip_tool_version_comments "$tool_versions_path" | cut -d ' ' -f 1) @@ -117,23 +117,14 @@ install_local_tool_versions() { exit 1 fi + local tools_installed if [ -n "$plugins_installed" ]; then - for plugin_name in $plugins_installed; do - local plugin_version_and_path - plugin_version_and_path="$(find_versions "$plugin_name" "$search_path")" - - if [ -n "$plugin_version_and_path" ]; then - local plugin_version - some_tools_installed='yes' - plugin_versions=$(cut -d '|' -f 1 <<<"$plugin_version_and_path") - for plugin_version in $plugin_versions; do - install_tool_version "$plugin_name" "$plugin_version" - done - fi - done + plugins_installed=$(printf "%s" "$plugins_installed" | tr "\n" " " | awk '{$1=$1};1') + display_debug "install_local_tool_versions: plugins_installed='$plugins_installed'" + tools_installed=$(install_directory_tools_recursive "$search_path" "$plugins_installed") fi - if [ -z "$some_tools_installed" ]; then + if [ -z "$tools_installed" ]; then printf "Either specify a tool & version in the command\n" printf "OR add .tool-versions file in this directory\n" printf "or in a parent directory\n" @@ -141,6 +132,174 @@ install_local_tool_versions() { fi } +install_directory_tools_recursive() { + local search_path=$1 + local plugins_installed=$2 + local tools_installed="" + + display_debug "install_directory_tools_recursive '$search_path': entered with plugins_installed='$plugins_installed'" + + while [ "$search_path" != "/" ]; do + # install tools from files in current directory + display_debug_hr + tools_installed=$(install_directory_tools "$search_path" "$plugins_installed" "$tools_installed") + display_debug "install_directory_tools_recursive '$search_path': install_directory_tools returned tools_installed='$tools_installed'" + + # terminate if all tools are installed + if [[ -n $(stringlist_a_subset_of_b "$plugins_installed" "$tools_installed") ]]; then + display_debug "install_directory_tools_recursive '$search_path': exiting because all known tools ($plugins_installed) are installed ($tools_installed)" + printf "%s\n" "$tools_installed" + return 0 + fi + + # go up a directory + search_path=$(dirname "$search_path") + done + + printf "%s\n" "$tools_installed" +} + +install_directory_tools() { + local search_path=$1 + local plugins_installed=$2 + local tools_installed=$3 + display_debug "install_directory_tools '$search_path': starting install. tools_installed='$tools_installed'" + + # install tools from .tool-versions + # install order is the order listed in .tool-versions + file_name=$(asdf_tool_versions_filename) + tools_installed=$(_install_directory_tools "$search_path" "$file_name" "$plugins_installed" "$tools_installed") + + # install tools from legacy version files + # install order is plugin order which is alphabetical + local legacy_config + legacy_config=$(get_asdf_config_value "legacy_version_file") + if [ "$legacy_config" = "yes" ]; then + tools_installed=$(_install_directory_tools_legacy "$search_path" "$plugins_installed" "$tools_installed") + fi + + printf "%s\n" "$tools_installed" +} + +_install_directory_tools() { + local search_path=$1 + local file_name=$2 + local plugins_installed=$3 + local tools_installed=$4 + + local tool_versions + if ! [[ -f "$search_path/$file_name" ]]; then + display_debug "_install_directory_tools '$search_path': exiting early... $file_name file not found" + printf "%s\n" "$tools_installed" + return 0 + fi + + tool_versions=$(strip_tool_version_comments "$search_path/$file_name" | awk '{$1=$1};1') + if [[ -z $tool_versions ]]; then + display_debug "_install_directory_tools '$search_path': exiting early... no tools found in directory" + printf "%s\n" "$tools_installed" + return 0 + fi + while IFS=' ' read -r tool_version; do + display_debug "_install_directory_tools '$search_path': found '$tool_version'" + + # read one version from the file + IFS=' ' read -ra parts <<<"$tool_version" + local plugin_name + plugin_name=${parts[0]} + + # skip if plugin is installed already + if [[ -n $(stringlist_contains "$tools_installed" "$plugin_name") ]]; then + display_debug "_install_directory_tools '$search_path': '$plugin_name' is already installed... skipping" + continue + fi + + # install the versions + local plugin_version + for plugin_version in "${parts[@]:1}"; do + # install the version + display_none "$(install_tool_version "$plugin_name" "$plugin_version")" + tools_installed=$(printf "%s %s" "$tools_installed" "$plugin_name" | awk '{$1=$1};1') + done + + # check if there is an environment override for it + # if so also install the environment version + local env_version + env_version=$(get_version_from_env "$plugin_name") + if [ -n "$env_version" ]; then + display_debug "_install_directory_tools '$search_path': $plugin_name: using environment override $env_version" + plugin_version=$env_version + + # install the version + display_none "$(install_tool_version "$plugin_name" "$plugin_version")" + tools_installed=$(printf "%s %s" "$tools_installed" "$plugin_name" | awk '{$1=$1};1') + fi + + display_debug "_install_directory_tools '$search_path': installed '$plugin_name':'$plugin_version' new state of tools_installed='$tools_installed'" + done <<<"$tool_versions" + + printf "%s\n" "$tools_installed" +} + +_install_directory_tools_legacy() { + local search_path=$1 + local plugins_installed=$2 + local tools_installed=$3 + + display_debug "_install_directory_tools_legacy '$search_path': resolving legacy files" + local plugin_name + for plugin_name in $plugins_installed; do + # skip if plugin is installed already + if [[ -n $(stringlist_contains "$tools_installed" "$plugin_name") ]]; then + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: skipping as tool was already installed" + continue + fi + + # extract plugin legacy information + local plugin_path + plugin_path=$(get_plugin_path "$plugin_name") + local legacy_list_filenames_script + legacy_list_filenames_script="${plugin_path}/bin/list-legacy-filenames" + + # skip if no legacy_list_filenames_script available + if ! [[ -f "$legacy_list_filenames_script" ]]; then + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: skipping as legacy files are not supported" + continue + fi + + # extract plugin legacy filenames + local legacy_filenames="" + legacy_filenames=$("$legacy_list_filenames_script") + + # lookup plugin version in current dir + local plugin_version + plugin_version=$(get_legacy_version_in_dir "$plugin_name" "$search_path" "$legacy_filenames") + + # check if there is an environment override for it + # if so take the environment version + local env_version + env_version=$(get_version_from_env "$plugin_name") + if [ -n "$env_version" ]; then + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: using environment override $env_version" + plugin_version=$env_version + fi + + # skip if version cannot be found + if [ -z "$plugin_version" ]; then + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: skipping as version cannot be found" + continue + fi + + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: plugin_version='$plugin_version'" + display_none "$(install_tool_version "$plugin_name" "$plugin_version")" + + tools_installed=$(printf "%s %s" "$tools_installed" "$plugin_name" | awk '{$1=$1};1') + display_debug "_install_directory_tools_legacy '$search_path': legacy_install $plugin_name: installed '$plugin_version' new state of tools_installed='$tools_installed'" + done + + printf "%s\n" "$tools_installed" +} + install_tool_version() { local plugin_name=$1 local full_version=$2 @@ -253,3 +412,47 @@ install_tool_version() { fi fi } + +get_legacy_version_in_dir() { + local plugin_name=$1 + local search_path=$2 + local legacy_filenames=$3 + + local asdf_version + for filename in $legacy_filenames; do + local legacy_version + legacy_version=$(parse_legacy_version_file "$search_path/$filename" "$plugin_name") + + if [ -n "$legacy_version" ]; then + printf "%s\n" "$legacy_version" + return 0 + fi + done +} + +stringlist_a_subset_of_b() { + local list_a=$1 + local list_b=$2 + local array_a + IFS=' ' read -r -a array_a <<<"$list_a" + for item_a in "${array_a[@]}"; do + if [[ -z $(stringlist_contains "$list_b" "$item_a") ]]; then + return 0 + fi + done + printf "true\n" + return 0 +} + +stringlist_contains() { + local list=$1 + local search=$2 + local array + IFS=' ' read -r -a array <<<"$list" + for item in "${array[@]}"; do + if [ "$item" = "$search" ]; then + printf "true\n" + return 0 + fi + done +} diff --git a/lib/utils.bash b/lib/utils.bash index 21978a929..ae3dc6e5f 100644 --- a/lib/utils.bash +++ b/lib/utils.bash @@ -156,6 +156,20 @@ display_error() { printf "%s\n" "$1" >&2 } +display_debug_hr() { + display_debug "--------------------------------------------------------------------------------------------------------------" +} + +display_debug() { + if [[ $DEBUG = "true" ]]; then + printf "debug: %s\n" "$1" >&2 + fi +} + +display_none() { + printf "" +} + get_version_in_dir() { local plugin_name=$1 local search_path=$2 diff --git a/scripts/lint.bash b/scripts/lint.bash index 6ebfe809c..8632eff1e 100755 --- a/scripts/lint.bash +++ b/scripts/lint.bash @@ -41,6 +41,7 @@ run_shfmt_stylecheck() { test/test_helpers.bash \ test/fixtures/dummy_broken_plugin/bin/* \ test/fixtures/dummy_legacy_plugin/bin/* \ + test/fixtures/dummy_dependent_plugin/bin/* \ test/fixtures/dummy_plugin/bin/* print.info "Checking .bats with shfmt" @@ -65,6 +66,7 @@ run_shellcheck_linter() { test/test_helpers.bash \ test/fixtures/dummy_broken_plugin/bin/* \ test/fixtures/dummy_legacy_plugin/bin/* \ + test/fixtures/dummy_dependent_plugin/bin/* \ test/fixtures/dummy_plugin/bin/* print.info "Checking .bats files with Shellcheck" diff --git a/test/fixtures/dummy_dependent_plugin/LICENSE b/test/fixtures/dummy_dependent_plugin/LICENSE new file mode 100644 index 000000000..d3084751c --- /dev/null +++ b/test/fixtures/dummy_dependent_plugin/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Akash Manohar J + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/fixtures/dummy_dependent_plugin/bin/install b/test/fixtures/dummy_dependent_plugin/bin/install new file mode 100755 index 000000000..b5b413d68 --- /dev/null +++ b/test/fixtures/dummy_dependent_plugin/bin/install @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# recurse 2 directories up to get to the +# installs directory +_ASDF_PATH=$(dirname "$ASDF_INSTALL_PATH") +_ASDF_PATH=$(dirname "$_ASDF_PATH") +DUMMY_EXPECTED_INSTALL="$_ASDF_PATH/dummy/1.2.0/version" + +mkdir -p "$ASDF_INSTALL_PATH" +if ! [[ -f "$DUMMY_EXPECTED_INSTALL" ]]; then + echo "Could not detect dependent plugin dummy. at path $DUMMY_EXPECTED_INSTALL" + exit 1 +fi +echo "Successfully detected depdendent plugin dummy." + +env >"$ASDF_INSTALL_PATH/env" +echo "$ASDF_INSTALL_VERSION" >"$ASDF_INSTALL_PATH/version" diff --git a/test/install_command.bats b/test/install_command.bats index 9e70d544b..67a8251f0 100644 --- a/test/install_command.bats +++ b/test/install_command.bats @@ -7,6 +7,7 @@ setup() { install_dummy_legacy_plugin install_dummy_plugin install_dummy_broken_plugin + install_dummy_dependent_plugin PROJECT_DIR="$HOME/project" mkdir -p "$PROJECT_DIR" @@ -36,6 +37,15 @@ teardown() { [ "$(cat "$ASDF_DIR/installs/dummy/1.2.0/version")" = "1.2.0" ] } +@test "install_command installs plugins in order" { + cd "$PROJECT_DIR" + printf 'dummy 1.2.0\ndummy-dependent 1.3.0' >".tool-versions" + run asdf install + [ "$status" -eq 0 ] + [ "$(cat "$ASDF_DIR/installs/dummy/1.2.0/version")" = "1.2.0" ] + [ "$(cat "$ASDF_DIR/installs/dummy-dependent/1.3.0/version")" = "1.3.0" ] +} + @test "install_command with only name installs the version in .tool-versions" { cd "$PROJECT_DIR" echo -n 'dummy 1.2.0' >".tool-versions" diff --git a/test/test_helpers.bash b/test/test_helpers.bash index 6f4468e1b..ad887eeb9 100644 --- a/test/test_helpers.bash +++ b/test/test_helpers.bash @@ -48,6 +48,12 @@ install_mock_broken_plugin() { cp -r "$BATS_TEST_DIRNAME/fixtures/dummy_broken_plugin" "$location/plugins/$plugin_name" } +install_mock_dependent_plugin() { + local plugin_name=$1 + local location="${2:-$ASDF_DIR}" + cp -r "$BATS_TEST_DIRNAME/fixtures/dummy_dependent_plugin" "$location/plugins/$plugin_name" +} + install_mock_plugin_repo() { local plugin_name=$1 local location="${BASE_DIR}/repo-${plugin_name}" @@ -82,6 +88,10 @@ install_dummy_broken_plugin() { install_mock_broken_plugin "dummy-broken" } +install_dummy_dependent_plugin() { + install_mock_dependent_plugin "dummy-dependent" +} + install_dummy_version() { install_mock_plugin_version "dummy" "$1" }