asdf-vm / asdf

Extendable version manager with support for Ruby, Node.js, Elixir, Erlang & more
https://asdf-vm.com/
MIT License
21.13k stars 763 forks source link

Install should have `--local` and `--global` optional flags #1745

Open andrewduss opened 2 months ago

andrewduss commented 2 months ago

Is your feature request related to a problem? Please describe

[I'm always frustrated when] installing a new addon and have to run asdf install <plugin> <version> and asdf global <plugin> <version>

Describe the proposed solution

[I would like to see] an optional --local and --global flag added to the asdf install command which will automatically set the local or global versions for the selected tool.

For example: asdf install --global golang 1.19.1 would Install golang 1.19.1 and set it as the global default. asdf install --local ruby 2.6.4 would install ruby 2.6.4 and set the local tool-versions file to use that version.

Describe similar asdf features and why they are not sufficient

ASDF currently has all these functions, but this change would make it easier to use.

Describe other workarounds you've considered

Run both asdf install ... and asdf global/local ...commands.

slewsys commented 1 week ago

You'll need a recent version of the Bash shell in your path to run the following script.

#!/usr/bin/env bash
#
# @(#) asdf-install
#
# SYNOPSIS
#
#     asdf-install [-C WORK-DIR] [--local | -l] PLUGIN [VERSION]
#
#     Options:
#         -C WORK-DIR   Change to WORK-DIR before running asdf.
#         --local, -l   Install plugin in local scope.
#
#      If VERSION is omitted, then the latest PLUGIN is installed.
#      If VERSION is under-specified - e.g., nodejs 20 - then the
#      latest iteration of that VERSION is installed.
#
: ${ASDF_CMD:='asdf'}
: ${CAT_CMD:='@CAT_CMD@'}
: ${GETOPT_CMD:='@GETOPT_CMD@'}
: ${READLINK_CMD:='@READLINK_CMD@'}

usage ()
{
    $CAT_CMD >&2 <<EOF
Usage: ${script_name} [-C WORK-DIR] [--local | -l] PLUGIN [VERSION]

Options:
   -C WORK-DIR   Change to WORK-DIR before running asdf.
   --help, -h    Print (this) help, then exit.
   --global, -g  Install plugin in global scope (default).
   --local, -l   Install plugin in local scope.

If VERSION is omitted, then the latest PLUGIN is installed. If VERSION
is under-specified - e.g., nodejs 20 - then the latest iteration of
that VERSION is installed.
EOF
}

# OS-agnstoic readlink for existent files/directories.
resolve-existing ()
{
    case $($READLINK_CMD --version 2>&1) in
        *coreutils*)
            $READLINK_CMD -e "$@"
            ;;
        *)
            $READLINK_CMD -f N "$@"
            ;;
    esac
}

install-asdf-plugin ()
{
    local -n params=$1

    local latest_version=''
    local status=''

    latest_version=$($ASDF_CMD latest "${params[plugin]}" "${params[version]}") || return $?
    pushd "${params[workdir]}" >/dev/null || return $?

    trap 'popd >/dev/null' RETURN

    $ASDF_CMD install "${params[plugin]}" "$latest_version" >&2 || return $?
    $ASDF_CMD "${params[scope]}" "${params[plugin]}" "$latest_version" >&2 || return $?
    popd >/dev/null

    trap - RETURN
}

parse-command-line ()
{
    local -n params=$1

    local args
    local optarg
    local -r shortopts=hC:gl
    local -r longopts=help,global,local

    shift
    if ! args=$(
            $GETOPT_CMD --shell bash --options "$shortopts"  \
                        --longoptions "$longopts" --name "$script_name" -- "$@"
         ); then
        usage
        return 1
    fi

    eval set -- "$args"
    unset args

    while true; do
        case $1 in
            --help|-h)
                usage
                exit
                ;;
            -C)
                if test ! -d "$2"; then
                    usage
                    return 2
                fi
                params[workdir]=$2
                shift
                ;;
            --global|-g)
                params[scope]=global
                ;;
            --local|-l)
                params[scope]=local
                ;;
            --)
                shift
                break
                ;;
            *)
                usage
                return 3
                ;;
        esac
        shift
    done

    if (( $# < 1 || 2 < $# )); then
        usage
        return 4
    fi

    params[plugin]=$1
    params[version]=$2
}

if test ."$0" = ."${BASH_SOURCE[0]}"; then
    declare script=''
    declare script_name=''

    script=$(resolve-existing "$0") || exit $?
    script_name=${script##*/}

    declare -A parameters=(
        [plugin]=''
        [version]=''
        [workdir]=$PWD
        [scope]=global
    )

    parse-command-line parameters "$@" || exit $?
    install-asdf-plugin parameters
fi