├── .gitignore ├── defaults ├── lib ├── commands │ ├── help.sh │ ├── plugin-remove.sh │ ├── plugin-list.sh │ ├── list-all.sh │ ├── which.sh │ ├── plugin-push.sh │ ├── plugin-update.sh │ ├── plugin-add.sh │ ├── list.sh │ ├── where.sh │ ├── uninstall.sh │ ├── plugin-test.sh │ ├── version_commands.sh │ ├── install.sh │ └── reshim.sh └── utils.sh ├── completions ├── _asdf ├── asdf.bash └── asdf.fish ├── .travis.yml ├── asdf.fish ├── asdf.sh ├── test ├── utils.bats ├── get_asdf_config_value.bats └── version_commands.bats ├── ballad-of-asdf.md ├── LICENSE ├── help.txt ├── bin ├── private │ └── asdf-exec └── asdf ├── Vagrantfile ├── docs └── creating-plugins.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | installs 2 | plugins 3 | shims 4 | .vagrant 5 | 6 | -------------------------------------------------------------------------------- /defaults: -------------------------------------------------------------------------------- 1 | # enables the use of .ruby-version like files used by other version managers 2 | legacy_version_file = no 3 | -------------------------------------------------------------------------------- /lib/commands/help.sh: -------------------------------------------------------------------------------- 1 | help_command () { 2 | echo "version: $(asdf_version)" 3 | echo "" 4 | cat $(asdf_dir)/help.txt 5 | } 6 | -------------------------------------------------------------------------------- /completions/_asdf: -------------------------------------------------------------------------------- 1 | #compdef asdf 2 | 3 | _arguments "1: :(plugin-add plugin-list plugin-remove plugin-update install uninstall which where list list-all reshim)" 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: bats test 3 | before_script: 4 | - git clone https://github.com/sstephenson/bats.git /tmp/bats 5 | - export PATH=/tmp/bats/bin:$PATH 6 | os: 7 | - linux 8 | - osx 9 | -------------------------------------------------------------------------------- /lib/commands/plugin-remove.sh: -------------------------------------------------------------------------------- 1 | plugin_remove_command() { 2 | local plugin_name=$1 3 | local plugin_path=$(get_plugin_path $plugin_name) 4 | 5 | rm -rf $plugin_path 6 | rm -rf $(asdf_dir)/installs/${plugin_name} 7 | } 8 | -------------------------------------------------------------------------------- /asdf.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | set -l asdf_dir (dirname (status -f)) 4 | 5 | # we get an ugly warning when setting the path if shims does not exist 6 | mkdir -p $asdf_dir/shims 7 | 8 | set -xg PATH $asdf_dir/bin $asdf_dir/shims $PATH 9 | -------------------------------------------------------------------------------- /lib/commands/plugin-list.sh: -------------------------------------------------------------------------------- 1 | plugin_list_command() { 2 | local plugins_path=$(get_plugin_path) 3 | 4 | if ls $plugins_path &> /dev/null; then 5 | for plugin_path in $plugins_path/* ; do 6 | echo "$(basename $plugin_path)" 7 | done 8 | else 9 | echo 'Oohes nooes ~! No plugins installed' 10 | fi 11 | } 12 | -------------------------------------------------------------------------------- /lib/commands/list-all.sh: -------------------------------------------------------------------------------- 1 | list_all_command() { 2 | local plugin_name=$1 3 | local plugin_path=$(get_plugin_path $plugin_name) 4 | check_if_plugin_exists $plugin_path 5 | 6 | local versions=$(bash ${plugin_path}/bin/list-all) 7 | 8 | IFS=' ' read -a versions_list <<< "$versions" 9 | 10 | for version in "${versions_list[@]}" 11 | do 12 | echo "${version}" 13 | done 14 | } 15 | -------------------------------------------------------------------------------- /lib/commands/which.sh: -------------------------------------------------------------------------------- 1 | which_command() { 2 | local plugin_name=$1 3 | local plugin_path=$(get_plugin_path $plugin_name) 4 | check_if_plugin_exists $plugin_path 5 | 6 | full_version=$(get_preset_version_for $plugin_name) 7 | 8 | if [ "$full_version" == "" ]; then 9 | echo "No version set for ${plugin_name}" 10 | exit -1 11 | else 12 | echo "$full_version" 13 | exit 0 14 | fi 15 | } 16 | -------------------------------------------------------------------------------- /asdf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "${BASH_SOURCE[0]}" != "" ]; then 4 | current_script_path=${BASH_SOURCE[0]} 5 | else 6 | current_script_path=$0 7 | fi 8 | 9 | asdf_dir=$(cd $(dirname $current_script_path); echo $(pwd)) 10 | export PATH="${asdf_dir}/bin:${asdf_dir}/shims:$PATH" 11 | 12 | if [ -n "$ZSH_VERSION" ]; then 13 | fpath=(${asdf_dir}/completions $fpath) 14 | autoload -U compinit 15 | compinit 16 | fi 17 | -------------------------------------------------------------------------------- /lib/commands/plugin-push.sh: -------------------------------------------------------------------------------- 1 | plugin_push_command() { 2 | local plugin_name=$1 3 | if [ "$plugin_name" = "--all" ]; then 4 | for dir in $(asdf_dir)/plugins/*; do 5 | echo "Pushing $(basename $dir)..." 6 | (cd "$dir" && git push) 7 | done 8 | else 9 | local plugin_path=$(get_plugin_path $plugin_name) 10 | check_if_plugin_exists $plugin_path 11 | echo "Pushing $plugin_name..." 12 | (cd $plugin_path; git push) 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /lib/commands/plugin-update.sh: -------------------------------------------------------------------------------- 1 | plugin_update_command() { 2 | local plugin_name=$1 3 | if [ "$plugin_name" = "--all" ]; then 4 | for dir in $(asdf_dir)/plugins/*; do 5 | echo "Updating $(basename $dir)..." 6 | (cd "$dir" && git pull) 7 | done 8 | else 9 | local plugin_path=$(get_plugin_path $plugin_name) 10 | check_if_plugin_exists $plugin_path 11 | echo "Updating $plugin_name..." 12 | (cd $plugin_path; git pull) 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /lib/commands/plugin-add.sh: -------------------------------------------------------------------------------- 1 | plugin_add_command() { 2 | local plugin_name=$1 3 | local source_url=$2 4 | local plugin_path=$(get_plugin_path $plugin_name) 5 | 6 | mkdir -p $(asdf_dir)/plugins 7 | 8 | if [ -d $plugin_path ]; then 9 | echo "Plugin named $plugin_name already added" 10 | exit 1 11 | else 12 | git clone $source_url $plugin_path 13 | if [ $? -eq 0 ]; then 14 | chmod +x $plugin_path/bin/* 15 | else 16 | exit 1 17 | fi 18 | fi 19 | } 20 | -------------------------------------------------------------------------------- /lib/commands/list.sh: -------------------------------------------------------------------------------- 1 | list_command() { 2 | local plugin_name=$1 3 | local plugin_path=$(get_plugin_path $plugin_name) 4 | check_if_plugin_exists $plugin_path 5 | 6 | local plugin_installs_path=$(asdf_dir)/installs/${plugin_name} 7 | 8 | if [ -d $plugin_installs_path ]; then 9 | #TODO check if dir is empty and show no-installed-versions msg 10 | for install in ${plugin_installs_path}/*/; do 11 | echo "$(basename $install)" 12 | done 13 | else 14 | echo 'Oohes nooes ~! No versions installed' 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /lib/commands/where.sh: -------------------------------------------------------------------------------- 1 | where_command() { 2 | local plugin_name=$1 3 | local full_version=$2 4 | local plugin_path=$(get_plugin_path $plugin_name) 5 | check_if_plugin_exists $plugin_path 6 | 7 | IFS=':' read -a version_info <<< "$full_version" 8 | if [ "${version_info[0]}" = "ref" ]; then 9 | local install_type="${version_info[0]}" 10 | local version="${version_info[1]}" 11 | else 12 | local install_type="version" 13 | local version="${version_info[0]}" 14 | fi 15 | 16 | local install_path=$(get_install_path $plugin_name $install_type $version) 17 | 18 | if [ -d $install_path ]; then 19 | echo $install_path 20 | exit 0 21 | else 22 | echo "Version not installed" 23 | exit 1 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /test/utils.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | . $(dirname $BATS_TEST_DIRNAME)/lib/utils.sh 4 | 5 | setup() { 6 | ASDF_DIR=$(mktemp -dt asdf.XXXX) 7 | } 8 | 9 | teardown() { 10 | rm -rf $ASDF_DIR 11 | unset ASDF_DIR 12 | } 13 | 14 | @test "check_if_version_exists should exit with 1 if plugin does not exist" { 15 | mkdir -p $ASDF_DIR/installs 16 | run check_if_version_exists "foo" "1.0.0" 17 | [ "$status" -eq 1 ] 18 | [ "$output" = "version 1.0.0 is not installed for foo" ] 19 | } 20 | 21 | @test "check_if_version_exists should exit with 1 if version does not exist" { 22 | mkdir -p $ASDF_DIR/installs/foo 23 | run check_if_version_exists "foo" "1.0.0" 24 | [ "$status" -eq 1 ] 25 | [ "$output" = "version 1.0.0 is not installed for foo" ] 26 | } 27 | 28 | @test "check_if_version_exists should be noop if version exists" { 29 | mkdir -p $ASDF_DIR/installs/foo/1.0.0 30 | run check_if_version_exists "foo" "1.0.0" 31 | [ "$status" -eq 0 ] 32 | [ "$output" = "" ] 33 | } 34 | -------------------------------------------------------------------------------- /lib/commands/uninstall.sh: -------------------------------------------------------------------------------- 1 | uninstall_command() { 2 | local plugin_name=$1 3 | local full_version=$2 4 | local plugin_path=$(get_plugin_path $plugin_name) 5 | 6 | check_if_plugin_exists $plugin_path 7 | 8 | IFS=':' read -a version_info <<< "$full_version" 9 | if [ "${version_info[0]}" = "ref" ]; then 10 | local install_type="${version_info[0]}" 11 | local version="${version_info[1]}" 12 | else 13 | local install_type="version" 14 | local version="${version_info[0]}" 15 | fi 16 | 17 | local install_path=$(get_install_path $plugin_name $install_type $version) 18 | 19 | if [ ! -d $install_path ]; then 20 | display_error "No such version" 21 | exit 1 22 | fi 23 | 24 | if [ -f ${plugin_path}/bin/uninstall ]; then 25 | ( 26 | export ASDF_INSTALL_TYPE=$install_type 27 | export ASDF_INSTALL_VERSION=$version 28 | export ASDF_INSTALL_PATH=$install_path 29 | bash ${plugin_path}/bin/uninstall 30 | ) 31 | else 32 | rm -rf $install_path 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /ballad-of-asdf.md: -------------------------------------------------------------------------------- 1 | # Ballad of asdf 2 | 3 | > Once upon a time there was a programming language 4 | There were many versions of it 5 | So people wrote a version manager for it 6 | To switch between versions for projects 7 | Different, old, new. 8 | 9 | > Then there came more programming languages 10 | So there came more version managers 11 | And many commands for them 12 | 13 | > I installed a lot of them 14 | I learnt a lot of commands 15 | 16 | > Then I said, just one more version manager 17 | Which I will write instead 18 | 19 | > So, there came another version manager 20 | **asdf version manager** - 21 | 22 | > A version manager so extendable 23 | for which anyone can create a plugin 24 | To support their favourite language 25 | No more installing more version managers 26 | Or learning more commands 27 | 28 | --- 29 | 30 | *This was the mail I wrote to a few friends to tell them about the project. Thanks to [@roshanvid](http://twitter.com/roshanvid) for suggesting that this go into the readme* 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Akash Manohar J 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/get_asdf_config_value.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | . $(dirname $BATS_TEST_DIRNAME)/lib/utils.sh 4 | 5 | setup() { 6 | AZDF_CONFIG_FILE=$BATS_TMPDIR/asdfrc 7 | cat > $AZDF_CONFIG_FILE <<-EOM 8 | key1 = value1 9 | legacy_version_file = yes 10 | EOM 11 | 12 | AZDF_CONFIG_DEFAULT_FILE=$BATS_TMPDIR/asdfrc_defaults 13 | cat > $AZDF_CONFIG_DEFAULT_FILE <<-EOM 14 | # i have a comment, it's ok 15 | key2 = value2 16 | legacy_version_file = no 17 | EOM 18 | } 19 | 20 | teardown() { 21 | rm $AZDF_CONFIG_FILE 22 | rm $AZDF_CONFIG_DEFAULT_FILE 23 | unset AZDF_CONFIG_DEFAULT_FILE 24 | unset AZDF_CONFIG_FILE 25 | } 26 | 27 | @test "get_config returns default when config file does not exist" { 28 | result=$(AZDF_CONFIG_FILE="/some/fake/path" get_asdf_config_value "legacy_version_file") 29 | [ "$result" = "no" ] 30 | } 31 | 32 | @test "get_config returns default value when the key does not exist" { 33 | [ $(get_asdf_config_value "key2") = "value2" ] 34 | } 35 | 36 | @test "get_config returns config file value when key exists" { 37 | [ $(get_asdf_config_value "key1") = "value1" ] 38 | [ $(get_asdf_config_value "legacy_version_file") = "yes" ] 39 | } 40 | -------------------------------------------------------------------------------- /completions/asdf.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | _asdf () { 4 | local cur=${COMP_WORDS[COMP_CWORD]} 5 | local cmd=${COMP_WORDS[1]} 6 | local prev=${COMP_WORDS[COMP_CWORD-1]} 7 | local plugins=$(asdf plugin-list | tr '\n' ' ') 8 | 9 | COMPREPLY=() 10 | 11 | case "$cmd" in 12 | plugin-update) 13 | COMPREPLY=($(compgen -W "$plugins --all" -- $cur)) 14 | ;; 15 | plugin-remove|which|list|list-all) 16 | COMPREPLY=($(compgen -W "$plugins" -- $cur)) 17 | ;; 18 | install) 19 | if [[ "$plugins" == *"$prev"* ]] ; then 20 | local versions=$(asdf list-all $prev) 21 | COMPREPLY=($(compgen -W "$versions" -- $cur)) 22 | else 23 | COMPREPLY=($(compgen -W "$plugins" -- $cur)) 24 | fi 25 | ;; 26 | uninstall|where|reshim) 27 | if [[ "$plugins" == *"$prev"* ]] ; then 28 | local versions=$(asdf list $prev) 29 | COMPREPLY=($(compgen -W "$versions" -- $cur)) 30 | else 31 | COMPREPLY=($(compgen -W "$plugins" -- $cur)) 32 | fi 33 | ;; 34 | *) 35 | local cmds='plugin-add plugin-list plugin-remove plugin-update install uninstall which where list list-all reshim' 36 | COMPREPLY=($(compgen -W "$cmds" -- $cur)) 37 | ;; 38 | esac 39 | 40 | return 0 41 | } 42 | 43 | complete -F _asdf asdf 44 | -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | MANAGE PLUGINS 2 | asdf plugin-add Add git repo as plugin 3 | asdf plugin-list List installed plugins 4 | asdf plugin-remove Remove plugin and package versions 5 | asdf plugin-update Update plugin 6 | asdf plugin-update --all Update all plugins 7 | 8 | 9 | MANAGE PACKAGES 10 | asdf install Install a specific version of a package or, 11 | with no arguments, install all the package 12 | versions listed in the .tool-versions file 13 | asdf uninstall Remove a specific version of a package 14 | asdf which Display version set or being used for package 15 | asdf where Display install path for an installed version 16 | asdf local [name] Display a package local version 17 | asdf local Set the package local version 18 | asdf global [name] Display a package global version 19 | asdf global Set the package global version 20 | asdf list List installed versions of a package 21 | asdf list-all List all versions of a package 22 | 23 | 24 | UTILS 25 | asdf reshim Recreate shims for version of a package 26 | 27 | 28 | "Late but latest" 29 | -- Rajinikanth 30 | -------------------------------------------------------------------------------- /lib/commands/plugin-test.sh: -------------------------------------------------------------------------------- 1 | fail_test() { 2 | echo "FAILED: $1" 3 | rm -rf $ASDF_DIR 4 | exit 1 5 | } 6 | 7 | plugin_test_command() { 8 | export ASDF_DIR=$(mktemp -dt asdf.XXXX) 9 | git clone https://github.com/asdf-vm/asdf.git $ASDF_DIR 10 | 11 | local plugin_name=$1 12 | local plugin_url=$2 13 | local plugin_command="${@:3}" 14 | 15 | if [ -z "$plugin_name" -o -z "$plugin_url" ]; then 16 | fail_test "please provide a plugin name and url" 17 | fi 18 | 19 | (asdf plugin-add $plugin_name $plugin_url) 20 | if [ $? -ne 0 ]; then 21 | fail_test "could not install $plugin_name from $plugin_url" 22 | fi 23 | 24 | if ! asdf plugin-list | grep $plugin_name > /dev/null; then 25 | fail_test "$plugin_name was not properly installed" 26 | fi 27 | 28 | read -a versions <<< $(asdf list-all $plugin_name) 29 | 30 | if [ $? -ne 0 ]; then 31 | fail_test "list-all exited with an error" 32 | fi 33 | 34 | if [ ${#versions} -eq 0 ]; then 35 | fail_test "list-all did not return any version" 36 | fi 37 | 38 | latest_version=${versions[${#versions[@]} - 1]} 39 | 40 | (asdf install $plugin_name $latest_version) 41 | 42 | if [ $? -ne 0 ]; then 43 | fail_test "install exited with an error" 44 | fi 45 | 46 | cd $ASDF_DIR 47 | (asdf local $plugin_name $latest_version) 48 | if [ $? -ne 0 ]; then 49 | fail_test "install did not add the requested version" 50 | fi 51 | 52 | (asdf reshim $plugin_name) 53 | if [ $? -ne 0 ]; then 54 | fail_test "could not reshim plugin" 55 | fi 56 | 57 | if [ -n "$plugin_command" ]; then 58 | (PATH="$ASDF_DIR/bin":"$ASDF_DIR/shims":$PATH eval "$plugin_command") 59 | exit_code=$? 60 | if [ $exit_code -ne 0 ]; then 61 | fail_test "$plugin_command failed with exit code $?" 62 | fi 63 | fi 64 | 65 | rm -rf $ASDF_DIR 66 | } 67 | -------------------------------------------------------------------------------- /lib/commands/version_commands.sh: -------------------------------------------------------------------------------- 1 | get_plugin_version() { 2 | local cmd=$1 3 | local file=$2 4 | local plugin=$3 5 | local legacy_version_file_support=$(get_asdf_config_value "legacy_version_file") 6 | local result 7 | 8 | if [ $cmd = "local" -a "$legacy_version_file_support" = "yes" -a \ 9 | \( ! -f $file -o $file = "$HOME/.tool-versions" \) ]; then 10 | result=$(get_tool_version_from_legacy_file $plugin $(pwd)) 11 | if [ -n "$result" ]; then 12 | echo $result 13 | exit 0 14 | fi 15 | fi 16 | 17 | if [ -f $file ]; then 18 | result=$(get_tool_version_from_file $file $plugin) 19 | fi 20 | 21 | if [ -n "$result" ]; then 22 | echo $result 23 | exit 0 24 | fi 25 | 26 | 27 | echo "version not set for $plugin" 28 | exit 1 29 | } 30 | 31 | version_command() { 32 | local cmd=$1 33 | 34 | local file 35 | if [ $cmd = "global" ]; then 36 | file=$HOME/.tool-versions 37 | else 38 | file=$(get_asdf_versions_file_path) 39 | if [ -z "$file" ]; then 40 | file=.tool-versions 41 | fi 42 | fi 43 | 44 | if [ $# -eq 1 -a ! -f $file ]; then 45 | echo $file does not exist 46 | exit 1 47 | fi 48 | 49 | if [ $# -eq 1 ]; then 50 | cat $file 51 | exit 0 52 | fi 53 | 54 | local plugin=$2 55 | 56 | check_if_plugin_exists $(get_plugin_path $plugin) 57 | 58 | if [ $# -eq 2 ]; then 59 | get_plugin_version $cmd $file $plugin 60 | fi 61 | 62 | local version=${@:3} 63 | 64 | if [ $cmd = "local" ]; then 65 | file=$(pwd)/.tool-versions 66 | fi 67 | 68 | check_if_version_exists $plugin $version 69 | 70 | if [ -f $file ] && grep $plugin $file > /dev/null; then 71 | sed -i -e "s/$plugin .*/$plugin $version/" $file 72 | else 73 | echo "$plugin $version" >> $file 74 | fi 75 | } 76 | 77 | local_command() { 78 | version_command "local" $@ 79 | } 80 | 81 | global_command() { 82 | version_command "global" $@ 83 | } 84 | -------------------------------------------------------------------------------- /lib/commands/install.sh: -------------------------------------------------------------------------------- 1 | install_command() { 2 | local plugin_name=$1 3 | local full_version=$2 4 | 5 | if [ "$plugin_name" = "" ] && [ "$full_version" = "" ]; then 6 | install_local_tool_versions 7 | else 8 | install_tool_version $plugin_name $full_version 9 | fi 10 | } 11 | 12 | 13 | install_local_tool_versions() { 14 | if [ -f $(pwd)/.tool-versions ]; then 15 | local asdf_versions_path=$(pwd)/.tool-versions 16 | 17 | while read tool_line; do 18 | IFS=' ' read -a tool_info <<< $tool_line 19 | local tool_name=$(echo "${tool_info[0]}" | xargs) 20 | local tool_version=$(echo "${tool_info[1]}" | xargs) 21 | 22 | if ! [[ -z "$tool_name" || -z "$tool_version" ]]; then 23 | install_tool_version $tool_name $tool_version 24 | fi 25 | done < $asdf_versions_path 26 | else 27 | echo "Either specify a tool & version in the command" 28 | echo "OR add .tool-versions file in this directory" 29 | exit 1 30 | fi 31 | } 32 | 33 | 34 | install_tool_version() { 35 | local plugin_name=$1 36 | local full_version=$2 37 | local plugin_path=$(get_plugin_path $plugin_name) 38 | check_if_plugin_exists $plugin_path 39 | 40 | 41 | IFS=':' read -a version_info <<< "$full_version" 42 | if [ "${version_info[0]}" = "ref" ]; then 43 | local install_type="${version_info[0]}" 44 | local version="${version_info[1]}" 45 | else 46 | local install_type="version" 47 | local version="${version_info[0]}" 48 | fi 49 | 50 | 51 | local install_path=$(get_install_path $plugin_name $install_type $version) 52 | if [ -d $install_path ]; then 53 | echo "$plugin_name $full_version is already installed" 54 | else 55 | ( 56 | export ASDF_INSTALL_TYPE=$install_type 57 | export ASDF_INSTALL_VERSION=$version 58 | export ASDF_INSTALL_PATH=$install_path 59 | mkdir $install_path 60 | bash ${plugin_path}/bin/install 61 | ) 62 | 63 | local exit_code=$? 64 | if [ $exit_code -eq 0 ]; then 65 | reshim_command $plugin_name $full_version 66 | else 67 | rm -rf $install_path 68 | exit 1 69 | fi 70 | fi 71 | } 72 | -------------------------------------------------------------------------------- /bin/private/asdf-exec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source $(dirname $(dirname $(dirname $0)))/lib/utils.sh 4 | 5 | plugin_name=$1 6 | executable_path=$2 7 | 8 | plugin_path=$(get_plugin_path $plugin_name) 9 | check_if_plugin_exists $plugin_path 10 | 11 | full_version=$(get_preset_version_for $plugin_name) 12 | 13 | if [ "$full_version" == "" ]; then 14 | echo "No version set for ${plugin_name}" 15 | exit -1 16 | fi 17 | 18 | IFS=' ' read -a versions <<< "$full_version" 19 | 20 | for version in "${versions[@]}"; do 21 | IFS=':' read -a version_info <<< "$version" 22 | 23 | if [ "${version_info[0]}" = "ref" ]; then 24 | install_type="${version_info[0]}" 25 | version="${version_info[1]}" 26 | install_path=$(get_install_path $plugin_name $install_type $version) 27 | elif [ "${version_info[0]}" = "path" ]; then 28 | # This is for people who have the local source already compiled 29 | # Like those who work on the language, etc 30 | # We'll allow specifying path:/foo/bar/project in .tool-versions 31 | # And then use the binaries there 32 | install_type="path" 33 | version="path" 34 | install_path="${version_info[1]}" 35 | else 36 | install_type="version" 37 | version="${version_info[0]}" 38 | install_path=$(get_install_path $plugin_name $install_type $version) 39 | fi 40 | 41 | 42 | if [ ! -d $install_path ]; then 43 | echo "$plugin_name $version not installed" 44 | exit 1 45 | fi 46 | 47 | if [ -f ${install_path}/${executable_path} ]; then 48 | if [ -f ${plugin_path}/bin/exec-env ]; then 49 | export ASDF_INSTALL_TYPE=$install_type 50 | export ASDF_INSTALL_VERSION=$version 51 | export ASDF_INSTALL_PATH=$install_path 52 | 53 | source ${plugin_path}/bin/exec-env 54 | 55 | # unset everything, we don't want to pollute 56 | unset ASDF_INSTALL_TYPE 57 | unset ASDF_INSTALL_VERSION 58 | unset ASDF_INSTALL_PATH 59 | exec ${install_path}/${executable_path} "${@:3}" 60 | else 61 | exec ${install_path}/${executable_path} "${@:3}" 62 | fi 63 | fi 64 | done 65 | 66 | echo "No such command in $full_version of $plugin_name" 67 | exit 1 68 | -------------------------------------------------------------------------------- /bin/asdf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source $(dirname $(dirname $0))/lib/utils.sh 4 | 5 | source $(dirname $(dirname $0))/lib/commands/help.sh 6 | source $(dirname $(dirname $0))/lib/commands/install.sh 7 | source $(dirname $(dirname $0))/lib/commands/uninstall.sh 8 | source $(dirname $(dirname $0))/lib/commands/which.sh 9 | source $(dirname $(dirname $0))/lib/commands/where.sh 10 | source $(dirname $(dirname $0))/lib/commands/version_commands.sh 11 | source $(dirname $(dirname $0))/lib/commands/list.sh 12 | source $(dirname $(dirname $0))/lib/commands/list-all.sh 13 | source $(dirname $(dirname $0))/lib/commands/reshim.sh 14 | source $(dirname $(dirname $0))/lib/commands/plugin-add.sh 15 | source $(dirname $(dirname $0))/lib/commands/plugin-list.sh 16 | source $(dirname $(dirname $0))/lib/commands/plugin-update.sh 17 | source $(dirname $(dirname $0))/lib/commands/plugin-remove.sh 18 | 19 | source $(dirname $(dirname $0))/lib/commands/plugin-push.sh 20 | source $(dirname $(dirname $0))/lib/commands/plugin-test.sh 21 | 22 | 23 | callback_args="${@:2}" 24 | 25 | case $1 in 26 | 27 | "--version") 28 | asdf_version $callback_args;; 29 | 30 | "help") 31 | help_command $callback_args;; 32 | 33 | "install") 34 | install_command $callback_args;; 35 | 36 | "uninstall") 37 | uninstall_command $callback_args;; 38 | 39 | "which") 40 | which_command $callback_args;; 41 | 42 | "where") 43 | where_command $callback_args;; 44 | 45 | "local") 46 | local_command $callback_args;; 47 | 48 | "global") 49 | global_command $callback_args;; 50 | 51 | "list") 52 | list_command $callback_args;; 53 | 54 | "list-all") 55 | list_all_command $callback_args;; 56 | 57 | "shim") 58 | shim_command $callback_args;; 59 | 60 | "reshim") 61 | reshim_command $callback_args;; 62 | 63 | "plugin-add") 64 | plugin_add_command $callback_args;; 65 | 66 | "plugin-list") 67 | plugin_list_command $callback_args;; 68 | 69 | "plugin-update") 70 | plugin_update_command $callback_args;; 71 | 72 | "plugin-remove") 73 | plugin_remove_command $callback_args;; 74 | 75 | # Undocumented commands for development 76 | "plugin-push") 77 | plugin_push_command $callback_args;; 78 | 79 | "plugin-test") 80 | plugin_test_command $callback_args;; 81 | 82 | *) 83 | help_command 84 | exit 1;; 85 | esac 86 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/trusty64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | 64 | # Enable provisioning with a shell script. Additional provisioners such as 65 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 66 | # documentation for more information about their specific syntax and use. 67 | # config.vm.provision "shell", inline: <<-SHELL 68 | # sudo apt-get update 69 | # sudo apt-get install -y apache2 70 | # SHELL 71 | end 72 | -------------------------------------------------------------------------------- /test/version_commands.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | . $(dirname $BATS_TEST_DIRNAME)/lib/utils.sh 4 | . $(dirname $BATS_TEST_DIRNAME)/lib/commands/version_commands.sh 5 | 6 | setup() { 7 | BASE_DIR=$(mktemp -dt asdf.XXXX) 8 | HOME=$BASE_DIR/home 9 | ASDF_DIR=$HOME/.asdf 10 | OTHER_DIR=$BASE_DIR/other 11 | mkdir -p $ASDF_DIR/plugins/foo $ASDF_DIR/plugins/bar $ASDF_DIR/installs/foo/1.0.0 $ASDF_DIR/installs/foo/1.1.0 $ASDF_DIR/installs/foo/1.2.0 $ASDF_DIR/installs/bar/1.0.0 $OTHER_DIR 12 | 13 | cd $OTHER_DIR 14 | echo 'foo 1.0.0' >> $HOME/.tool-versions 15 | echo 'foo 1.1.0' >> .tool-versions 16 | } 17 | 18 | teardown() { 19 | rm -rf $BASE_DIR 20 | } 21 | 22 | 23 | @test "local should emit an error when run in lookup mode and file does not exist" { 24 | rm .tool-versions 25 | run local_command 26 | [ "$status" -eq 1 ] 27 | [ "$output" = ".tool-versions does not exist" ] 28 | } 29 | 30 | @test "global should emit an error when run in lookup mode and file does not exist" { 31 | rm $HOME/.tool-versions 32 | run global_command "foo" 33 | [ "$status" -eq 1 ] 34 | [ "$output" = "version not set for foo" ] 35 | } 36 | 37 | @test "local should emit an error when plugin does not exist" { 38 | run local_command "inexistent" "1.0.0" 39 | [ "$status" -eq 1 ] 40 | [ "$output" = "No such plugin" ] 41 | } 42 | 43 | @test "local should emit an error when plugin version does not exist" { 44 | run local_command "foo" "0.0.1" 45 | [ "$status" -eq 1 ] 46 | [ "$output" = "version 0.0.1 is not installed for foo" ] 47 | } 48 | 49 | @test "local should return and set the local version" { 50 | 51 | run local_command 52 | [ "$status" -eq 0 ] 53 | [ "$output" = "foo 1.1.0" ] 54 | 55 | run local_command foo "1.2.0" 56 | 57 | run local_command foo 58 | [ "$status" -eq 0 ] 59 | [ "$output" = "1.2.0" ] 60 | 61 | run local_command bar 62 | [ "$status" -eq 1 ] 63 | 64 | run local_command bar 1.0.0 65 | [ "$status" -eq 0 ] 66 | 67 | run local_command bar 68 | [ "$status" -eq 0 ] 69 | [ "$output" = "1.0.0" ] 70 | 71 | rm .tool-versions 72 | run local_command foo 1.2.0 73 | [ -f .tool-versions ] 74 | 75 | run local_command foo 76 | [ "$status" -eq 0 ] 77 | [ "$output" = "1.2.0" ] 78 | run global_command foo 79 | [ "$output" = "1.0.0" ] 80 | } 81 | 82 | @test "local should fallback to legacy-file when enabled" { 83 | echo 'legacy_version_file = yes' > $HOME/.asdfrc 84 | mkdir -p $ASDF_DIR/plugins/foo/bin 85 | echo 'echo 1.0.0' > $ASDF_DIR/plugins/foo/bin/get-version-from-legacy-file 86 | rm .tool-versions 87 | 88 | run local_command foo 89 | 90 | [ "$status" -eq 0 ] 91 | [ "$output" = "1.0.0" ] 92 | } 93 | 94 | @test "local should ignore legacy-file when disabled" { 95 | mkdir -p $ASDF_DIR/plugins/foo/bin 96 | echo 'cat 1.0.0' > $ASDF_DIR/plugins/foo/bin/get-version-from-legacy-file 97 | rm .tool-versions 98 | 99 | run local_command foo 100 | 101 | [ "$status" -eq 1 ] 102 | [ "$output" = "version not set for foo" ] 103 | } 104 | 105 | 106 | @test "global should return and set the global version" { 107 | run global_command 108 | [ "$status" -eq 0 ] 109 | [ "$output" = "foo 1.0.0" ] 110 | 111 | run global_command foo 1.2.0 112 | [ "$status" -eq 0 ] 113 | 114 | run global_command foo 115 | [ "$status" -eq 0 ] 116 | [ "$output" = "1.2.0" ] 117 | } 118 | -------------------------------------------------------------------------------- /docs/creating-plugins.md: -------------------------------------------------------------------------------- 1 | ## Creating plugins 2 | 3 | A plugin is a git repo, with a couple executable scripts, to support versioning another language or tool. These scripts are run when `list-all`, `install` or `uninstall` commands are run. You can set or unset env vars and do anything required to setup the environment for the tool. 4 | 5 | ### Required scripts 6 | 7 | * `bin/list-all` - lists all installable versions 8 | * `bin/install` - installs the specified version 9 | 10 | 11 | All scripts except `bin/list-all` will have access to the following env vars to act upon: 12 | 13 | * `ASDF_INSTALL_TYPE` - `version` or `ref` 14 | * `ASDF_INSTALL_VERSION` - if `ASDF_INSTALL_TYPE` is `version` then this will be the version number. Else it will be the git ref that is passed. Might point to a tag/commit/branch on the repo. 15 | * `ASDF_INSTALL_PATH` - the dir where the it *has been* installed (or *should* be installed in case of the `bin/install` script) 16 | 17 | 18 | #### bin/list-all 19 | 20 | Must print a string with a space-seperated list of versions. Example output would be the following: 21 | 22 | ``` 23 | 1.0.1 1.0.2 1.3.0 1.4 24 | ``` 25 | 26 | #### bin/install 27 | 28 | This script should install the version, in the path mentioned in `ASDF_INSTALL_PATH` 29 | 30 | 31 | ### Optional scripts 32 | 33 | #### bin/list-bin-paths 34 | 35 | List executables for the specified version of the tool. Must print a string with a space-seperated list of dir paths that contain executables. The paths must be relative to the install path passed. Example output would be: 36 | 37 | ``` 38 | bin tools veggies 39 | ``` 40 | 41 | This will instruct asdf to create shims for the files in `/bin`, `/tools` and `/veggies` 42 | 43 | If this script is not specified, asdf will look for the `bin` dir in an installation and create shims for those. 44 | 45 | #### bin/exec-env 46 | 47 | Setup the env to run the binaries in the package. 48 | 49 | #### bin/uninstall 50 | 51 | Uninstalls a specific version of a tool. 52 | 53 | #### bin/get_version_from_legacy_file 54 | 55 | Prints the version of the tool to use if a legacy version file is found in the current directory (e.g. rbenv's `.ruby-version`). Current directory is passed as the only argument to this script. 56 | 57 | ### Custom shim templates 58 | 59 | **PLEASE use this feature only if absolutely required** 60 | 61 | asdf allows custom shim templates. For an executable called `foo`, if there's a `shims/foo` file in the plugin, then asdf will copy that file instead of using it's standard shim template. 62 | 63 | This must be used wisely. For now AFAIK, it's only being used in the Elixir plugin, because an executable is also read as an Elixir file apart from just being an executable. Which makes it not possible to use the standard bash shim. 64 | 65 | ## Testing plugins 66 | 67 | `asdf` contains the `plugin-test` command to test your plugin. 68 | You can use it as follows 69 | 70 | ```sh 71 | asdf plugin-test [test-command] 72 | ``` 73 | 74 | The two first arguments are required. A command can also be passed to check it runs correctly. 75 | For example to test the NodeJS plugin, we could run 76 | 77 | ```sh 78 | asdf plugin-test nodejs https://github.com/asdf-vm/asdf-nodejs.git 'node --version' 79 | ``` 80 | 81 | We strongly recommend you test your plugin on TravisCI, to make sure it works 82 | on both Linux and OSX. 83 | 84 | Here is a sample `.travis.yml` file, customize it to your needs 85 | 86 | ```yaml 87 | language: c 88 | script: asdf plugin-test nodejs https://github.com/asdf-vm/asdf-nodejs.git 'node --version' 89 | before_script: 90 | - git clone https://github.com/asdf-vm/asdf.git asdf 91 | - . asdf/asdf.sh 92 | os: 93 | - linux 94 | - osx 95 | ``` 96 | -------------------------------------------------------------------------------- /lib/commands/reshim.sh: -------------------------------------------------------------------------------- 1 | shim_command() { 2 | local plugin_name=$1 3 | local executable_path=$2 4 | local plugin_path=$(get_plugin_path $plugin_name) 5 | check_if_plugin_exists $plugin_path 6 | ensure_shims_dir 7 | 8 | generate_shim_for_executable $plugin_name $executable_path 9 | } 10 | 11 | reshim_command() { 12 | local plugin_name=$1 13 | local full_version=$2 14 | local plugin_path=$(get_plugin_path $plugin_name) 15 | check_if_plugin_exists $plugin_path 16 | ensure_shims_dir 17 | 18 | if [ "$full_version" != "" ]; then 19 | # generate for the whole package version 20 | generate_shims_for_version $plugin_name $full_version 21 | else 22 | # generate for all versions of the package 23 | local plugin_installs_path=$(asdf_dir)/installs/${plugin_name} 24 | 25 | for install in ${plugin_installs_path}/*/; do 26 | local full_version_name=$(echo $(basename $install) | sed 's/ref\-/ref\:/') 27 | generate_shims_for_version $plugin_name $full_version_name 28 | done 29 | fi 30 | } 31 | 32 | 33 | ensure_shims_dir() { 34 | # Create shims dir if doesn't exist 35 | if [ ! -d $(asdf_dir)/shims ]; then 36 | mkdir $(asdf_dir)/shims 37 | fi 38 | } 39 | 40 | 41 | write_shim_script() { 42 | local plugin_name=$1 43 | local executable_path=$2 44 | local executable_name=$(basename $executable_path) 45 | local plugin_shims_path=$(get_plugin_path $plugin_name)/shims 46 | local shim_path=$(asdf_dir)/shims/$executable_name 47 | 48 | if [ -f $plugin_shims_path/$executable_name ]; then 49 | cp $plugin_shims_path/$executable_name $shim_path 50 | else 51 | echo """#!/usr/bin/env bash 52 | exec $(asdf_dir)/bin/private/asdf-exec ${plugin_name} ${executable_path} \"\$@\" 53 | """ > $shim_path 54 | fi 55 | 56 | chmod +x $shim_path 57 | } 58 | 59 | 60 | generate_shim_for_executable() { 61 | local plugin_name=$1 62 | local executable=$2 63 | local plugin_path=$(get_plugin_path $plugin_name) 64 | 65 | check_if_plugin_exists $plugin_path 66 | 67 | IFS=':' read -a version_info <<< "$full_version" 68 | if [ "${version_info[0]}" = "ref" ]; then 69 | local install_type="${version_info[0]}" 70 | local version="${version_info[1]}" 71 | else 72 | local install_type="version" 73 | local version="${version_info[0]}" 74 | fi 75 | 76 | write_shim_script $plugin_name $executable 77 | } 78 | 79 | 80 | generate_shims_for_version() { 81 | local plugin_name=$1 82 | local full_version=$2 83 | local plugin_path=$(get_plugin_path $plugin_name) 84 | check_if_plugin_exists $plugin_path 85 | 86 | IFS=':' read -a version_info <<< "$full_version" 87 | if [ "${version_info[0]}" = "ref" ]; then 88 | local install_type="${version_info[0]}" 89 | local version="${version_info[1]}" 90 | else 91 | local install_type="version" 92 | local version="${version_info[0]}" 93 | fi 94 | 95 | local install_path=$(get_install_path $plugin_name $install_type $version) 96 | 97 | if [ -f ${plugin_path}/bin/list-bin-paths ]; then 98 | local space_seperated_list_of_bin_paths=$( 99 | export ASDF_INSTALL_TYPE=$install_type 100 | export ASDF_INSTALL_VERSION=$version 101 | export ASDF_INSTALL_PATH=$install_path 102 | bash ${plugin_path}/bin/list-bin-paths 103 | ) 104 | else 105 | local space_seperated_list_of_bin_paths="bin" 106 | fi 107 | 108 | IFS=' ' read -a all_bin_paths <<< "$space_seperated_list_of_bin_paths" 109 | 110 | for bin_path in "${all_bin_paths[@]}"; do 111 | for executable_file in $install_path/$bin_path/*; do 112 | # because just $executable_file gives absolute path; We don't want version hardcoded in shim 113 | local executable_path_relative_to_install_path=$bin_path/$(basename $executable_file) 114 | if [ -x "$executable_file" ]; then 115 | write_shim_script $plugin_name $executable_path_relative_to_install_path 116 | fi 117 | done 118 | done 119 | } 120 | -------------------------------------------------------------------------------- /completions/asdf.fish: -------------------------------------------------------------------------------- 1 | function __fish_asdf_needs_command 2 | set -l cmd (commandline -opc) 3 | if test (count $cmd) -eq 1 4 | return 0 5 | end 6 | return 1 7 | end 8 | 9 | function __fish_asdf_using_command -a current_command 10 | set -l cmd (commandline -opc) 11 | if test (count $cmd) -gt 1 12 | if test $current_command = $cmd[2] 13 | return 0 14 | end 15 | end 16 | return 1 17 | end 18 | 19 | function __fish_asdf_arg_number -a number 20 | set -l cmd (commandline -opc) 21 | test (count $cmd) -eq $number 22 | end 23 | 24 | function __fish_asdf_arg_at -a number 25 | set -l cmd (commandline -opc) 26 | echo $cmd[$number] 27 | end 28 | 29 | set -l official_plugins ruby erlang nodejs elixir 30 | 31 | 32 | # plugin-add completion 33 | complete -f -c asdf -n '__fish_asdf_needs_command' -a plugin-add -d "Add git repo as plugin" 34 | complete -f -c asdf -n '__fish_asdf_using_command plugin-add; and __fish_asdf_arg_number 2' -a (echo $official_plugins) 35 | complete -f -c asdf -n '__fish_asdf_using_command plugin-add; and __fish_asdf_arg_number 3' -a '(echo https://github.com/asdf-vm/asdf-(__fish_asdf_arg_at 3).git)' 36 | 37 | # plugin-list completion 38 | complete -f -c asdf -n '__fish_asdf_needs_command' -a plugin-list -d "List installed plugins" 39 | 40 | # plugin-remove completion 41 | complete -f -c asdf -n '__fish_asdf_needs_command' -a plugin-remove -d "Remove plugin and package versions" 42 | complete -f -c asdf -n '__fish_asdf_using_command plugin-remove; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' -A 43 | 44 | # plugin-update completion 45 | complete -f -c asdf -n '__fish_asdf_needs_command' -a plugin-update -d "Update plugin" 46 | complete -f -c asdf -n '__fish_asdf_using_command plugin-update; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' -A 47 | complete -f -c asdf -n '__fish_asdf_using_command plugin-update; and __fish_asdf_arg_number 2' -a --all -A 48 | 49 | # install completion 50 | complete -f -c asdf -n '__fish_asdf_needs_command' -a install -d "Install a specific version of a package" 51 | complete -f -c asdf -n '__fish_asdf_using_command install; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 52 | complete -f -c asdf -n '__fish_asdf_using_command install; and __fish_asdf_arg_number 3' -a '(asdf list-all (__fish_asdf_arg_at 3))' 53 | 54 | # uninstall completion 55 | complete -f -c asdf -n '__fish_asdf_needs_command' -a uninstall -d "Remove a specific version of a package" 56 | complete -f -c asdf -n '__fish_asdf_using_command uninstall; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 57 | complete -f -c asdf -n '__fish_asdf_using_command uninstall; and __fish_asdf_arg_number 3' -a '(asdf list (__fish_asdf_arg_at 3))' 58 | 59 | # which completion 60 | complete -f -c asdf -n '__fish_asdf_needs_command' -a which -d "Display version set or being used for package" 61 | complete -f -c asdf -n '__fish_asdf_using_command which; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 62 | 63 | # where completion 64 | complete -f -c asdf -n '__fish_asdf_needs_command' -a where -d "Display install path for an installed version" 65 | complete -f -c asdf -n '__fish_asdf_using_command where; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 66 | complete -f -c asdf -n '__fish_asdf_using_command where; and __fish_asdf_arg_number 3' -a '(asdf list (__fish_asdf_arg_at 3))' 67 | 68 | # list completion 69 | complete -f -c asdf -n '__fish_asdf_needs_command' -a list -d "List installed versions of a package" 70 | complete -f -c asdf -n '__fish_asdf_using_command list; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 71 | 72 | # list-all completion 73 | complete -f -c asdf -n '__fish_asdf_needs_command' -a list-all -d "List all versions of a package" 74 | complete -f -c asdf -n '__fish_asdf_using_command list-all; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 75 | 76 | # reshim completion 77 | complete -f -c asdf -n '__fish_asdf_needs_command' -a reshim -d "Recreate shims for version of a package" 78 | complete -f -c asdf -n '__fish_asdf_using_command reshim; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 79 | complete -f -c asdf -n '__fish_asdf_using_command reshim; and __fish_asdf_arg_number 3' -a '(asdf list (__fish_asdf_arg_at 3))' 80 | 81 | # local completion 82 | complete -f -c asdf -n '__fish_asdf_needs_command' -a local -d "Set local version for a plugin" 83 | complete -f -c asdf -n '__fish_asdf_using_command local; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 84 | complete -f -c asdf -n '__fish_asdf_using_command local; and test (count (commandline -opc)) -gt 2' -a '(asdf list (__fish_asdf_arg_at 3))' 85 | 86 | # global completion 87 | complete -f -c asdf -n '__fish_asdf_needs_command' -a global -d "Set global version for a plugin" 88 | complete -f -c asdf -n '__fish_asdf_using_command global; and __fish_asdf_arg_number 2' -a '(asdf plugin-list)' 89 | complete -f -c asdf -n '__fish_asdf_using_command global; and test (count (commandline -opc)) -gt 2' -a '(asdf list (__fish_asdf_arg_at 3))' 90 | 91 | # misc 92 | complete -f -c asdf -n '__fish_asdf_needs_command' -l "help" -d "Displays help" 93 | complete -f -c asdf -n '__fish_asdf_needs_command' -l "version" -d "Displays asdf version" 94 | -------------------------------------------------------------------------------- /lib/utils.sh: -------------------------------------------------------------------------------- 1 | asdf_version() { 2 | echo "0.1" 3 | } 4 | 5 | 6 | asdf_dir() { 7 | if [ -z $ASDF_DIR ]; then 8 | local current_script_path=${BASH_SOURCE[0]} 9 | export ASDF_DIR=$(cd $(dirname $(dirname $current_script_path)); echo $(pwd)) 10 | fi 11 | 12 | echo $ASDF_DIR 13 | } 14 | 15 | 16 | get_install_path() { 17 | local plugin=$1 18 | local install_type=$2 19 | local version=$3 20 | mkdir -p $(asdf_dir)/installs/${plugin} 21 | 22 | if [ $install_type = "version" ] 23 | then 24 | echo $(asdf_dir)/installs/${plugin}/${version} 25 | else 26 | echo $(asdf_dir)/installs/${plugin}/${install_type}-${version} 27 | fi 28 | } 29 | 30 | 31 | check_if_plugin_exists() { 32 | if [ ! -d $1 ] 33 | then 34 | display_error "No such plugin" 35 | exit 1 36 | fi 37 | } 38 | 39 | check_if_version_exists() { 40 | local plugin=$1 41 | local version=$2 42 | local version_dir=$(asdf_dir)/installs/$plugin/$version 43 | if [ ! -d $version_dir ]; then 44 | display_error "version $version is not installed for $plugin" 45 | exit 1 46 | fi 47 | } 48 | 49 | 50 | get_version_part() { 51 | IFS='@' read -a version_info <<< "$1" 52 | echo ${version_info[$2]} 53 | } 54 | 55 | 56 | get_plugin_path() { 57 | echo $(asdf_dir)/plugins/$1 58 | } 59 | 60 | 61 | display_error() { 62 | echo $1 63 | } 64 | 65 | 66 | get_asdf_versions_file_path() { 67 | local asdf_tool_versions_path="" 68 | local search_path=$(pwd) 69 | 70 | while [ "$search_path" != "/" ]; do 71 | if [ -f "$search_path/.tool-versions" ]; then 72 | asdf_tool_versions_path="$search_path/.tool-versions" 73 | break 74 | fi 75 | search_path=$(dirname "$search_path") 76 | done 77 | 78 | echo $asdf_tool_versions_path 79 | } 80 | 81 | 82 | get_preset_version_for() { 83 | local tool_name=$1 84 | local asdf_versions_path=$(get_asdf_versions_file_path) 85 | local matching_tool_version="" 86 | local legacy_version_file_support=$(get_asdf_config_value "legacy_version_file") 87 | 88 | # If .tool-versions is not in the working directory 89 | if [ "$asdf_versions_path" != "$(pwd)/.tool-versions" ] && [ "$legacy_version_file_support" = "yes" ]; then 90 | # Check for legacy version file 91 | matching_tool_version=$(get_tool_version_from_legacy_file $tool_name $(pwd)) 92 | fi 93 | 94 | # If no legacy version file, see if we can use a .tool-versions file higher in the directory tree 95 | if [ "$matching_tool_version" = "" ] && [ "$asdf_versions_path" != "" ]; then 96 | matching_tool_version=$(get_tool_version_from_file $asdf_versions_path $tool_name) 97 | fi 98 | 99 | 100 | # If there's no global .tool-versions file 101 | # then we create it and return blank 102 | if [ "$matching_tool_version" = "" ]; then 103 | local global_tool_versions_path=$HOME/.tool-versions 104 | if [ ! -f $global_tool_versions_path ]; then 105 | touch $global_tool_versions_path 106 | else 107 | matching_tool_version=$(get_tool_version_from_file $global_tool_versions_path $tool_name) 108 | fi 109 | fi 110 | 111 | 112 | echo $matching_tool_version 113 | } 114 | 115 | 116 | get_tool_version_from_file() { 117 | local asdf_versions_path=$1 118 | local tool_name=$2 119 | local get_all_versions=$3 120 | local matching_tool_version="" 121 | 122 | local read_done=false 123 | until $read_done; do 124 | read tool_line || read_done=true 125 | 126 | if $read_done ; then 127 | break; 128 | fi 129 | 130 | IFS=' ' read -a tool_info <<< $tool_line 131 | local t_name=$(echo "${tool_info[0]}" | xargs) 132 | local t_version=$(echo "${tool_info[@]:1}" | xargs) 133 | 134 | if [ "$t_name" = "$tool_name" ] 135 | then 136 | matching_tool_version=$t_version 137 | break; 138 | fi 139 | done < $asdf_versions_path 140 | 141 | echo $matching_tool_version 142 | } 143 | 144 | 145 | get_tool_version_from_legacy_file() { 146 | local plugin_name=$1 147 | local directory=$2 148 | local legacy_tool_version="" 149 | local plugin_path=$(get_plugin_path $plugin_name) 150 | local legacy_version_script="${plugin_path}/bin/get-version-from-legacy-file" 151 | check_if_plugin_exists $plugin_path 152 | 153 | if [ -f $legacy_version_script ]; then 154 | local legacy_tool_version=$(bash $legacy_version_script $directory) 155 | fi 156 | 157 | # Should return the version/tag/commit/branch/path 158 | echo $legacy_tool_version 159 | } 160 | 161 | 162 | get_asdf_config_value_from_file() { 163 | local config_path=$1 164 | local key=$2 165 | 166 | if [ ! -f $config_path ]; then 167 | return 0 168 | fi 169 | 170 | local result=$(grep -E "^\s*$key\s*=" $config_path | awk -F '=' '{ gsub(/ /, "", $2); print $2 }') 171 | if [ -n "$result" ]; then 172 | echo $result 173 | fi 174 | } 175 | 176 | get_asdf_config_value() { 177 | local key=$1 178 | local config_path=${AZDF_CONFIG_FILE:-"$HOME/.asdfrc"} 179 | local default_config_path=${AZDF_CONFIG_DEFAULT_FILE:-"$(asdf_dir)/defaults"} 180 | 181 | local result=$(get_asdf_config_value_from_file $config_path $key) 182 | 183 | if [ -n "$result" ]; then 184 | echo $result 185 | else 186 | get_asdf_config_value_from_file $default_config_path $key 187 | fi 188 | } 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asdf 2 | ### _extendable version manager_ 3 | 4 | Supported languages include Ruby, Node.js, Elixir and more. Supporting a new language is as simple as [this plugin API](https://github.com/asdf-vm/asdf/blob/master/docs/creating-plugins.md). 5 | 6 | ## SETUP 7 | 8 | Copy-paste the following into command line: 9 | 10 | ```bash 11 | git clone https://github.com/asdf-vm/asdf.git ~/.asdf 12 | 13 | ``` 14 | 15 | Depending on your OS, run the following 16 | ```bash 17 | # For Ubuntu or other linux distros 18 | echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc 19 | echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc 20 | 21 | # OR for Max OSX 22 | echo '. $HOME/.asdf/asdf.sh' >> ~/.bash_profile 23 | echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bash_profile 24 | ``` 25 | 26 | If you use zsh or any other shell, replace `.bashrc` with the config file for the respective shell. 27 | 28 | For fish, you can use the following: 29 | 30 | ``` 31 | echo 'source ~/.asdf/asdf.fish' >> ~/.config/fish/config.fish 32 | mkdir -p ~/.config/fish/completions; and cp ~/.asdf/completions/asdf.fish ~/.config/fish/completions 33 | ``` 34 | 35 | > For most plugins, it is good if you have installed the following packages OR their equivalent on you OS 36 | 37 | > * **OS X**: Install these via homebrew `automake autoconf openssl libyaml readline libxslt libtool unixodbc` 38 | > * **Ubuntu**: `automake autoconf libreadline-dev libncurses-dev libssl-dev libyaml-dev libxslt-dev libffi-dev libtool unixodbc-dev` 39 | > * **Fedora**: `automake autoconf readline-devel ncurses-devel openssl-devel libyaml-devel libxslt-devel libffi-devel libtool unixODBC-devel` 40 | 41 | **That's all ~! You are ready to use asdf** 42 | 43 | ----------------------- 44 | 45 | 46 | ## USAGE 47 | 48 | ### Manage plugins 49 | 50 | Plugins are how asdf understands how to handle different packages. Below is a list of plugins for languages. There is a [super-simple API](https://github.com/asdf-vm/asdf/blob/master/docs/creating-plugins.md) for supporting more languages. 51 | 52 | | Language | Repository | CI Status 53 | |-----------|-------------|---------- 54 | | Elixir | [asdf-vm/asdf-elixir](https://github.com/asdf-vm/asdf-elixir) | [![Build Status](https://travis-ci.org/asdf-vm/asdf-elixir.svg?branch=master)](https://travis-ci.org/asdf-vm/asdf-elixir) 55 | | Erlang | [asdf-vm/asdf-erlang](https://github.com/asdf-vm/asdf-erlang) | [![Build Status](https://travis-ci.org/asdf-vm/asdf-erlang.svg?branch=master)](https://travis-ci.org/asdf-vm/asdf-erlang) 56 | | Go | [kennyp/asdf-golang](https://github.com/kennyp/asdf-golang) | [![Build Status](https://travis-ci.org/kennyp/asdf-golang.svg?branch=master)](https://travis-ci.org/kennyp/asdf-golang) 57 | | Lua | [Stratus3D/asdf-lua](https://github.com/Stratus3D/asdf-lua) | [![Build Status](https://travis-ci.org/Stratus3D/asdf-lua.svg?branch=master)](https://travis-ci.org/Stratus3D/asdf-lua) 58 | | OpenResty | [smashedtoatoms/asdf-openresty](https://github.com/smashedtoatoms/asdf-openresty) | [![Build Status](https://travis-ci.org/smashedtoatoms/asdf-openresty.svg?branch=master)](https://travis-ci.org/smashedtoatoms/asdf-openresty) 59 | | Node.js | [asdf-vm/asdf-nodejs](https://github.com/asdf-vm/asdf-nodejs) | [![Build Status](https://travis-ci.org/asdf-vm/asdf-nodejs.svg?branch=master)](https://travis-ci.org/asdf-vm/asdf-nodejs) 60 | | Postgres | [smashedtoatoms/asdf-postgres](https://github.com/smashedtoatoms/asdf-postgres) | [![Build Status](https://travis-ci.org/smashedtoatoms/asdf-postgres.svg?branch=master)](https://travis-ci.org/smashedtoatoms/asdf-postgres) 61 | | Python | [tuvistavie/asdf-python](https://github.com/tuvistavie/asdf-python) | [![Build Status](https://travis-ci.org/tuvistavie/asdf-python.svg?branch=master)](https://travis-ci.org/tuvistavie/asdf-python) 62 | | Redis | [smashedtoatoms/asdf-redis](https://github.com/smashedtoatoms/asdf-redis) | [![Build Status](https://travis-ci.org/smashedtoatoms/asdf-redis.svg?branch=master)](https://travis-ci.org/smashedtoatoms/asdf-redis) 63 | | Riak | [smashedtoatoms/asdf-riak](https://github.com/smashedtoatoms/asdf-riak) | [![Build Status](https://travis-ci.org/smashedtoatoms/asdf-riak.svg?branch=master)](https://travis-ci.org/smashedtoatoms/asdf-riak) 64 | | Ruby | [asdf-vm/asdf-ruby](https://github.com/asdf-vm/asdf-ruby) | [![Build Status](https://travis-ci.org/asdf-vm/asdf-ruby.svg?branch=master)](https://travis-ci.org/asdf-vm/asdf-ruby) 65 | 66 | 67 | ##### Add a plugin 68 | 69 | ```bash 70 | asdf plugin-add 71 | # asdf plugin-add erlang https://github.com/asdf-vm/asdf-erlang.git 72 | ``` 73 | 74 | ##### List installed plugins 75 | 76 | ```bash 77 | asdf plugin-list 78 | # asdf plugin-list 79 | ``` 80 | 81 | ##### Remove a plugin 82 | 83 | ```bash 84 | asdf plugin-remove 85 | # asdf plugin-remove erlang 86 | ``` 87 | 88 | 89 | ##### Update plugins 90 | 91 | ```bash 92 | asdf plugin-update --all 93 | ``` 94 | 95 | If you want to update a specific package, just say so. 96 | 97 | ```bash 98 | asdf plugin-update 99 | # asdf plugin-update erlang 100 | ``` 101 | 102 | ### Manage versions 103 | 104 | ```bash 105 | asdf install 106 | # asdf install erlang 17.3 107 | 108 | asdf which 109 | # asdf which erlang 110 | # 17.3 111 | 112 | asdf uninstall 113 | # asdf uninstall erlang 17.3 114 | ``` 115 | 116 | *If a plugin supports downloading & compiling from source, you can also do this `ref:foo` (replace `foo` with the branch/tag/commit).* You'll have to use the same name when uninstalling too. 117 | 118 | ##### Lists installed versions 119 | 120 | ```bash 121 | asdf list 122 | # asdf list erlang 123 | ``` 124 | 125 | ##### List all available versions 126 | 127 | ```bash 128 | asdf list-all 129 | # asdf list-all erlang 130 | ``` 131 | 132 | #### View current version 133 | 134 | ```bash 135 | asdf local [name] 136 | asdf global [name] 137 | # asdf local 138 | # asdf global 139 | # asdf local elixir 140 | # asdf global elixir 141 | ``` 142 | 143 | `global` reads from `$HOME/.tool-versions`. 144 | 145 | `local` reads from `$PWD/.tool-versions` if it exists, 146 | or searches recursively in the parent directories until it finds a `.tool-versions` file. 147 | 148 | #### Set current version 149 | 150 | ```bash 151 | asdf global 152 | asdf local 153 | asdf global elixir 1.2.4 154 | ``` 155 | 156 | `global` writes the version to `$HOME/.tool-versions`. 157 | 158 | `local` writes the version to `$PWD/.tool-versions`, creating it if needed. 159 | 160 | ## The `.tool-versions` file 161 | 162 | Add a `.tool-versions` file to your project dir and versions of those tools will be used. 163 | **Global defaults can be set in the file `$HOME/.tool-versions`** 164 | 165 | This is what a `.tool-versions` file looks like: 166 | 167 | ``` 168 | ruby 2.2.0 169 | nodejs 0.12.3 170 | ``` 171 | 172 | The versions can be in the following format: 173 | 174 | * `0.12.3` - an actual version. Plugins that support downloading binaries, will download binaries. 175 | * `ref:v1.0.2-a` or `ref:39cb398vb39` - tag/commit/branch to download from github and compile 176 | * `path:/src/elixir` - a path to custom compiled version of a tool to use. For use by language developers and such. 177 | 178 | To install all the tools defined in a `.tool-versions` file run the `asdf install` command with no other arguments in the directory containing the `.tool-versions` file. 179 | 180 | You can view/modify the file by hand or use `asdf local` and `asdf global` to manage it. 181 | 182 | ## Credits 183 | 184 | Me ([@HashNuke](http://github.com/HashNuke)), High-fever, cold, cough. 185 | 186 | Copyright 2014 to the end of time ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE)) 187 | 188 | ### Maintainers 189 | 190 | - [@HashNuke](http://github.com/HashNuke) 191 | - [@tuvistavie](http://github.com/tuvistavie) 192 | - [@Stratus3D](https://github.com/Stratus3D) 193 | 194 | ------- 195 | 196 | Read the [ballad](https://github.com/asdf-vm/asdf/blob/master/ballad-of-asdf.md). 197 | --------------------------------------------------------------------------------