Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2023-02-22 - shell との結合度が高いツールのシングルバイナリ配布方法に感心した #213

Closed
kachick opened this issue Feb 22, 2023 · 0 comments
Labels

Comments

@kachick
Copy link
Owner

kachick commented Feb 22, 2023

異なるマシンで作業したり、既存の環境をまっさらにして入れ直したくなる事がこの1年多く、久しぶりに dotfiles を弄りだしたら止まらなくなってしまった。盆栽とはよく言ったもので、毎日育ててる。(流石にもうすぐ飽きそう)
最近は rust で高機能なCLI というのが人気なようで、うっすら名前は聞いてたようなのから初耳なのまで片っ端から試してる。前はこういうのを見ても「とは言え全部の環境に入れるの面倒だからシェルスクリプトには使えないし、結局 GNU Utils ぐらいまでで落ち着くのでは・・・?」みたいな考えで日常的に使う事はしなかったんだけど、 nix で諸々楽になってからはポンポン使ってる。

ただ、starship とか rtx みたいに、如何にもシェルにベッタリな物を、既存のシェルスクリプトでも新しい shell を作るでも無くワンバイナリ配布で実現するってどういうことなんだろう?
というのがちょっとピンと来てなかったんだけど、導入方法を見てなるほどと思った。

eval "$(starship init bash)"
eval "$(rtx activate bash)"

大体こんな感じのを dotfile に突っ込めと言ってくる。1行でインストールは完了だと、はて?

starship init bash

            __main() {
                local major="${BASH_VERSINFO[0]}"
                local minor="${BASH_VERSINFO[1]}"

                if ((major > 4)) || { ((major == 4)) && ((minor >= 1)); }; then
                    source <(/home/kachick/.nix-profile/bin/starship init bash --print-full-init)
                else
                    source /dev/stdin <<<"$(/home/kachick/.nix-profile/bin/starship init bash --print-full-init)"
                fi
            }
            __main
            unset -f __main
            %/home/kachick/.nix-profile/bin/starship init bash --print-full-init
# We use PROMPT_COMMAND and the DEBUG trap to generate timing information. We try
# to avoid clobbering what we can, and try to give the user ways around our
# clobbers, if it's unavoidable. For example, PROMPT_COMMAND is appended to,
# and the DEBUG trap is layered with other traps, if it exists.

# A bash quirk is that the DEBUG trap is fired every time a command runs, even
# if it's later on in the pipeline. If uncorrected, this could cause bad timing
# data for commands like `slow | slow | fast`, since the timer starts at the start
# of the "fast" command.

# To solve this, we set a flag `STARSHIP_PREEXEC_READY` when the prompt is
# drawn, and only start the timer if this flag is present. That way, timing is
# for the entire command, and not just a portion of it.

# A way to set '$?', since bash does not allow assigning to '$?' directly
function _starship_set_return() { return "${1:-0}"; }

# Will be run before *every* command (even ones in pipes!)
starship_preexec() {
    # Save previous command's last argument, otherwise it will be set to "starship_preexec"
    local PREV_LAST_ARG=$1

    # Avoid restarting the timer for commands in the same pipeline
    if [ "$STARSHIP_PREEXEC_READY" = "true" ]; then
        STARSHIP_PREEXEC_READY=false
        STARSHIP_START_TIME=$(/home/kachick/.nix-profile/bin/starship time)
    fi

    : "$PREV_LAST_ARG"
}

# Will be run before the prompt is drawn
starship_precmd() {
    # Save the status, because commands in this pipeline will change $?
    STARSHIP_CMD_STATUS=$? STARSHIP_PIPE_STATUS=(${PIPESTATUS[@]})
    if [[ "${#BP_PIPESTATUS[@]}" -gt "${#STARSHIP_PIPE_STATUS[@]}" ]]; then
        STARSHIP_PIPE_STATUS=(${BP_PIPESTATUS[@]})
    fi

    local NUM_JOBS=0
    # Evaluate the number of jobs before running the preserved prompt command, so that tools
    # like z/autojump, which background certain jobs, do not cause spurious background jobs
    # to be displayed by starship. Also avoids forking to run `wc`, slightly improving perf.
    for job in $(jobs -p); do [[ $job ]] && ((NUM_JOBS++)); done

    # Run the bash precmd function, if it's set. If not set, evaluates to no-op
    "${starship_precmd_user_func-:}"

    # Set $? to the preserved value before running additional parts of the prompt
    # command pipeline, which may rely on it.
    _starship_set_return "$STARSHIP_CMD_STATUS"

    eval "$_PRESERVED_PROMPT_COMMAND"

    # Prepare the timer data, if needed.
    if [[ $STARSHIP_START_TIME ]]; then
        STARSHIP_END_TIME=$(/home/kachick/.nix-profile/bin/starship time)
        STARSHIP_DURATION=$((STARSHIP_END_TIME - STARSHIP_START_TIME))
        PS1="$(/home/kachick/.nix-profile/bin/starship prompt --terminal-width="$COLUMNS" --status=$STARSHIP_CMD_STATUS --pipestatus="${STARSHIP_PIPE_STATUS[*]}" --jobs="$NUM_JOBS" --cmd-duration=$STARSHIP_DURATION)"
        unset STARSHIP_START_TIME
    else
        PS1="$(/home/kachick/.nix-profile/bin/starship prompt --terminal-width="$COLUMNS" --status=$STARSHIP_CMD_STATUS --pipestatus="${STARSHIP_PIPE_STATUS[*]}" --jobs="$NUM_JOBS")"
    fi
    STARSHIP_PREEXEC_READY=true  # Signal that we can safely restart the timer
}

