From 27f7ef78529649534b8383daa58e4b370b3cbd7f Mon Sep 17 00:00:00 2001 From: Trevor Brown Date: Mon, 25 Apr 2022 08:45:19 -0400 Subject: [PATCH] fix: don't invoke asdf inside asdf commands (#1208) * fix: don't invoke asdf inside asdf commands Recursive calls have a number of disadvantages: * Poorer performance since each invocation spawns and new process and re-executes all the code in bin/asdf * Makes debugging more difficult * More likely to introduce subtle bugs and the possibility for infinite loops --- lib/commands/command-current.bash | 5 +- .../command-export-shell-version.bash | 4 +- lib/commands/command-help.bash | 4 +- lib/commands/command-info.bash | 4 +- lib/commands/command-install.bash | 246 +----------------- lib/commands/command-latest.bash | 86 +----- lib/commands/command-list-all.bash | 49 +--- lib/commands/command-local.bash | 26 -- lib/commands/command-plugin-add.bash | 57 +--- lib/commands/command-plugin-list.bash | 52 +--- lib/commands/command-plugin-test.bash | 25 +- lib/commands/command-plugin-update.bash | 65 +---- lib/commands/command-reshim.bash | 137 ---------- lib/commands/reshim.bash | 137 ++++++++++ lib/commands/version_commands.bash | 68 +---- lib/functions/installs.bash | 239 +++++++++++++++++ lib/functions/plugins.bash | 167 ++++++++++++ lib/functions/versions.bash | 222 ++++++++++++++++ 18 files changed, 813 insertions(+), 780 deletions(-) create mode 100644 lib/functions/installs.bash create mode 100644 lib/functions/plugins.bash create mode 100644 lib/functions/versions.bash diff --git a/lib/commands/command-current.bash b/lib/commands/command-current.bash index 2200afea..e15bf6e4 100644 --- a/lib/commands/command-current.bash +++ b/lib/commands/command-current.bash @@ -1,4 +1,6 @@ # -*- sh -*- +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" # shellcheck disable=SC2059 plugin_current_command() { @@ -48,7 +50,8 @@ current_command() { # printf "$terminal_format" "PLUGIN" "VERSION" "SET BY CONFIG" # disbale this until we release headings across the board if [ $# -eq 0 ]; then - for plugin in $(asdf plugin list); do + # shellcheck disable=SC2119 + for plugin in $(plugin_list_command); do plugin_current_command "$plugin" "$terminal_format" done else diff --git a/lib/commands/command-export-shell-version.bash b/lib/commands/command-export-shell-version.bash index 3403176b..a398a005 100644 --- a/lib/commands/command-export-shell-version.bash +++ b/lib/commands/command-export-shell-version.bash @@ -1,4 +1,6 @@ # -*- sh -*- +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" # Output from this command must be executable shell code shell_command() { @@ -36,7 +38,7 @@ shell_command() { exit 0 fi if [ "$version" = "latest" ]; then - version=$(asdf latest "$plugin") + version=$(latest_command "$plugin") fi if ! (check_if_version_exists "$plugin" "$version"); then version_not_installed_text "$plugin" "$version" 1>&2 diff --git a/lib/commands/command-help.bash b/lib/commands/command-help.bash index 68b2fbff..8eb4354e 100644 --- a/lib/commands/command-help.bash +++ b/lib/commands/command-help.bash @@ -1,4 +1,6 @@ # -*- sh -*- +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" asdf_help() { printf "version: %s\\n\\n" "$(asdf_version)" @@ -53,7 +55,7 @@ help_command() { if [ "${version_info[0]}" = "latest" ]; then local version - version=$(asdf latest "$plugin_name" "${version_info[1]}") + version=$(latest_command "$plugin_name" "${version_info[1]}") else local version="${version_info[0]}" fi diff --git a/lib/commands/command-info.bash b/lib/commands/command-info.bash index 2c6c3fc8..2ab6ffbd 100644 --- a/lib/commands/command-info.bash +++ b/lib/commands/command-info.bash @@ -1,11 +1,13 @@ # -*- sh -*- +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" info_command() { printf "%s:\\n%s\\n\\n" "OS" "$(uname -a)" printf "%s:\\n%s\\n\\n" "SHELL" "$($SHELL --version)" printf "%s:\\n%s\\n\\n" "ASDF VERSION" "$(asdf_version)" printf "%s:\\n%s\\n\\n" "ASDF ENVIRONMENT VARIABLES" "$(env | grep -E "ASDF_DIR|ASDF_DATA_DIR|ASDF_CONFIG_FILE|ASDF_DEFAULT_TOOL_VERSIONS_FILENAME")" - printf "%s:\\n%s\\n\\n" "ASDF INSTALLED PLUGINS" "$(asdf plugin list --urls --refs)" + printf "%s:\\n%s\\n\\n" "ASDF INSTALLED PLUGINS" "$(plugin_list_command --urls --refs)" } info_command "$@" diff --git a/lib/commands/command-install.bash b/lib/commands/command-install.bash index 0bdd007f..44c4d3fc 100644 --- a/lib/commands/command-install.bash +++ b/lib/commands/command-install.bash @@ -1,243 +1,9 @@ # -*- sh -*- - -handle_failure() { - local install_path="$1" - rm -rf "$install_path" - exit 1 -} - -handle_cancel() { - local install_path="$1" - printf "\\nreceived sigint, cleaning up" - handle_failure "$install_path" -} - -install_command() { - local plugin_name=$1 - local full_version=$2 - local extra_args="${*:3}" - - if [ "$plugin_name" = "" ] && [ "$full_version" = "" ]; then - install_local_tool_versions "$extra_args" - elif [[ $# -eq 1 ]]; then - install_one_local_tool "$plugin_name" - else - install_tool_version "$plugin_name" "$full_version" "$extra_args" - fi -} - -get_concurrency() { - if command -v nproc >/dev/null 2>&1; then - nproc - elif command -v sysctl >/dev/null 2>&1 && sysctl hw.ncpu >/dev/null 2>&1; then - sysctl -n hw.ncpu - elif [ -f /proc/cpuinfo ]; then - grep -c processor /proc/cpuinfo - else - printf "1\\n" - fi -} - -install_one_local_tool() { - local plugin_name=$1 - local search_path - search_path=$(pwd) - - local plugin_versions - - local plugin_version - - 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 - else - printf "No versions specified for %s in config files or environment\\n" "$plugin_name" - exit 1 - fi -} - -install_local_tool_versions() { - local plugins_path - plugins_path=$(get_plugin_path) - - local search_path - search_path=$(pwd) - - local some_tools_installed - local some_plugin_not_installed - - local tool_versions_path - tool_versions_path=$(find_tool_versions) - - # Locate all the plugins installed in the system - local plugins_installed - if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then - for plugin_path in "$plugins_path"/*; do - local plugin_name - plugin_name=$(basename "$plugin_path") - plugins_installed="$plugins_installed $plugin_name" - done - plugins_installed=$(printf "%s" "$plugins_installed" | tr " " "\n") - fi - - if [ -z "$plugins_installed" ]; then - printf "Install plugins first to be able to install tools\\n" - exit 1 - fi - - # Locate all the plugins defined in the versions file. - local tools_file - if [ -f "$tool_versions_path" ]; then - tools_file=$(strip_tool_version_comments "$tool_versions_path" | cut -d ' ' -f 1) - for plugin_name in $tools_file; do - if ! printf '%s\n' "${plugins_installed[@]}" | grep -q "^$plugin_name\$"; then - printf "%s plugin is not installed\n" "$plugin_name" - some_plugin_not_installed='yes' - fi - done - fi - - if [ -n "$some_plugin_not_installed" ]; then - exit 1 - fi - - 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 - fi - - if [ -z "$some_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" - exit 1 - fi -} - -install_tool_version() { - local plugin_name=$1 - local full_version=$2 - local flags=$3 - local keep_download - local plugin_path - - plugin_path=$(get_plugin_path "$plugin_name") - check_if_plugin_exists "$plugin_name" - - for flag in $flags; do - case "$flag" in - "--keep-download") - keep_download=true - shift - ;; - *) - shift - ;; - esac - done - - if [ "$full_version" = "system" ]; then - return - fi - - IFS=':' read -r -a version_info <<<"$full_version" - if [ "${version_info[0]}" = "ref" ]; then - local install_type="${version_info[0]}" - local version="${version_info[1]}" - else - local install_type="version" - - if [ "${version_info[0]}" = "latest" ]; then - local version - version=$(asdf latest "$plugin_name" "${version_info[1]}") - full_version=$version - else - local version="${version_info[0]}" - fi - fi - - local install_path - install_path=$(get_install_path "$plugin_name" "$install_type" "$version") - local download_path - download_path=$(get_download_path "$plugin_name" "$install_type" "$version") - local concurrency - concurrency=$(get_concurrency) - trap 'handle_cancel $install_path' INT - - if [ -d "$install_path" ]; then - printf "%s %s is already installed\\n" "$plugin_name" "$full_version" - else - - if [ -f "${plugin_path}/bin/download" ]; then - # Not a legacy plugin - # Run the download script - ( - # shellcheck disable=SC2030 - export ASDF_INSTALL_TYPE=$install_type - # shellcheck disable=SC2030 - export ASDF_INSTALL_VERSION=$version - # shellcheck disable=SC2030 - export ASDF_INSTALL_PATH=$install_path - # shellcheck disable=SC2030 - export ASDF_DOWNLOAD_PATH=$download_path - mkdir "$download_path" - asdf_run_hook "pre_asdf_download_${plugin_name}" "$full_version" - "${plugin_path}"/bin/download - ) - fi - - local download_exit_code=$? - if [ $download_exit_code -eq 0 ]; then - ( - # shellcheck disable=SC2031 - export ASDF_INSTALL_TYPE=$install_type - # shellcheck disable=SC2031 - export ASDF_INSTALL_VERSION=$version - # shellcheck disable=SC2031 - export ASDF_INSTALL_PATH=$install_path - # shellcheck disable=SC2031 - export ASDF_DOWNLOAD_PATH=$download_path - # shellcheck disable=SC2031 - export ASDF_CONCURRENCY=$concurrency - mkdir "$install_path" - asdf_run_hook "pre_asdf_install_${plugin_name}" "$full_version" - "${plugin_path}"/bin/install - ) - fi - - local install_exit_code=$? - if [ $install_exit_code -eq 0 ] && [ $download_exit_code -eq 0 ]; then - # Remove download directory if --keep-download flag or always_keep_download config setting are not set - always_keep_download=$(get_asdf_config_value "always_keep_download") - if [ ! "$keep_download" = "true" ] && [ ! "$always_keep_download" = "yes" ] && [ -d "$download_path" ]; then - rm -r "$download_path" - fi - - asdf reshim "$plugin_name" "$full_version" - - asdf_run_hook "post_asdf_install_${plugin_name}" "$full_version" - else - handle_failure "$install_path" - fi - fi -} +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" +# shellcheck source=lib/commands/reshim.bash +. "$(dirname "$ASDF_CMD_FILE")/reshim.bash" +# shellcheck source=lib/functions/installs.bash +. "$(dirname "$(dirname "$0")")/lib/functions/installs.bash" install_command "$@" diff --git a/lib/commands/command-latest.bash b/lib/commands/command-latest.bash index 16f2e7ef..da478e30 100644 --- a/lib/commands/command-latest.bash +++ b/lib/commands/command-latest.bash @@ -1,87 +1,5 @@ # -*- sh -*- - -latest_command() { - DEFAULT_QUERY="[0-9]" - - local plugin_name=$1 - local query=$2 - local plugin_path - - if [ "$plugin_name" == "--all" ]; then - latest_all - fi - - [[ -z $query ]] && query="$DEFAULT_QUERY" - - plugin_path=$(get_plugin_path "$plugin_name") - check_if_plugin_exists "$plugin_name" - - local versions - - if [ -f "${plugin_path}/bin/latest-stable" ]; then - versions=$("${plugin_path}"/bin/latest-stable "$query") - if [ -z "${versions}" ]; then - # this branch requires this print to mimic the error from the list-all branch - printf "No compatible versions available (%s %s)\n" "$plugin_name" "$query" >&2 - exit 1 - fi - else - # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest) - versions=$(asdf list-all "$plugin_name" "$query" | - grep -ivE "(^Available versions:|-src|-dev|-latest|-stm|[-\\.]rc|-alpha|-beta|[-\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)" | - sed 's/^[[:space:]]\+//' | - tail -1) - if [ -z "${versions}" ]; then - exit 1 - fi - fi - - printf "%s\n" "$versions" -} - -latest_all() { - local plugins_path - plugins_path=$(get_plugin_path) - - if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then - for plugin_path in "$plugins_path"/*; do - plugin_name=$(basename "$plugin_path") - - # Retrieve the version of the plugin - local version - if [ -f "${plugin_path}/bin/latest-stable" ]; then - # We can't filter by a concrete query because different plugins might - # have different queries. - version=$("${plugin_path}"/bin/latest-stable "") - if [ -z "${version}" ]; then - version="unknown" - fi - else - # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest) - version=$(asdf list-all "$plugin_name" | - grep -ivE "(^Available version:|-src|-dev|-latest|-stm|[-\\.]rc|-alpha|-beta|[-\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)" | - sed 's/^[[:space:]]\+//' | - tail -1) - if [ -z "${version}" ]; then - version="unknown" - fi - fi - - local installed_status - installed_status="missing" - - local installed_versions - installed_versions=$(list_installed_versions "$plugin_name") - - if [ -n "$installed_versions" ] && printf '%s\n' "$installed_versions" | grep -q "^$version\$"; then - installed_status="installed" - fi - printf "%s\\t%s\\t%s\\n" "$plugin_name" "$version" "$installed_status" - done - else - printf "%s\\n" 'No plugins installed' - fi - exit 0 -} +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" latest_command "$@" diff --git a/lib/commands/command-list-all.bash b/lib/commands/command-list-all.bash index f0357a36..730a9af9 100644 --- a/lib/commands/command-list-all.bash +++ b/lib/commands/command-list-all.bash @@ -1,50 +1,5 @@ # -*- sh -*- - -list_all_command() { - local plugin_name=$1 - local query=$2 - local plugin_path - local std_out_file - local std_err_file - local output - plugin_path=$(get_plugin_path "$plugin_name") - check_if_plugin_exists "$plugin_name" - - # Capture return code to allow error handling - std_out_file="$(mktemp "/tmp/asdf-command-list-all-${plugin_name}.stdout.XXXXXX")" - std_err_file="$(mktemp "/tmp/asdf-command-list-all-${plugin_name}.stderr.XXXXXX")" - return_code=0 && "${plugin_path}/bin/list-all" >"$std_out_file" 2>"$std_err_file" || return_code=$? - - if [[ $return_code -ne 0 ]]; then - # Printing all output to allow plugin to handle error formatting - printf "Plugin %s's list-all callback script failed with output:\\n" "${plugin_name}" >&2 - printf "%s\\n" "$(cat "$std_err_file")" >&2 - printf "%s\\n" "$(cat "$std_out_file")" >&2 - rm "$std_out_file" "$std_err_file" - exit 1 - fi - - if [[ $query ]]; then - output=$(tr ' ' '\n' <"$std_out_file" | - grep -E "^\\s*$query" | - tr '\n' ' ') - else - output=$(cat "$std_out_file") - fi - - if [ -z "$output" ]; then - display_error "No compatible versions available ($plugin_name $query)" - exit 1 - fi - - IFS=' ' read -r -a versions_list <<<"$output" - - for version in "${versions_list[@]}"; do - printf "%s\\n" "${version}" - done - - # Remove temp files if they still exist - rm "$std_out_file" "$std_err_file" || true -} +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" list_all_command "$@" diff --git a/lib/commands/command-local.bash b/lib/commands/command-local.bash index 78443d49..034e8ac0 100644 --- a/lib/commands/command-local.bash +++ b/lib/commands/command-local.bash @@ -3,30 +3,4 @@ # shellcheck source=lib/commands/version_commands.bash . "$(dirname "$ASDF_CMD_FILE")/version_commands.bash" -local_command() { - local parent=false - local positional=() - - while [[ $# -gt 0 ]]; do - case $1 in - -p | --parent) - parent="true" - shift # past value - ;; - *) - positional+=("$1") # save it in an array for later - shift # past argument - ;; - esac - done - - set -- "${positional[@]}" # restore positional parameters - - if [ $parent = true ]; then - version_command local-tree "$@" - else - version_command local "$@" - fi -} - local_command "$@" diff --git a/lib/commands/command-plugin-add.bash b/lib/commands/command-plugin-add.bash index 0f4685a6..be9c8603 100644 --- a/lib/commands/command-plugin-add.bash +++ b/lib/commands/command-plugin-add.bash @@ -1,58 +1,5 @@ # -*- sh -*- - -plugin_add_command() { - if [[ $# -lt 1 || $# -gt 2 ]]; then - display_error "usage: asdf plugin add []" - exit 1 - fi - - local plugin_name=$1 - - if ! printf "%s" "$plugin_name" | grep -q -E "^[a-zA-Z0-9_-]+$"; then - display_error "$plugin_name is invalid. Name must match regex ^[a-zA-Z0-9_-]+$" - exit 1 - fi - - if [ -n "$2" ]; then - local source_url=$2 - else - initialize_or_update_repository - local source_url - source_url=$(get_plugin_source_url "$plugin_name") - fi - - if [ -z "$source_url" ]; then - display_error "plugin $plugin_name not found in repository" - exit 1 - fi - - local plugin_path - plugin_path=$(get_plugin_path "$plugin_name") - - mkdir -p "$(asdf_data_dir)/plugins" - - if [ -d "$plugin_path" ]; then - display_error "Plugin named $plugin_name already added" - exit 2 - else - asdf_run_hook "pre_asdf_plugin_add" "$plugin_name" - asdf_run_hook "pre_asdf_plugin_add_${plugin_name}" - - if ! git clone -q "$source_url" "$plugin_path"; then - exit 1 - fi - - if [ -f "${plugin_path}/bin/post-plugin-add" ]; then - ( - export ASDF_PLUGIN_SOURCE_URL=$source_url - export ASDF_PLUGIN_PATH=$plugin_path - "${plugin_path}/bin/post-plugin-add" - ) - fi - - asdf_run_hook "post_asdf_plugin_add" "$plugin_name" - asdf_run_hook "post_asdf_plugin_add_${plugin_name}" - fi -} +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" plugin_add_command "$@" diff --git a/lib/commands/command-plugin-list.bash b/lib/commands/command-plugin-list.bash index 91a1948d..a9465894 100644 --- a/lib/commands/command-plugin-list.bash +++ b/lib/commands/command-plugin-list.bash @@ -1,53 +1,5 @@ # -*- sh -*- - -plugin_list_command() { - local plugins_path - plugins_path=$(get_plugin_path) - - local show_repo - local show_ref - - while [ -n "$*" ]; do - case "$1" in - "--urls") - show_repo=true - shift - ;; - "--refs") - show_ref=true - shift - ;; - *) - shift - ;; - esac - done - - if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then - ( - for plugin_path in "$plugins_path"/*; do - plugin_name=$(basename "$plugin_path") - printf "%s" "$plugin_name" - - if [ -n "$show_repo" ]; then - printf "\\t%s" "$(git --git-dir "$plugin_path/.git" remote get-url origin 2>/dev/null)" - fi - - if [ -n "$show_ref" ]; then - local branch - local gitref - branch=$(git --git-dir "$plugin_path/.git" rev-parse --abbrev-ref HEAD 2>/dev/null) - gitref=$(git --git-dir "$plugin_path/.git" rev-parse --short HEAD 2>/dev/null) - printf "\\t%s\\t%s" "$branch" "$gitref" - fi - - printf "\\n" - done - ) | awk '{ if (NF > 1) { printf("%-28s", $1) ; $1="" }; print $0}' - else - display_error 'No plugins installed' - exit 1 - fi -} +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" plugin_list_command "$@" diff --git a/lib/commands/command-plugin-test.bash b/lib/commands/command-plugin-test.bash index 82f1e2ce..e253fea2 100644 --- a/lib/commands/command-plugin-test.bash +++ b/lib/commands/command-plugin-test.bash @@ -1,4 +1,12 @@ # -*- sh -*- +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" +# shellcheck source=lib/commands/reshim.bash +. "$(dirname "$ASDF_CMD_FILE")/reshim.bash" +# shellcheck source=lib/functions/installs.bash +. "$(dirname "$(dirname "$0")")/lib/functions/installs.bash" plugin_test_command() { @@ -66,15 +74,16 @@ plugin_test_command() { # shellcheck disable=SC1090 . "$ASDF_DIR/asdf.sh" - if ! (asdf plugin add "$plugin_name" "$plugin_url"); then + if ! (plugin_add_command "$plugin_name" "$plugin_url"); then fail_test "could not install $plugin_name from $plugin_url" fi - if ! (asdf plugin-list | grep "^$plugin_name$" >/dev/null); then + # shellcheck disable=SC2119 + if ! (plugin_list_command | grep "^$plugin_name$" >/dev/null); then fail_test "$plugin_name was not properly installed" fi - if ! (asdf plugin-update "$plugin_name" "$plugin_gitref"); then + if ! (plugin_update_command "$plugin_name" "$plugin_gitref"); then fail_test "failed to checkout $plugin_name gitref: $plugin_gitref" fi @@ -102,7 +111,7 @@ plugin_test_command() { local versions # shellcheck disable=SC2046 - if ! read -r -a versions <<<$(asdf list-all "$plugin_name"); then + if ! read -r -a versions <<<$(list_all_command "$plugin_name"); then fail_test "list-all exited with an error" fi @@ -115,7 +124,7 @@ plugin_test_command() { # Use the version passed in if it was set. Otherwise grab the latest # version from the versions list if [ -z "$tool_version" ] || [[ "$tool_version" == *"latest"* ]]; then - version="$(asdf latest "$plugin_name" "$(sed -e 's#latest##;s#^:##' <<<"$tool_version")")" + version="$(latest_command "$plugin_name" "$(sed -e 's#latest##;s#^:##' <<<"$tool_version")")" if [ -z "$version" ]; then fail_test "could not get latest version" fi @@ -123,17 +132,17 @@ plugin_test_command() { version="$tool_version" fi - if ! (asdf install "$plugin_name" "$version"); then + if ! (install_command "$plugin_name" "$version"); then fail_test "install exited with an error" fi cd "$TEST_DIR" || fail_test "could not cd $TEST_DIR" - if ! (asdf local "$plugin_name" "$version"); then + if ! (local_command "$plugin_name" "$version"); then fail_test "install did not add the requested version" fi - if ! (asdf reshim "$plugin_name"); then + if ! (reshim_command "$plugin_name"); then fail_test "could not reshim plugin" fi diff --git a/lib/commands/command-plugin-update.bash b/lib/commands/command-plugin-update.bash index 8819bc9f..6ed9da74 100644 --- a/lib/commands/command-plugin-update.bash +++ b/lib/commands/command-plugin-update.bash @@ -1,66 +1,5 @@ # -*- sh -*- - -plugin_update_command() { - if [ "$#" -lt 1 ]; then - display_error "usage: asdf plugin-update { [git-ref] | --all}" - exit 1 - fi - - local plugin_name="$1" - local gitref="${2}" - local plugins= - - if [ "$plugin_name" = "--all" ]; then - if [ -d "$(asdf_data_dir)"/plugins ]; then - plugins=$(find "$(asdf_data_dir)"/plugins -mindepth 1 -maxdepth 1 -type d) - while IFS= read -r dir; do - update_plugin "$(basename "$dir")" "$dir" "$gitref" & - done <<<"$plugins" - wait - fi - else - local plugin_path - plugin_path="$(get_plugin_path "$plugin_name")" - check_if_plugin_exists "$plugin_name" - update_plugin "$plugin_name" "$plugin_path" "$gitref" - fi -} - -update_plugin() { - local plugin_name=$1 - local plugin_path=$2 - plugin_remote_default_branch=$(git --git-dir "$plugin_path/.git" --work-tree "$plugin_path" ls-remote --symref origin HEAD | awk '{ sub(/refs\/heads\//, ""); print $2; exit }') - local gitref=${3:-${plugin_remote_default_branch}} - logfile=$(mktemp) - - local common_git_options=(--git-dir "$plugin_path/.git" --work-tree "$plugin_path") - local prev_ref= - local post_ref= - { - asdf_run_hook "pre_asdf_plugin_update" "$plugin_name" - asdf_run_hook "pre_asdf_plugin_update_${plugin_name}" - - printf "Updating %s to %s\\n" "$plugin_name" "$gitref" - - git "${common_git_options[@]}" fetch --prune --update-head-ok origin "$gitref:$gitref" - prev_ref=$(git "${common_git_options[@]}" rev-parse --short HEAD) - post_ref=$(git "${common_git_options[@]}" rev-parse --short "${gitref}") - git "${common_git_options[@]}" -c advice.detachedHead=false checkout --force "$gitref" - - if [ -f "${plugin_path}/bin/post-plugin-update" ]; then - ( - export ASDF_PLUGIN_PATH=$plugin_path - export ASDF_PLUGIN_PREV_REF=$prev_ref - export ASDF_PLUGIN_POST_REF=$post_ref - "${plugin_path}/bin/post-plugin-update" - ) - fi - - asdf_run_hook "post_asdf_plugin_update" "$plugin_name" - asdf_run_hook "post_asdf_plugin_update_${plugin_name}" - } >"$logfile" 2>&1 - cat "$logfile" - rm "$logfile" -} +# shellcheck source=lib/functions/plugins.bash +. "$(dirname "$(dirname "$0")")/lib/functions/plugins.bash" plugin_update_command "$@" diff --git a/lib/commands/command-reshim.bash b/lib/commands/command-reshim.bash index 8251f31d..93386246 100644 --- a/lib/commands/command-reshim.bash +++ b/lib/commands/command-reshim.bash @@ -3,141 +3,4 @@ # shellcheck source=lib/commands/reshim.bash . "$(dirname "$ASDF_CMD_FILE")/reshim.bash" -reshim_command() { - local plugin_name=$1 - local full_version=$2 - - if [ -z "$plugin_name" ]; then - local plugins_path - plugins_path=$(get_plugin_path) - - if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then - for plugin_path in "$plugins_path"/*; do - plugin_name=$(basename "$plugin_path") - reshim_command "$plugin_name" - done - fi - return 0 - fi - - check_if_plugin_exists "$plugin_name" - ensure_shims_dir - - if [ "$full_version" != "" ]; then - # generate for the whole package version - asdf_run_hook "pre_asdf_reshim_$plugin_name" "$full_version" - generate_shims_for_version "$plugin_name" "$full_version" - asdf_run_hook "post_asdf_reshim_$plugin_name" "$full_version" - else - # generate for all versions of the package - local plugin_installs_path - plugin_installs_path="$(asdf_data_dir)/installs/${plugin_name}" - - for install in "${plugin_installs_path}"/*/; do - local full_version_name - full_version_name=$(basename "$install" | sed 's/ref\-/ref\:/') - asdf_run_hook "pre_asdf_reshim_$plugin_name" "$full_version_name" - generate_shims_for_version "$plugin_name" "$full_version_name" - remove_obsolete_shims "$plugin_name" "$full_version_name" - asdf_run_hook "post_asdf_reshim_$plugin_name" "$full_version_name" - done - fi - -} - -ensure_shims_dir() { - # Create shims dir if doesn't exist - if [ ! -d "$(asdf_data_dir)/shims" ]; then - mkdir "$(asdf_data_dir)/shims" - fi -} - -write_shim_script() { - local plugin_name=$1 - local version=$2 - local executable_path=$3 - - if ! is_executable "$executable_path"; then - return 0 - fi - - local executable_name - executable_name=$(basename "$executable_path") - - local shim_path - shim_path="$(asdf_data_dir)/shims/$executable_name" - - if [ -f "$shim_path" ]; then - if ! grep -x "# asdf-plugin: ${plugin_name} ${version}" "$shim_path" >/dev/null; then - sed -i.bak -e "s/exec /# asdf-plugin: ${plugin_name} ${version}\\"$'\n''exec /' "$shim_path" - rm -f "$shim_path".bak - fi - else - cat <"$shim_path" -#!/usr/bin/env bash -# asdf-plugin: ${plugin_name} ${version} -exec $(asdf_dir)/bin/asdf exec "${executable_name}" "\$@" -EOF - fi - - chmod +x "$shim_path" -} - -generate_shim_for_executable() { - local plugin_name=$1 - local executable=$2 - - check_if_plugin_exists "$plugin_name" - - local version - IFS=':' read -r -a version_info <<<"$full_version" - if [ "${version_info[0]}" = "ref" ]; then - version="${version_info[1]}" - else - version="${version_info[0]}" - fi - - write_shim_script "$plugin_name" "$version" "$executable" -} - -generate_shims_for_version() { - local plugin_name=$1 - local full_version=$2 - local all_executable_paths - IFS=$'\n' read -rd '' -a all_executable_paths <<<"$(plugin_executables "$plugin_name" "$full_version")" - for executable_path in "${all_executable_paths[@]}"; do - write_shim_script "$plugin_name" "$full_version" "$executable_path" - done -} - -remove_obsolete_shims() { - local plugin_name=$1 - local full_version=$2 - - local shims - shims=$(plugin_shims "$plugin_name" "$full_version" | xargs -IX basename X | sort) - - local exec_names - exec_names=$(plugin_executables "$plugin_name" "$full_version" | xargs -IX basename X | sort) - - local obsolete_shims - local formatted_shims - local formatted_exec_names - - # comm only takes to files, so we write this data to temp files so we can - # pass it to comm. - formatted_shims=$(mktemp /tmp/asdf-command-reshim-formatted-shims.XXXXXX) - printf "%s\\n" "$shims" >"$formatted_shims" - - formatted_exec_names=$(mktemp /tmp/asdf-command-reshim-formatted-exec-names.XXXXXX) - printf "%s\\n" "$exec_names" >"$formatted_exec_names" - - obsolete_shims=$(comm -23 "$formatted_shims" "$formatted_exec_names") - rm -f "$formatted_exec_names" "$formatted_shims" - - for shim_name in $obsolete_shims; do - remove_shim_for_version "$plugin_name" "$full_version" "$shim_name" - done -} - reshim_command "$@" diff --git a/lib/commands/reshim.bash b/lib/commands/reshim.bash index 40865f1c..be6fd655 100644 --- a/lib/commands/reshim.bash +++ b/lib/commands/reshim.bash @@ -23,3 +23,140 @@ remove_shim_for_version() { rm -f "$shim_path" fi } + +reshim_command() { + local plugin_name=$1 + local full_version=$2 + + if [ -z "$plugin_name" ]; then + local plugins_path + plugins_path=$(get_plugin_path) + + if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then + for plugin_path in "$plugins_path"/*; do + plugin_name=$(basename "$plugin_path") + reshim_command "$plugin_name" + done + fi + return 0 + fi + + check_if_plugin_exists "$plugin_name" + ensure_shims_dir + + if [ "$full_version" != "" ]; then + # generate for the whole package version + asdf_run_hook "pre_asdf_reshim_$plugin_name" "$full_version" + generate_shims_for_version "$plugin_name" "$full_version" + asdf_run_hook "post_asdf_reshim_$plugin_name" "$full_version" + else + # generate for all versions of the package + local plugin_installs_path + plugin_installs_path="$(asdf_data_dir)/installs/${plugin_name}" + + for install in "${plugin_installs_path}"/*/; do + local full_version_name + full_version_name=$(basename "$install" | sed 's/ref\-/ref\:/') + asdf_run_hook "pre_asdf_reshim_$plugin_name" "$full_version_name" + generate_shims_for_version "$plugin_name" "$full_version_name" + remove_obsolete_shims "$plugin_name" "$full_version_name" + asdf_run_hook "post_asdf_reshim_$plugin_name" "$full_version_name" + done + fi + +} + +ensure_shims_dir() { + # Create shims dir if doesn't exist + if [ ! -d "$(asdf_data_dir)/shims" ]; then + mkdir "$(asdf_data_dir)/shims" + fi +} + +write_shim_script() { + local plugin_name=$1 + local version=$2 + local executable_path=$3 + + if ! is_executable "$executable_path"; then + return 0 + fi + + local executable_name + executable_name=$(basename "$executable_path") + + local shim_path + shim_path="$(asdf_data_dir)/shims/$executable_name" + + if [ -f "$shim_path" ]; then + if ! grep -x "# asdf-plugin: ${plugin_name} ${version}" "$shim_path" >/dev/null; then + sed -i.bak -e "s/exec /# asdf-plugin: ${plugin_name} ${version}\\"$'\n''exec /' "$shim_path" + rm -f "$shim_path".bak + fi + else + cat <"$shim_path" +#!/usr/bin/env bash +# asdf-plugin: ${plugin_name} ${version} +exec $(asdf_dir)/bin/asdf exec "${executable_name}" "\$@" +EOF + fi + + chmod +x "$shim_path" +} + +generate_shim_for_executable() { + local plugin_name=$1 + local executable=$2 + + check_if_plugin_exists "$plugin_name" + + local version + IFS=':' read -r -a version_info <<<"$full_version" + if [ "${version_info[0]}" = "ref" ]; then + version="${version_info[1]}" + else + version="${version_info[0]}" + fi + + write_shim_script "$plugin_name" "$version" "$executable" +} + +generate_shims_for_version() { + local plugin_name=$1 + local full_version=$2 + local all_executable_paths + IFS=$'\n' read -rd '' -a all_executable_paths <<<"$(plugin_executables "$plugin_name" "$full_version")" + for executable_path in "${all_executable_paths[@]}"; do + write_shim_script "$plugin_name" "$full_version" "$executable_path" + done +} + +remove_obsolete_shims() { + local plugin_name=$1 + local full_version=$2 + + local shims + shims=$(plugin_shims "$plugin_name" "$full_version" | xargs -IX basename X | sort) + + local exec_names + exec_names=$(plugin_executables "$plugin_name" "$full_version" | xargs -IX basename X | sort) + + local obsolete_shims + local formatted_shims + local formatted_exec_names + + # comm only takes to files, so we write this data to temp files so we can + # pass it to comm. + formatted_shims=$(mktemp /tmp/asdf-command-reshim-formatted-shims.XXXXXX) + printf "%s\\n" "$shims" >"$formatted_shims" + + formatted_exec_names=$(mktemp /tmp/asdf-command-reshim-formatted-exec-names.XXXXXX) + printf "%s\\n" "$exec_names" >"$formatted_exec_names" + + obsolete_shims=$(comm -23 "$formatted_shims" "$formatted_exec_names") + rm -f "$formatted_exec_names" "$formatted_shims" + + for shim_name in $obsolete_shims; do + remove_shim_for_version "$plugin_name" "$full_version" "$shim_name" + done +} diff --git a/lib/commands/version_commands.bash b/lib/commands/version_commands.bash index 8f8f70ef..5c2773c0 100644 --- a/lib/commands/version_commands.bash +++ b/lib/commands/version_commands.bash @@ -1,67 +1,3 @@ # -*- sh -*- - -version_command() { - local cmd=$1 - local plugin_name=$2 - - if [ "$#" -lt "3" ]; then - if [ "$cmd" = "global" ]; then - printf "Usage: asdf global \\n" - else - printf "Usage: asdf local \\n" - fi - exit 1 - fi - - shift 2 - local versions=("$@") - - local file - if [ "$cmd" = "global" ]; then - file=${ASDF_DEFAULT_TOOL_VERSIONS_FILENAME:-$HOME/.tool-versions} - elif [ "$cmd" = "local-tree" ]; then - file=$(find_tool_versions) - else # cmd = local - file="$(pwd)/.tool-versions" - fi - - if [ -L "$file" ]; then - # Resolve file path if symlink - file="$(resolve_symlink "$file")" - fi - - check_if_plugin_exists "$plugin_name" - - declare -a resolved_versions - local item - for item in "${!versions[@]}"; do - IFS=':' read -r -a version_info <<<"${versions[$item]}" - if [ "${version_info[0]}" = "latest" ] && [ -n "${version_info[1]}" ]; then - version=$(asdf latest "$plugin_name" "${version_info[1]}") - elif [ "${version_info[0]}" = "latest" ] && [ -z "${version_info[1]}" ]; then - version=$(asdf latest "$plugin_name") - else - # if branch handles ref: || path: || normal versions - version="${versions[$item]}" - fi - - # check_if_version_exists should probably handle if either param is empty string - if [ -z "$version" ]; then - exit 1 - fi - - if ! (check_if_version_exists "$plugin_name" "$version"); then - version_not_installed_text "$plugin_name" "$version" 1>&2 - exit 1 - fi - - resolved_versions+=("$version") - done - - if [ -f "$file" ] && grep "^$plugin_name " "$file" >/dev/null; then - sed -i.bak -e "s|^$plugin_name .*$|$plugin_name ${resolved_versions[*]}|" "$file" - rm -f "$file".bak - else - printf "%s %s\\n" "$plugin_name" "${resolved_versions[*]}" >>"$file" - fi -} +# shellcheck source=lib/functions/versions.bash +. "$(dirname "$(dirname "$0")")/lib/functions/versions.bash" diff --git a/lib/functions/installs.bash b/lib/functions/installs.bash new file mode 100644 index 00000000..7f15d299 --- /dev/null +++ b/lib/functions/installs.bash @@ -0,0 +1,239 @@ +handle_failure() { + local install_path="$1" + rm -rf "$install_path" + exit 1 +} + +handle_cancel() { + local install_path="$1" + printf "\\nreceived sigint, cleaning up" + handle_failure "$install_path" +} + +install_command() { + local plugin_name=$1 + local full_version=$2 + local extra_args="${*:3}" + + if [ "$plugin_name" = "" ] && [ "$full_version" = "" ]; then + install_local_tool_versions "$extra_args" + elif [[ $# -eq 1 ]]; then + install_one_local_tool "$plugin_name" + else + install_tool_version "$plugin_name" "$full_version" "$extra_args" + fi +} + +get_concurrency() { + if command -v nproc >/dev/null 2>&1; then + nproc + elif command -v sysctl >/dev/null 2>&1 && sysctl hw.ncpu >/dev/null 2>&1; then + sysctl -n hw.ncpu + elif [ -f /proc/cpuinfo ]; then + grep -c processor /proc/cpuinfo + else + printf "1\\n" + fi +} + +install_one_local_tool() { + local plugin_name=$1 + local search_path + search_path=$(pwd) + + local plugin_versions + + local plugin_version + + 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 + else + printf "No versions specified for %s in config files or environment\\n" "$plugin_name" + exit 1 + fi +} + +install_local_tool_versions() { + local plugins_path + plugins_path=$(get_plugin_path) + + local search_path + search_path=$(pwd) + + local some_tools_installed + local some_plugin_not_installed + + local tool_versions_path + tool_versions_path=$(find_tool_versions) + + # Locate all the plugins installed in the system + local plugins_installed + if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then + for plugin_path in "$plugins_path"/*; do + local plugin_name + plugin_name=$(basename "$plugin_path") + plugins_installed="$plugins_installed $plugin_name" + done + plugins_installed=$(printf "%s" "$plugins_installed" | tr " " "\n") + fi + + if [ -z "$plugins_installed" ]; then + printf "Install plugins first to be able to install tools\\n" + exit 1 + fi + + # Locate all the plugins defined in the versions file. + local tools_file + if [ -f "$tool_versions_path" ]; then + tools_file=$(strip_tool_version_comments "$tool_versions_path" | cut -d ' ' -f 1) + for plugin_name in $tools_file; do + if ! printf '%s\n' "${plugins_installed[@]}" | grep -q "^$plugin_name\$"; then + printf "%s plugin is not installed\n" "$plugin_name" + some_plugin_not_installed='yes' + fi + done + fi + + if [ -n "$some_plugin_not_installed" ]; then + exit 1 + fi + + 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 + fi + + if [ -z "$some_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" + exit 1 + fi +} + +install_tool_version() { + local plugin_name=$1 + local full_version=$2 + local flags=$3 + local keep_download + local plugin_path + + plugin_path=$(get_plugin_path "$plugin_name") + check_if_plugin_exists "$plugin_name" + + for flag in $flags; do + case "$flag" in + "--keep-download") + keep_download=true + shift + ;; + *) + shift + ;; + esac + done + + if [ "$full_version" = "system" ]; then + return + fi + + IFS=':' read -r -a version_info <<<"$full_version" + if [ "${version_info[0]}" = "ref" ]; then + local install_type="${version_info[0]}" + local version="${version_info[1]}" + else + local install_type="version" + + if [ "${version_info[0]}" = "latest" ]; then + local version + version=$(latest_command "$plugin_name" "${version_info[1]}") + full_version=$version + else + local version="${version_info[0]}" + fi + fi + + local install_path + install_path=$(get_install_path "$plugin_name" "$install_type" "$version") + local download_path + download_path=$(get_download_path "$plugin_name" "$install_type" "$version") + local concurrency + concurrency=$(get_concurrency) + trap 'handle_cancel $install_path' INT + + if [ -d "$install_path" ]; then + printf "%s %s is already installed\\n" "$plugin_name" "$full_version" + else + + if [ -f "${plugin_path}/bin/download" ]; then + # Not a legacy plugin + # Run the download script + ( + # shellcheck disable=SC2030 + export ASDF_INSTALL_TYPE=$install_type + # shellcheck disable=SC2030 + export ASDF_INSTALL_VERSION=$version + # shellcheck disable=SC2030 + export ASDF_INSTALL_PATH=$install_path + # shellcheck disable=SC2030 + export ASDF_DOWNLOAD_PATH=$download_path + mkdir "$download_path" + asdf_run_hook "pre_asdf_download_${plugin_name}" "$full_version" + "${plugin_path}"/bin/download + ) + fi + + local download_exit_code=$? + if [ $download_exit_code -eq 0 ]; then + ( + # shellcheck disable=SC2031 + export ASDF_INSTALL_TYPE=$install_type + # shellcheck disable=SC2031 + export ASDF_INSTALL_VERSION=$version + # shellcheck disable=SC2031 + export ASDF_INSTALL_PATH=$install_path + # shellcheck disable=SC2031 + export ASDF_DOWNLOAD_PATH=$download_path + # shellcheck disable=SC2031 + export ASDF_CONCURRENCY=$concurrency + mkdir "$install_path" + asdf_run_hook "pre_asdf_install_${plugin_name}" "$full_version" + "${plugin_path}"/bin/install + ) + fi + + local install_exit_code=$? + if [ $install_exit_code -eq 0 ] && [ $download_exit_code -eq 0 ]; then + # Remove download directory if --keep-download flag or always_keep_download config setting are not set + always_keep_download=$(get_asdf_config_value "always_keep_download") + if [ ! "$keep_download" = "true" ] && [ ! "$always_keep_download" = "yes" ] && [ -d "$download_path" ]; then + rm -r "$download_path" + fi + + reshim_command "$plugin_name" "$full_version" + + asdf_run_hook "post_asdf_install_${plugin_name}" "$full_version" + else + handle_failure "$install_path" + fi + fi +} diff --git a/lib/functions/plugins.bash b/lib/functions/plugins.bash new file mode 100644 index 00000000..9d68375c --- /dev/null +++ b/lib/functions/plugins.bash @@ -0,0 +1,167 @@ +plugin_list_command() { + local plugins_path + plugins_path=$(get_plugin_path) + + local show_repo + local show_ref + + while [ -n "$*" ]; do + case "$1" in + "--urls") + show_repo=true + shift + ;; + "--refs") + show_ref=true + shift + ;; + *) + shift + ;; + esac + done + + if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then + ( + for plugin_path in "$plugins_path"/*; do + plugin_name=$(basename "$plugin_path") + printf "%s" "$plugin_name" + + if [ -n "$show_repo" ]; then + printf "\\t%s" "$(git --git-dir "$plugin_path/.git" remote get-url origin 2>/dev/null)" + fi + + if [ -n "$show_ref" ]; then + local branch + local gitref + branch=$(git --git-dir "$plugin_path/.git" rev-parse --abbrev-ref HEAD 2>/dev/null) + gitref=$(git --git-dir "$plugin_path/.git" rev-parse --short HEAD 2>/dev/null) + printf "\\t%s\\t%s" "$branch" "$gitref" + fi + + printf "\\n" + done + ) | awk '{ if (NF > 1) { printf("%-28s", $1) ; $1="" }; print $0}' + else + display_error 'No plugins installed' + exit 1 + fi +} + +plugin_add_command() { + if [[ $# -lt 1 || $# -gt 2 ]]; then + display_error "usage: asdf plugin add []" + exit 1 + fi + + local plugin_name=$1 + + if ! printf "%s" "$plugin_name" | grep -q -E "^[a-zA-Z0-9_-]+$"; then + display_error "$plugin_name is invalid. Name must match regex ^[a-zA-Z0-9_-]+$" + exit 1 + fi + + if [ -n "$2" ]; then + local source_url=$2 + else + initialize_or_update_repository + local source_url + source_url=$(get_plugin_source_url "$plugin_name") + fi + + if [ -z "$source_url" ]; then + display_error "plugin $plugin_name not found in repository" + exit 1 + fi + + local plugin_path + plugin_path=$(get_plugin_path "$plugin_name") + + mkdir -p "$(asdf_data_dir)/plugins" + + if [ -d "$plugin_path" ]; then + display_error "Plugin named $plugin_name already added" + exit 2 + else + asdf_run_hook "pre_asdf_plugin_add" "$plugin_name" + asdf_run_hook "pre_asdf_plugin_add_${plugin_name}" + + if ! git clone -q "$source_url" "$plugin_path"; then + exit 1 + fi + + if [ -f "${plugin_path}/bin/post-plugin-add" ]; then + ( + export ASDF_PLUGIN_SOURCE_URL=$source_url + export ASDF_PLUGIN_PATH=$plugin_path + "${plugin_path}/bin/post-plugin-add" + ) + fi + + asdf_run_hook "post_asdf_plugin_add" "$plugin_name" + asdf_run_hook "post_asdf_plugin_add_${plugin_name}" + fi +} + +plugin_update_command() { + if [ "$#" -lt 1 ]; then + display_error "usage: asdf plugin-update { [git-ref] | --all}" + exit 1 + fi + + local plugin_name="$1" + local gitref="${2}" + local plugins= + + if [ "$plugin_name" = "--all" ]; then + if [ -d "$(asdf_data_dir)"/plugins ]; then + plugins=$(find "$(asdf_data_dir)"/plugins -mindepth 1 -maxdepth 1 -type d) + while IFS= read -r dir; do + update_plugin "$(basename "$dir")" "$dir" "$gitref" & + done <<<"$plugins" + wait + fi + else + local plugin_path + plugin_path="$(get_plugin_path "$plugin_name")" + check_if_plugin_exists "$plugin_name" + update_plugin "$plugin_name" "$plugin_path" "$gitref" + fi +} + +update_plugin() { + local plugin_name=$1 + local plugin_path=$2 + plugin_remote_default_branch=$(git --git-dir "$plugin_path/.git" --work-tree "$plugin_path" ls-remote --symref origin HEAD | awk '{ sub(/refs\/heads\//, ""); print $2; exit }') + local gitref=${3:-${plugin_remote_default_branch}} + logfile=$(mktemp) + + local common_git_options=(--git-dir "$plugin_path/.git" --work-tree "$plugin_path") + local prev_ref= + local post_ref= + { + asdf_run_hook "pre_asdf_plugin_update" "$plugin_name" + asdf_run_hook "pre_asdf_plugin_update_${plugin_name}" + + printf "Updating %s to %s\\n" "$plugin_name" "$gitref" + + git "${common_git_options[@]}" fetch --prune --update-head-ok origin "$gitref:$gitref" + prev_ref=$(git "${common_git_options[@]}" rev-parse --short HEAD) + post_ref=$(git "${common_git_options[@]}" rev-parse --short "${gitref}") + git "${common_git_options[@]}" -c advice.detachedHead=false checkout --force "$gitref" + + if [ -f "${plugin_path}/bin/post-plugin-update" ]; then + ( + export ASDF_PLUGIN_PATH=$plugin_path + export ASDF_PLUGIN_PREV_REF=$prev_ref + export ASDF_PLUGIN_POST_REF=$post_ref + "${plugin_path}/bin/post-plugin-update" + ) + fi + + asdf_run_hook "post_asdf_plugin_update" "$plugin_name" + asdf_run_hook "post_asdf_plugin_update_${plugin_name}" + } >"$logfile" 2>&1 + cat "$logfile" + rm "$logfile" +} diff --git a/lib/functions/versions.bash b/lib/functions/versions.bash new file mode 100644 index 00000000..807327d8 --- /dev/null +++ b/lib/functions/versions.bash @@ -0,0 +1,222 @@ +version_command() { + local cmd=$1 + local plugin_name=$2 + + if [ "$#" -lt "3" ]; then + if [ "$cmd" = "global" ]; then + printf "Usage: asdf global \\n" + else + printf "Usage: asdf local \\n" + fi + exit 1 + fi + + shift 2 + local versions=("$@") + + local file + if [ "$cmd" = "global" ]; then + file=${ASDF_DEFAULT_TOOL_VERSIONS_FILENAME:-$HOME/.tool-versions} + elif [ "$cmd" = "local-tree" ]; then + file=$(find_tool_versions) + else # cmd = local + file="$(pwd)/.tool-versions" + fi + + if [ -L "$file" ]; then + # Resolve file path if symlink + file="$(resolve_symlink "$file")" + fi + + check_if_plugin_exists "$plugin_name" + + declare -a resolved_versions + local item + for item in "${!versions[@]}"; do + IFS=':' read -r -a version_info <<<"${versions[$item]}" + if [ "${version_info[0]}" = "latest" ] && [ -n "${version_info[1]}" ]; then + version=$(latest_command "$plugin_name" "${version_info[1]}") + elif [ "${version_info[0]}" = "latest" ] && [ -z "${version_info[1]}" ]; then + version=$(latest_command "$plugin_name") + else + # if branch handles ref: || path: || normal versions + version="${versions[$item]}" + fi + + # check_if_version_exists should probably handle if either param is empty string + if [ -z "$version" ]; then + exit 1 + fi + + if ! (check_if_version_exists "$plugin_name" "$version"); then + version_not_installed_text "$plugin_name" "$version" 1>&2 + exit 1 + fi + + resolved_versions+=("$version") + done + + if [ -f "$file" ] && grep "^$plugin_name " "$file" >/dev/null; then + sed -i.bak -e "s|^$plugin_name .*$|$plugin_name ${resolved_versions[*]}|" "$file" + rm -f "$file".bak + else + printf "%s %s\\n" "$plugin_name" "${resolved_versions[*]}" >>"$file" + fi +} + +list_all_command() { + local plugin_name=$1 + local query=$2 + local plugin_path + local std_out_file + local std_err_file + local output + plugin_path=$(get_plugin_path "$plugin_name") + check_if_plugin_exists "$plugin_name" + + # Capture return code to allow error handling + std_out_file="$(mktemp "/tmp/asdf-command-list-all-${plugin_name}.stdout.XXXXXX")" + std_err_file="$(mktemp "/tmp/asdf-command-list-all-${plugin_name}.stderr.XXXXXX")" + return_code=0 && "${plugin_path}/bin/list-all" >"$std_out_file" 2>"$std_err_file" || return_code=$? + + if [[ $return_code -ne 0 ]]; then + # Printing all output to allow plugin to handle error formatting + printf "Plugin %s's list-all callback script failed with output:\\n" "${plugin_name}" >&2 + printf "%s\\n" "$(cat "$std_err_file")" >&2 + printf "%s\\n" "$(cat "$std_out_file")" >&2 + rm "$std_out_file" "$std_err_file" + exit 1 + fi + + if [[ $query ]]; then + output=$(tr ' ' '\n' <"$std_out_file" | + grep -E "^\\s*$query" | + tr '\n' ' ') + else + output=$(cat "$std_out_file") + fi + + if [ -z "$output" ]; then + display_error "No compatible versions available ($plugin_name $query)" + exit 1 + fi + + IFS=' ' read -r -a versions_list <<<"$output" + + for version in "${versions_list[@]}"; do + printf "%s\\n" "${version}" + done + + # Remove temp files if they still exist + rm "$std_out_file" "$std_err_file" || true +} + +latest_command() { + DEFAULT_QUERY="[0-9]" + + local plugin_name=$1 + local query=$2 + local plugin_path + + if [ "$plugin_name" == "--all" ]; then + latest_all + fi + + [[ -z $query ]] && query="$DEFAULT_QUERY" + + plugin_path=$(get_plugin_path "$plugin_name") + check_if_plugin_exists "$plugin_name" + + local versions + + if [ -f "${plugin_path}/bin/latest-stable" ]; then + versions=$("${plugin_path}"/bin/latest-stable "$query") + if [ -z "${versions}" ]; then + # this branch requires this print to mimic the error from the list-all branch + printf "No compatible versions available (%s %s)\\n" "$plugin_name" "$query" >&2 + exit 1 + fi + else + # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest) + versions=$(list_all_command "$plugin_name" "$query" | + grep -ivE "(^Available versions:|-src|-dev|-latest|-stm|[-\\.]rc|-alpha|-beta|[-\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)" | + sed 's/^[[:space:]]\+//' | + tail -1) + if [ -z "${versions}" ]; then + exit 1 + fi + fi + + printf "%s\\n" "$versions" +} + +latest_all() { + local plugins_path + plugins_path=$(get_plugin_path) + + if find "$plugins_path" -mindepth 1 -type d &>/dev/null; then + for plugin_path in "$plugins_path"/*; do + plugin_name=$(basename "$plugin_path") + + # Retrieve the version of the plugin + local version + if [ -f "${plugin_path}/bin/latest-stable" ]; then + # We can't filter by a concrete query because different plugins might + # have different queries. + version=$("${plugin_path}"/bin/latest-stable "") + if [ -z "${version}" ]; then + version="unknown" + fi + else + # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest) + version=$(list_all_command "$plugin_name" | + grep -ivE "(^Available version:|-src|-dev|-latest|-stm|[-\\.]rc|-alpha|-beta|[-\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)" | + sed 's/^[[:space:]]\+//' | + tail -1) + if [ -z "${version}" ]; then + version="unknown" + fi + fi + + local installed_status + installed_status="missing" + + local installed_versions + installed_versions=$(list_installed_versions "$plugin_name") + + if [ -n "$installed_versions" ] && printf '%s\n' "$installed_versions" | grep -q "^$version\$"; then + installed_status="installed" + fi + printf "%s\\t%s\\t%s\\n" "$plugin_name" "$version" "$installed_status" + done + else + printf "%s\\n" 'No plugins installed' + fi + exit 0 +} + +local_command() { + local parent=false + local positional=() + + while [[ $# -gt 0 ]]; do + case $1 in + -p | --parent) + parent="true" + shift # past value + ;; + *) + positional+=("$1") # save it in an array for later + shift # past argument + ;; + esac + done + + set -- "${positional[@]}" # restore positional parameters + + if [ $parent = true ]; then + version_command local-tree "$@" + else + version_command local "$@" + fi +}