GetStream / vg

Virtualgo: Easy and powerful workspace based development for go
MIT License
1.32k stars 45 forks source link

[bash] Functions in `$PROMPT_COMMAND` should aways return the previous exit code #47

Closed 13k closed 5 years ago

13k commented 5 years ago

Current $PROMPT_COMMAND function injected by vg (__vg_prompt_command) ignores the last exit code and always returns zero, which can break subsequent functions that depend on that value (like actual prompt-setting functions).

Here's an example, without and with the fix.

pcmd is a dumb function that appends a suffix to PS1 with the exit code, only if it's non-zero.

prompt-command-fix is a fictional fixed data/bash file.

/tmp/vg > export PS1="\\w > "
/tmp/vg > echo "$PROMPT_COMMAND"

/tmp/vg > pcmd() { local r=$? suffix; [[ $r -ne 0 ]] && suffix=" $r"; export PS1="\\w${suffix} > "; return $r; }
/tmp/vg > export PROMPT_COMMAND="pcmd"
/tmp/vg > cat nonexistent
cat: nonexistent: No such file or directory
/tmp/vg 1 > # <--- prompt was updated to include non-zero exit status
/tmp/vg 1 > eval "$(vg eval)"
/tmp/vg > echo "$PROMPT_COMMAND"
__vg_prompt_command ; pcmd
/tmp/vg > # PROMPT_COMMAND modified by vg with the function's current version
/tmp/vg > type __vg_prompt_command
__vg_prompt_command is a function
__vg_prompt_command () 
{ 
    { 
        local USE_X;
        local USE_U;
        USE_X=`case "$-" in *x*) echo "yes" ;; esac`;
        USE_U=`case "$-" in *u*) echo "yes" ;; esac`;
        set +x;
        set +u
    } 2> /dev/null;
    if [ "$PWD" != "$_PREV_VIRTUALGO_PWD" ]; then
        _PREV_VIRTUALGO_PWD="$PWD";
        __vg_auto_activate;
    fi;
    [ "$USE_U" != "yes" ] || set -u;
    [ "$USE_X" != "yes" ] || set -x
}
/tmp/vg > cat nonexistent
cat: nonexistent: No such file or directory
/tmp/vg > # <--- prompt was NOT updated, "$r" in pcmd() is zero
/tmp/vg > source prompt-command-fix
/tmp/vg > # PROMPT_COMMAND modified (fixed version)
/tmp/vg > type __vg_prompt_command
__vg_prompt_command is a function
__vg_prompt_command () 
{ 
    local last_exit=$?;
    { 
        local USE_X;
        local USE_U;
        USE_X=`case "$-" in *x*) echo "yes" ;; esac`;
        USE_U=`case "$-" in *u*) echo "yes" ;; esac`;
        set +x;
        set +u
    } 2> /dev/null;
    if [ "$PWD" != "$_PREV_VIRTUALGO_PWD" ]; then
        _PREV_VIRTUALGO_PWD="$PWD";
        __vg_auto_activate;
    fi;
    [ "$USE_U" != "yes" ] || set -u;
    [ "$USE_X" != "yes" ] || set -x;
    return $last_exit
}
/tmp/vg > cat nonexist
cat: nonexist: No such file or directory
/tmp/vg 1 > # <--- prompt was updated to include non-zero exit status
/tmp/vg 1 > echo test
test
/tmp/vg > # <--- prompt was updated back
/tmp/vg > cat nonexist
cat: nonexist: No such file or directory
/tmp/vg 1 > # <--- prompt was updated to include non-zero exit status
/tmp/vg 1 > echo $?
1

The second part of the fix is a change to only prepend __vg_prompt_command to PROMPT_COMMAND if it doesn't include it already. Multiple instances of the function inclusion can happen, for example, when you are in a vg-enabled shell and you launch a sub-shell that evals vg eval again (will happen if the sub-shell is a login shell and vg eval is in your profile/rc file). An example of such case is running a vg-enabled shell and entering a sub-shell using other tools like pipenv or poetry (Python virtualenv managers).