# If the user appears to be using https://github.com/rcaloras/bash-preexec,
# then hook our functions into their framework.
if [[ "${__bp_imported:-}" == "defined" || $preexec_functions || $precmd_functions ]]; then
    # bash-preexec needs a single function--wrap the args into a closure and pass
    starship_preexec_all(){ starship_preexec "$_"; }
    preexec_functions+=(starship_preexec_all)
    precmd_functions+=(starship_precmd)
else
    # We want to avoid destroying an existing DEBUG hook. If we detect one, create
    # a new function that runs both the existing function AND our function, then
    # re-trap DEBUG to use this new function. This prevents a trap clobber.
    dbg_trap="$(trap -p DEBUG | cut -d' ' -f3 | tr -d \')"
    if [[ -z "$dbg_trap" ]]; then
        trap 'starship_preexec "$_"' DEBUG
    elif [[ "$dbg_trap" != 'starship_preexec "$_"' && "$dbg_trap" != 'starship_preexec_all "$_"' ]]; then
        starship_preexec_all() {
            local PREV_LAST_ARG=$1 ; $dbg_trap; starship_preexec; : "$PREV_LAST_ARG";
        }
        trap 'starship_preexec_all "$_"' DEBUG
    fi

    # Finally, prepare the precmd function and set up the start time. We will avoid to
    # add multiple instances of the starship function and keep other user functions if any.
    if [[ -z "$PROMPT_COMMAND" ]]; then
        PROMPT_COMMAND="starship_precmd"
    elif [[ "$PROMPT_COMMAND" != *"starship_precmd"* ]]; then
        # Appending to PROMPT_COMMAND breaks exit status ($?) checking.
        # Prepending to PROMPT_COMMAND breaks "command duration" module.
        # So, we are preserving the existing PROMPT_COMMAND
        # which will be executed later in the starship_precmd function
        _PRESERVED_PROMPT_COMMAND="$PROMPT_COMMAND"
        PROMPT_COMMAND="starship_precmd"
    fi
fi

# Ensure that $COLUMNS gets set
shopt -s checkwinsize

# Set up the start time and STARSHIP_SHELL, which controls shell-specific sequences
STARSHIP_START_TIME=$(/home/kachick/.nix-profile/bin/starship time)
export STARSHIP_SHELL="bash"

# Set up the session key that will be used to store logs
STARSHIP_SESSION_KEY="$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM"; # Random generates a number b/w 0 - 32767
STARSHIP_SESSION_KEY="${STARSHIP_SESSION_KEY}0000000000000000" # Pad it to 16+ chars.
export STARSHIP_SESSION_KEY=${STARSHIP_SESSION_KEY:0:16}; # Trim to 16-digits if excess.

# Set the continuation prompt
PS2="$(/home/kachick/.nix-profile/bin/starship prompt --continuation)"rtx activate bash
_rtx_hook() {
  local previous_exit_status=$?;
  trap -- '' SIGINT;
  eval "$("/home/kachick/.local/share/rtx/bin/rtx" hook-env -s bash)";
  trap - SIGINT;
  return $previous_exit_status;
};
if ! [[ "${PROMPT_COMMAND:-}" =~ _rtx_hook ]]; then
  PROMPT_COMMAND="_rtx_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
fi

全く読めない。読めないがしかし、お膳立てしたシェルのコードを生成してやるから、それを食わせろ、何も考えるな(とは言ってない) ということは伝わってきた。
そのシングルバイナリの中にコアとなる機能と合わせてシェルスクリプト生成機能も含めて置くことで、導入の手間を最小限にしてくれてるのかー

なんかわかってしまえば普通だったなという気もしたんだけど、最初に見た時は結構関心したので、新鮮な心が残っているうちに書き留めておく。

@kachick kachick added the blog label Feb 22, 2023
@kachick kachick closed this as completed Feb 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant