Skip to content

Commit

Permalink
Add a new asdf direnv install command
Browse files Browse the repository at this point in the history
Before this diff, it was difficult to install tools that depend on
other tools. For example, here's what happens if I try to install a
version of poetry on my machine:

    $ asdf install poetry 1.5.1
    No version is set for command python3
    Consider adding one of the following versions in your config file at
    python 3.10.2
    python 3.8.10
    curl: (23) Failure writing output to destination

    Cleanup: Something went wrong!

    48 /home/jeremy/.asdf/plugins/poetry/bin/install: POETRY_HOME=$install_path python3 - --version "$version" $flags

This is because my system doesn't actually have a global `python` or
`python3`. While I *could* install python with my system's package
manager, I'd rather re-use my asdf-managed python if at all possible.
Plus, poetry's installer doesn't even work with the 2 most popular ways
of installing python systemwide on macOS (see [this poetry
issue](python-poetry/install.python-poetry.org#24 (comment))
and [this homebrew issue](Homebrew/homebrew-core#138159)).

I know this doesn't solve the general build-dependencies issue: asdf is
not a full package manager and I doubt it ever will become one. I do
think this fairly minor change is worth implementing though, as it will
solve a pain point my team has started running into ever since [homebrew
changed they way they build binaries, which broke poetry
install](Homebrew/homebrew-core#138159).

As a happy side effect, we can now run `direnv reload` for the user,
which saves them a step whenever they need to install missing
dependencies.
  • Loading branch information
jfly committed Aug 23, 2023
1 parent 8a68f0f commit c9ff9dc
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ github compare-link with the previous one.

## [Unreleased](https://github.com/asdf-community/asdf-direnv/compare/v0.3.0..master)

- Add new `asdf direnv install` command to help when installing tools that depend on each other. #180

- Fix `find` warning. #178

- Add '--no-touch-rc-file' option to prevent rc file modification during updates. #176

- Alternatively, `export ASDF_DIRENV_NO_TOUCH_RC_FILE=1` to prevent rc file modification. #176

- Add support for resolving 'latest:X' in .tool-versions #136

- Add support for resolving 'latest' in .tool-versions #168

## [0.3.0](https://github.com/asdf-community/asdf-direnv/compare/v0.3.0) (2022-04-03)
Expand Down
8 changes: 8 additions & 0 deletions lib/commands/command-install.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

# Exit on error, since this is an executable and not a sourced file.
set -Eeuo pipefail

# shellcheck source=lib/install-lib.bash
source "$(dirname "${BASH_SOURCE[0]}")/../install-lib.bash"
install_command "$@"
97 changes: 97 additions & 0 deletions lib/install-lib.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash

# shellcheck source=lib/tools-environment-lib.bash
source "$(dirname "${BASH_SOURCE[0]}")/tools-environment-lib.bash"

function print_usage() {
echo "Usage: asdf direnv install"
echo ""
echo "Installs tools needed for the current directory. This is very similar"
echo "to 'asdf install' except it installs the tools in the exact order specified"
echo "in the '.tool-versions' file, and loads the environment of each tool in order."
echo ""
echo "This is useful when installing tools that depend on other tools, for"
echo "example: the poetry plugin expects python to be available."
echo ""
echo "Note: this problem isn't entirely unique to asdf-direnv. asdf itself"
echo "isn't a full-fledged package manager. It simply doesn't understand"
echo "dependencies between plugins and therefore cannot do a dependency sort of"
echo "tools. You'll need to manually sort the lines of your '.tool-versions' file"
echo "for this to work as intended. See these discussions in core asdf for"
echo "more information:"
echo ""
echo " - https://github.com/asdf-vm/asdf/issues/929"
echo " - https://github.com/asdf-vm/asdf/issues/1127"
echo " - https://github.com/asdf-vm/asdf/issues/196"
}

function install_command() {
while [[ $# -gt 0 ]]; do
arg=$1
shift
case $arg in
-h | --help)
print_usage
exit 1
;;
*)
echo "Unknown option: $arg"
exit 1
;;
esac
done

install_tools
}

_load_asdf_functions_installs() {
# `install_tool_version` depends on `reshim_command` from reshim.bash.
# See https://github.com/asdf-vm/asdf/blob/v0.12.0/lib/functions/installs.bash#L243
_load_asdf_lib reshim_command commands/reshim.bash

_load_asdf_lib install_tool_version functions/installs.bash
}

function maybe_install_tool_version() {
_load_asdf_functions_installs

local install_path
install_path=$(get_install_path "$plugin_name" "version" "$version")

if [ -d "$install_path" ]; then
printf "%s %s is already installed\n" "$plugin_name" "$version"
else

# NOTE: we temporarily loosen the rules while invoking
# install_tool_version because it's from core asdf and it doesn't run
# well under "strict mode" (asdf_run_hook invokes get_asdf_config_value
# in a way such that if the config piece is missing, the program exits
# immediately if `set -e` is enabled.
(
set +ue
install_tool_version "$plugin_name" "$version"
set -ue
)
fi
}

function install_tools {
local tools_file
tools_file="$(_local_versions_file)"

while IFS=$'\n' read -r plugin_name; do
while IFS=$'\n' read -r version_and_path; do
local version _path
IFS='|' read -r version _path <<<"$version_and_path"

# Install this tool version if not already installed.
maybe_install_tool_version "$plugin_name" "$version"

# Load the tools environment so subsequent installs can use this tool.
direnv_code=$(_plugin_env_bash "$plugin_name" "$version" ">>> UH OH <<<")
eval "$direnv_code"
done <<<"$(_plugin_versions_and_path "$plugin_name")"
done <<<"$(_plugins_in_file "$tools_file")"

direnv reload
}
35 changes: 27 additions & 8 deletions lib/tools-environment-lib.bash
Original file line number Diff line number Diff line change
Expand Up @@ -213,22 +213,23 @@ _load_local_plugins_env() {
fi
}

# from asdf plugin_current_command
_load_plugin_version_and_file() {
function _plugin_versions_and_path() {
local plugin_name=$1
local versions_and_path
# NOTE: temporary disable nounset since find_versions expects ASDF_DEFAULT_TOOL_VERSIONS_FILENAME to be set.
# NOTE: temporarily disable nounset since find_versions expects
# ASDF_DEFAULT_TOOL_VERSIONS_FILENAME to be set.
versions_and_path="$(
set +u
find_versions "$plugin_name" "$(pwd)"
set -u
)"
if test -z "$versions_and_path"; then
if [ -z "$versions_and_path" ]; then
return 0
fi

local path
path=$(cut -d '|' -f 2 <<<"$versions_and_path")

local versions=()
while IFS=$' \t' read -r -a inline_versions; do
for ((idx = ${#inline_versions[@]} - 1; idx >= 0; idx--)); do
Expand All @@ -237,12 +238,30 @@ _load_plugin_version_and_file() {
done <<<"$(cut -d '|' -f 1 <<<"$versions_and_path" | uniq | _tail_r)"

for version in "${versions[@]}"; do
echo log_status "using asdf ${plugin_name} ${version}"
_plugin_env_bash "$plugin_name" "$version" "$plugin_name $version not installed. Run 'asdf install' and then 'direnv reload'."
printf '%q|%q\n' "$version" "$path"
done
if [ -f "$path" ]; then
printf 'watch_file %q\n' "$path"
}

# from asdf plugin_current_command
_load_plugin_version_and_file() {
local plugin_name=$1

local plugin_versions_and_path
plugin_versions_and_path="$(_plugin_versions_and_path "$plugin_name")"
if [ -z "$plugin_versions_and_path" ]; then
return 0
fi

while IFS=$'\n' read -r version_and_path; do
local version path
IFS='|' read -r version path <<<"$version_and_path"

echo log_status "using asdf ${plugin_name} ${version}"
_plugin_env_bash "$plugin_name" "$version" "$plugin_name $version not installed. Run 'asdf direnv install' to install."
if [ -f "$path" ]; then
printf 'watch_file %q\n' "$path"
fi
done <<<"$plugin_versions_and_path"
}

_new_items() {
Expand Down

0 comments on commit c9ff9dc

Please sign in to comment.