From 3379af845ed2e281703bc0e9e4f388a7845edc2a Mon Sep 17 00:00:00 2001 From: Edwin Kofler Date: Mon, 20 Mar 2023 22:06:57 -0700 Subject: [PATCH] fix!: rework POSIX entrypoint for greater shell support (#1480) --- asdf.sh | 100 ++++++++++++++++++-------- docs/guide/getting-started.md | 33 +++++++++ docs/pt-br/guide/getting-started.md | 33 +++++++++ docs/zh-hans/guide/getting-started.md | 33 +++++++++ lib/asdf.sh | 22 +++--- scripts/format.bash | 6 +- scripts/shellcheck.bash | 7 +- scripts/shfmt.bash | 6 +- 8 files changed, 187 insertions(+), 53 deletions(-) diff --git a/asdf.sh b/asdf.sh index 7a01f5e8..d7dd4c7e 100644 --- a/asdf.sh +++ b/asdf.sh @@ -1,37 +1,77 @@ -# For Korn shells (ksh, mksh, etc.), capture $_ (the final parameter passed to -# the last command) straightaway, as it will contain the path to this script. -# For Bash, ${BASH_SOURCE[0]} will be used to obtain this script's path. -# For Zsh and others, $0 (the path to the shell or script) will be used. -_under="$_" -if [ -z "${ASDF_DIR:-}" ]; then - if [ -n "${BASH_SOURCE[0]}" ]; then - current_script_path="${BASH_SOURCE[0]}" - elif [[ "$_under" == *".sh" ]]; then - current_script_path="$_under" - else - current_script_path="$0" +# shellcheck shell=sh +# shellcheck disable=SC1007 + +# This file is the entrypoint for all POSIX-compatible shells. If `ASDF_DIR` is +# not already set, this script is able to calculate it, but only if the shell is +# either Bash, Zsh, and Ksh. For other shells, `ASDF_DIR` must be manually set. + +export ASDF_DIR="${ASDF_DIR:-}" + +if [ -z "$ASDF_DIR" ]; then + if [ -n "$BASH_VERSION" ]; then + # Use BASH_SOURCE[0] to obtain the relative path to this source'd file. Since it's + # a relative path, 'cd' to its dirname and use '$PWD" to obtain the fullpath. + # Use 'builtin cd' to ensure user-defined 'cd()' functions aren't called. + # Use variable '_asdf_old_dir' to avoid using subshells. + + _asdf_old_dir=$PWD + # shellcheck disable=SC3028,SC3054 + if ! CDPATH= builtin cd -- "${BASH_SOURCE[0]%/*}"; then + printf '%s\n' 'asdf: Error: Failed to cd' >&2 + unset -v _asdf_old_dir + return 1 + fi + ASDF_DIR=$PWD + if ! CDPATH= builtin cd -- "$_asdf_old_dir"; then + printf '%s\n' 'asdf: Error: Failed to cd' >&2 + unset -v _asdf_old_dir + return 1 + fi + unset -v _asdf_old_dir + elif [ -n "$ZSH_VERSION" ]; then + # Use '%x' to expand to path of current file. It must be prefixed + # with '(%):-', so it expands in non-prompt-string contexts. + + # shellcheck disable=SC2296 + ASDF_DIR=${(%):-%x} + ASDF_DIR=${ASDF_DIR%/*} + elif [ -n "$KSH_VERSION" ] && [ -z "$PATHSEP" ]; then + # Only the original KornShell (kornshell.com) has a '.sh.file' variable with the path + # of the current file. To prevent errors with other variations, such as the MirBSD + # Korn shell (mksh), test for 'PATHSEP' which is _not_ set on the original Korn Shell. + + # shellcheck disable=SC2296 + ASDF_DIR=${.sh.file} + ASDF_DIR=${ASDF_DIR%/*} fi - - ASDF_DIR="$(dirname "$current_script_path")" fi -export ASDF_DIR -# shellcheck disable=SC2016 -[ -d "$ASDF_DIR" ] || printf "%s\n" "$ASDF_DIR is not a directory" -# Add asdf to PATH -# -# if in $PATH, remove, regardless of if it is in the right place (at the front) or not. -# replace all occurrences - ${parameter//pattern/string} -ASDF_BIN="${ASDF_DIR}/bin" -ASDF_USER_SHIMS="${ASDF_DATA_DIR:-$HOME/.asdf}/shims" -[[ ":$PATH:" == *":${ASDF_BIN}:"* ]] && PATH="${PATH//$ASDF_BIN:/}" -[[ ":$PATH:" == *":${ASDF_USER_SHIMS}:"* ]] && PATH="${PATH//$ASDF_USER_SHIMS:/}" -# add to front of $PATH -PATH="${ASDF_BIN}:$PATH" -PATH="${ASDF_USER_SHIMS}:$PATH" +if [ -z "$ASDF_DIR" ]; then + printf "%s\n" "asdf: Error: Source directory could not be calculated. Please set it manually before sourcing this file." >&2 + return 1 +fi + +if [ ! -d "$ASDF_DIR" ]; then + printf "%s\n" "asdf: Error: Variable '\$ASDF_DIR' is not a directory: $ASDF_DIR" >&2 + return 1 +fi + +_asdf_bin="${ASDF_DIR}/bin" +_asdf_shims="${ASDF_DATA_DIR:-$HOME/.asdf}/shims" + +# shellcheck disable=SC3060 +if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then + case ":$PATH:" in + *":${_asdf_bin}:"*) PATH="${PATH//$_asdf_bin:/}" ;; + esac + case ":$PATH:" in + *":${_asdf_shims}:"*) PATH="${PATH//$_asdf_shims:/}" ;; + esac +fi + +PATH="${_asdf_bin}:${_asdf_shims}:$PATH" +unset -v _asdf_bin _asdf_shims # shellcheck source=lib/asdf.sh -# Load the asdf wrapper function . "${ASDF_DIR}/lib/asdf.sh" -unset _under current_script_path ASDF_BIN ASDF_USER_SHIMS diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 5ec75697..0a2acc21 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -294,6 +294,39 @@ Add `asdf.nu` to your `~/.config/nushell/config.nu` with: Completions are automatically configured. ::: +::: details POSIX Shell & Git + +Add the following to `~/.profile`: + +```shell +export ASDF_DIR="$HOME/.asdf" +. "$HOME/.asdf/asdf.sh" +``` + +::: + +::: details POSIX Shell & Homebrew + +Add `asdf.sh` to your `~/.profile` with: + +```shell:no-line-numbers +echo -e "\nexport ASDF_DIR=\"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +echo -e "\n. \"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +``` + +::: + +::: details POSIX Shell & Pacman + +Add the following to `~/.profile`: + +```shell +export ASDF_DIR="/opt/asdf-vm" +. /opt/asdf-vm/asdf.sh +``` + +::: + `asdf` scripts need to be sourced **after** you have set your `$PATH` and **after** you have sourced your framework (oh-my-zsh etc). Restart your shell so that `PATH` changes take effect. Opening a new terminal tab will usually do it. diff --git a/docs/pt-br/guide/getting-started.md b/docs/pt-br/guide/getting-started.md index c57fb35c..69d902b5 100644 --- a/docs/pt-br/guide/getting-started.md +++ b/docs/pt-br/guide/getting-started.md @@ -262,6 +262,39 @@ Adicione a seguinte linha ao seu `~/.zshrc`: . /opt/asdf-vm/asdf.sh ``` +::: details POSIX Shell & Git + +Adicione a seguinte linha ao seu `~/.profile`: + +```shell +export ASDF_DIR="$HOME/.asdf" +. "$HOME/.asdf/asdf.sh" +``` + +::: + +::: details POSIX Shell & Homebrew + +Adicione `asdf.sh` ao `~/.profile` através do comando: + +```shell:no-line-numbers +echo -e "\nexport ASDF_DIR=\"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +echo -e "\n. \"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +``` + +::: + +::: details POSIX Shell & Pacman + +Adicione a seguinte linha ao seu `~/.profile`: + +```shell +export ASDF_DIR="/opt/asdf-vm" +. /opt/asdf-vm/asdf.sh +``` + +::: + O auto completar é colocado em um local familiar para o ZSH, [mas o ZSH deve ser configurado para conseguir utilizá-lo](https://wiki.archlinux.org/index.php/zsh#Command_completion). ::: diff --git a/docs/zh-hans/guide/getting-started.md b/docs/zh-hans/guide/getting-started.md index fb39a0b8..f77fbb60 100644 --- a/docs/zh-hans/guide/getting-started.md +++ b/docs/zh-hans/guide/getting-started.md @@ -263,6 +263,39 @@ echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc 补全功能会被放在一个对 ZSH 很友好的位置,但是 [ZSH 必须使用自动补全完成配置](https://wiki.archlinux.org/index.php/zsh#Command_completion)。 ::: +::: details POSIX Shell & Git + +在 `~/.profile` 文件中加入以下内容: + +```shell +export ASDF_DIR="$HOME/.asdf" +. "$HOME/.asdf/asdf.sh" +``` + +::: + +::: details POSIX Shell & Homebrew + +使用以下命令将 `asdf.sh` 加入到 `~/.profile` 文件中: + +```shell:no-line-numbers +echo -e "\nexport ASDF_DIR=\"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +echo -e "\n. \"$(brew --prefix asdf)/libexec/asdf.sh\"" >> ~/.profile +``` + +::: + +::: details POSIX Shell & Pacman + +在 `~/.profile` 文件中加入以下内容: + +```shell +export ASDF_DIR="/opt/asdf-vm" +. /opt/asdf-vm/asdf.sh +``` + +::: + `asdf` 脚本需要在设置好的 `$PATH` **之后**和已经生效的框架(比如 oh-my-zsh 等等)**之后**的位置生效。 通常打开一个新的终端标签页来重启你的 shell 让 `PATH` 更改即时生效。 diff --git a/lib/asdf.sh b/lib/asdf.sh index f43aae58..e5404aa8 100644 --- a/lib/asdf.sh +++ b/lib/asdf.sh @@ -1,20 +1,20 @@ +# shellcheck shell=sh + # The asdf function is a wrapper so we can export variables asdf() { - local command - command="$1" - if [ "$#" -gt 0 ]; then - shift - fi - - case "$command" in + case $1 in "shell") - # commands that need to export variables + if ! shift; then + printf '%s\n' 'asdf: Error: Failed to shift' >&2 + return 1 + fi + + # Invoke command that needs to export variables. eval "$(asdf export-shell-version sh "$@")" # asdf_allow: eval ;; *) - # forward other commands to asdf script - command asdf "$command" "$@" # asdf_allow: ' asdf ' + # Forward other commands to asdf script. + command asdf "$@" # asdf_allow: ' asdf ' ;; - esac } diff --git a/scripts/format.bash b/scripts/format.bash index 5be8205a..1a2eae63 100755 --- a/scripts/format.bash +++ b/scripts/format.bash @@ -3,10 +3,8 @@ set -euo pipefail # check .sh files -# TODO(jthegedus): unlock this check later -# TODO shfmt --language-dialect posix --indent 2 --write \ -# TODO asdf.sh \ -# TODO lib/*.sh +shfmt --language-dialect posix --indent 2 --write \ + lib/*.sh # check .bash files shfmt --language-dialect bash --indent 2 --write \ diff --git a/scripts/shellcheck.bash b/scripts/shellcheck.bash index 223af27a..ca49bd4c 100755 --- a/scripts/shellcheck.bash +++ b/scripts/shellcheck.bash @@ -3,10 +3,9 @@ set -euo pipefail # check .sh files -# TODO(jthegedus): unlock this check later -# TODO shellcheck --shell sh --external-sources \ -# TODO asdf.sh \ -# TODO lib/*.sh +shellcheck --shell sh --external-sources \ + asdf.sh \ + lib/*.sh # check .bash files shellcheck --shell bash --external-sources \ diff --git a/scripts/shfmt.bash b/scripts/shfmt.bash index 7f256b86..d5db8226 100755 --- a/scripts/shfmt.bash +++ b/scripts/shfmt.bash @@ -3,10 +3,8 @@ set -euo pipefail # check .sh files -# TODO(jthegedus): unlock this check later -# TODO shfmt --language-dialect posix --indent 2 --diff \ -# TODO asdf.sh \ -# TODO lib/*.sh +shfmt --language-dialect posix --indent 2 --diff \ + lib/*.sh # check .bash files shfmt --language-dialect bash --indent 2 --diff \