-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(bash): avoid duplicate trust warning after cd #8920
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| #!/usr/bin/env bash | ||
| # shellcheck disable=SC2016 | ||
| set -euo pipefail | ||
|
|
||
| # Reproduces the case where entering an untrusted project triggers hook-env from | ||
| # both chpwd and PROMPT_COMMAND. The user-visible symptom is a duplicate `mise trust` | ||
| # warning after a single `cd`. | ||
| # | ||
| # The expected bash behavior is: | ||
| # - `_mise_hook_prompt_command` remains registered after activation and after cd | ||
| # - the prompt immediately following `cd` does not repeat the same trust warning | ||
| # - bash does not fall back to chpwd-only behavior after the startup prompt | ||
|
|
||
| # The e2e harness trusts the isolated workspace by default; clear that so this | ||
| # test exercises the real untrusted-config warning path. | ||
| export MISE_TRUSTED_CONFIG_PATHS="" | ||
|
|
||
| # In CI, mise auto-trusts configs via ci_info::is_ci(); clear the common CI vars | ||
| # so hook-env follows the normal interactive trust-check behavior. | ||
| unset CI GITHUB_ACTIONS GITHUB_ACTION 2>/dev/null || true | ||
|
|
||
| contains_trust_warning() { | ||
| local file="$1" | ||
| grep -q "Config files in" "$file" && grep -q "are not trusted" "$file" | ||
| } | ||
|
|
||
| contains_mise_prompt_hook() { | ||
| [[ ";${PROMPT_COMMAND:-};" == *";_mise_hook_prompt_command;"* ]] | ||
| } | ||
|
|
||
| mkdir -p project | ||
| cat >project/.mise.toml <<'EOF' | ||
| [env] | ||
| FOO = "bar" | ||
| EOF | ||
|
|
||
| eval "$(mise activate bash --status)" | ||
|
|
||
| startup_file="$(mktemp)" | ||
| chpwd_file="$(mktemp)" | ||
| precmd_file="$(mktemp)" | ||
| trap 'rm -f "$startup_file" "$chpwd_file" "$precmd_file"' EXIT | ||
|
|
||
| # A persistent mise PROMPT_COMMAND hook should be registered immediately after activation. | ||
| if ! contains_mise_prompt_hook; then | ||
| echo "expected mise PROMPT_COMMAND hook to be registered after activation" | ||
| echo "PROMPT_COMMAND: ${PROMPT_COMMAND:-}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The bash chpwd skip flag should be initialised to 0 after activation. | ||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected __MISE_BASH_CHPWD_RAN=0 after activation" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Simulate the first prompt after activation before entering the untrusted project. | ||
| _mise_hook_prompt_command >"$startup_file" 2>&1 || true | ||
| if contains_trust_warning "$startup_file"; then | ||
| echo "startup PROMPT_COMMAND should not hit an untrusted project before cd" | ||
| cat "$startup_file" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The chpwd skip flag must not have been touched. | ||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected no chpwd skip flag after the first prompt" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The mise PROMPT_COMMAND hook should remain registered after the startup prompt | ||
| # so bash can continue checking for later environment changes on subsequent prompts. | ||
| if ! contains_mise_prompt_hook; then | ||
| echo "expected mise PROMPT_COMMAND hook to remain registered after the startup prompt" | ||
| echo "PROMPT_COMMAND: ${PROMPT_COMMAND:-}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Use builtin cd to avoid __zsh_like_cd triggering chpwd_functions automatically, | ||
| # so we can capture the hook output separately below. | ||
| builtin cd project | ||
|
|
||
| # Fire chpwd hooks manually (simulating what __zsh_like_cd does after cd). | ||
| : >"$chpwd_file" | ||
| for _hook in "${chpwd_functions[@]-}"; do | ||
| "$_hook" >>"$chpwd_file" 2>&1 || true | ||
| done | ||
|
|
||
| # After chpwd, the skip flag should be set. | ||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "1" ]]; then | ||
| echo "expected __MISE_BASH_CHPWD_RAN=1 after chpwd" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The chpwd hook should surface the untrusted-config warning on directory entry. | ||
| if ! contains_trust_warning "$chpwd_file"; then | ||
| echo "expected chpwd hook to report untrusted config" | ||
| cat "$chpwd_file" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Fire the PROMPT_COMMAND hook that would run at the next prompt after cd. | ||
| _mise_hook_prompt_command >"$precmd_file" 2>&1 || true | ||
|
|
||
| # The PROMPT_COMMAND hook should have consumed the chpwd skip flag. | ||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected __MISE_BASH_CHPWD_RAN to be reset to 0 after precmd consumed it" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The subsequent prompt should not repeat the same warning. | ||
| if contains_trust_warning "$precmd_file"; then | ||
| echo "PROMPT_COMMAND hook should not repeat untrusted config warning after chpwd" | ||
| cat "$precmd_file" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The mise PROMPT_COMMAND hook should still remain registered after chpwd + precmd | ||
| # so later prompts can continue to notice config/watch-file changes. | ||
| if ! contains_mise_prompt_hook; then | ||
| echo "expected mise PROMPT_COMMAND hook to remain registered after chpwd + precmd" | ||
| echo "PROMPT_COMMAND: ${PROMPT_COMMAND:-}" | ||
| exit 1 | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Verify that bash does not fall into an every-other-prompt pattern after the | ||
| # first PROMPT_COMMAND run. Two consecutive prompt runs without a directory | ||
| # change should both run hook-env normally. | ||
|
|
||
| eval "$(mise activate bash --status)" | ||
|
|
||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected __MISE_BASH_CHPWD_RAN=0 after activation" | ||
| exit 1 | ||
| fi | ||
|
|
||
| _mise_hook_prompt_command >/dev/null 2>&1 || true | ||
|
|
||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected no chpwd skip flag after the first prompt" | ||
| exit 1 | ||
| fi | ||
|
|
||
| _mise_hook_prompt_command >/dev/null 2>&1 || true | ||
|
|
||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected no chpwd skip flag after the second prompt" | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Verify that bash deactivation removes the stale `_mise_hook_chpwd` entry from | ||
| # `chpwd_functions` as well as unsetting the function itself. | ||
|
|
||
| eval "$(mise activate bash --status)" | ||
|
|
||
| if [[ " ${chpwd_functions[*]-} " != *" _mise_hook_chpwd "* ]]; then | ||
| echo "expected _mise_hook_chpwd to be registered after activation" | ||
| printf '%s\n' "${chpwd_functions[@]-}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| mise deactivate | ||
|
|
||
| if [[ " ${chpwd_functions[*]-} " == *" _mise_hook_chpwd "* ]]; then | ||
| echo "expected _mise_hook_chpwd to be removed from chpwd_functions after deactivation" | ||
| printf '%s\n' "${chpwd_functions[@]-}" | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Verify that the first PROMPT_COMMAND run after `mise activate bash` still runs | ||
| # hook-env. This protects the startup path where bash may mutate PATH during | ||
| # shell initialization before the first prompt is rendered. | ||
|
|
||
| eval "$(mise activate bash --status)" | ||
|
|
||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected __MISE_BASH_CHPWD_RAN=0 after activation" | ||
| exit 1 | ||
| fi | ||
|
|
||
| _mise_hook_prompt_command >/dev/null 2>&1 || true | ||
|
|
||
| if [[ ${__MISE_BASH_CHPWD_RAN:-unset} != "0" ]]; then | ||
| echo "expected first prompt after activation to leave __MISE_BASH_CHPWD_RAN unchanged" | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,77 @@ | ||||||
| # shellcheck shell=bash | ||||||
| __MISE_EXE=__MISE_EXE_VALUE__ | ||||||
| __MISE_FLAGS=(__MISE_FLAGS_VALUE__) | ||||||
| __MISE_HOOK_ENABLED=__MISE_HOOK_ENABLED_VALUE__ | ||||||
|
|
||||||
| export MISE_SHELL=bash | ||||||
|
|
||||||
| # On first activation, save the original PATH | ||||||
| # On re-activation, we keep the saved original | ||||||
| if [ -z "${__MISE_ORIG_PATH:-}" ]; then | ||||||
| export __MISE_ORIG_PATH="$PATH" | ||||||
| fi | ||||||
| __MISE_BASH_CHPWD_RAN=0 | ||||||
|
|
||||||
| mise() { | ||||||
| local command | ||||||
| command="${1:-}" | ||||||
| if [ "$#" = 0 ]; then | ||||||
| command "$__MISE_EXE" | ||||||
| return | ||||||
| fi | ||||||
| shift | ||||||
|
|
||||||
| case "$command" in | ||||||
| deactivate | shell | sh) | ||||||
| # if argv doesn't contains -h,--help | ||||||
| if [[ ! " $* " =~ " --help " ]] && [[ ! " $* " =~ " -h " ]]; then | ||||||
| eval "$(command "$__MISE_EXE" "$command" "$@")" | ||||||
| return $? | ||||||
| fi | ||||||
| ;; | ||||||
| esac | ||||||
| command "$__MISE_EXE" "$command" "$@" | ||||||
| } | ||||||
|
|
||||||
| _mise_hook() { | ||||||
| local previous_exit_status=$? | ||||||
| eval "$(mise hook-env "${__MISE_FLAGS[@]}" -s bash)" | ||||||
| return $previous_exit_status | ||||||
| } | ||||||
|
|
||||||
| if [ "$__MISE_HOOK_ENABLED" = "1" ]; then | ||||||
| _mise_hook_prompt_command() { | ||||||
| local previous_exit_status=$? | ||||||
| if [[ ${__MISE_BASH_CHPWD_RAN:-0} == "1" ]]; then | ||||||
| __MISE_BASH_CHPWD_RAN=0 | ||||||
| return $previous_exit_status | ||||||
| fi | ||||||
| eval "$(mise hook-env "${__MISE_FLAGS[@]}" -s bash --reason precmd)" | ||||||
| return $previous_exit_status | ||||||
| } | ||||||
|
|
||||||
| _mise_hook_chpwd() { | ||||||
| local previous_exit_status=$? | ||||||
| __MISE_BASH_CHPWD_RAN=1 | ||||||
| eval "$(mise hook-env "${__MISE_FLAGS[@]}" -s bash --reason chpwd)" | ||||||
| return $previous_exit_status | ||||||
| } | ||||||
|
|
||||||
| _mise_add_prompt_command() { | ||||||
| if [[ "$(declare -p PROMPT_COMMAND 2>/dev/null)" == "declare -a"* ]]; then | ||||||
| if [[ " ${PROMPT_COMMAND[*]} " != *" _mise_hook_prompt_command "* ]]; then | ||||||
| PROMPT_COMMAND=("_mise_hook_prompt_command" "${PROMPT_COMMAND[@]}") | ||||||
| fi | ||||||
| elif [[ ";${PROMPT_COMMAND:-};" != *";_mise_hook_prompt_command;"* ]]; then | ||||||
| local _mise_prompt_command_value="${PROMPT_COMMAND-}" | ||||||
| printf -v PROMPT_COMMAND '%s' "_mise_hook_prompt_command${_mise_prompt_command_value:+;$_mise_prompt_command_value}" | ||||||
| fi | ||||||
| } | ||||||
|
|
||||||
| _mise_add_prompt_command | ||||||
| __MISE_CHPWD_FUNCTIONS__ | ||||||
| __MISE_CHPWD_LOAD__ | ||||||
| chpwd_functions+=(_mise_hook_chpwd) | ||||||
|
timothysparg marked this conversation as resolved.
|
||||||
| fi | ||||||
|
|
||||||
| _mise_hook | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid a duplicate trust warning on shell startup when activating inside an untrusted project, the initial
Suggested change
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # shellcheck shell=bash | ||
| if [ -z "${_mise_cmd_not_found:-}" ]; then | ||
| _mise_cmd_not_found=1 | ||
| if [ -n "$(declare -f command_not_found_handle)" ]; then | ||
| _mise_cmd_not_found_handle=$(declare -f command_not_found_handle) | ||
| eval "${_mise_cmd_not_found_handle/command_not_found_handle/_command_not_found_handle}" | ||
| fi | ||
|
|
||
| command_not_found_handle() { | ||
| if [[ $1 != "mise" && $1 != "mise-"* ]] && __MISE_EXE__ hook-not-found -s bash -- "$1"; then | ||
| _mise_hook | ||
| "$@" | ||
| elif [ -n "$(declare -f _command_not_found_handle)" ]; then | ||
| _command_not_found_handle "$@" | ||
| else | ||
| echo "bash: command not found: $1" >&2 | ||
| return 127 | ||
| fi | ||
| } | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # shellcheck shell=bash | ||
| if [[ "$(declare -p PROMPT_COMMAND 2>/dev/null)" == "declare -a"* ]]; then | ||
| _mise_prompt_command=() | ||
| for _mise_pc in "${PROMPT_COMMAND[@]}"; do | ||
| if [[ $_mise_pc != "_mise_hook_prompt_command" && $_mise_pc != "_mise_hook" ]]; then | ||
| _mise_prompt_command+=("$_mise_pc") | ||
| fi | ||
| done | ||
|
timothysparg marked this conversation as resolved.
|
||
| PROMPT_COMMAND=("${_mise_prompt_command[@]}") | ||
| unset _mise_prompt_command _mise_pc | ||
| elif [[ ${PROMPT_COMMAND-} == *_mise_hook_prompt_command* ]]; then | ||
| _mise_prompt_command_value="${PROMPT_COMMAND-}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//_mise_hook_prompt_command;/}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//;_mise_hook_prompt_command/}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//_mise_hook_prompt_command/}" | ||
| printf -v PROMPT_COMMAND '%s' "$_mise_prompt_command_value" | ||
| unset _mise_prompt_command_value | ||
| elif [[ ${PROMPT_COMMAND-} == *_mise_hook* ]]; then | ||
| _mise_prompt_command_value="${PROMPT_COMMAND-}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//_mise_hook;/}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//;_mise_hook/}" | ||
| _mise_prompt_command_value="${_mise_prompt_command_value//_mise_hook/}" | ||
|
timothysparg marked this conversation as resolved.
|
||
| printf -v PROMPT_COMMAND '%s' "$_mise_prompt_command_value" | ||
| unset _mise_prompt_command_value | ||
| fi | ||
|
|
||
| if declare -p chpwd_functions >/dev/null 2>&1; then | ||
| _mise_chpwd_functions=() | ||
| for _mise_f in "${chpwd_functions[@]}"; do | ||
| if [[ $_mise_f != "_mise_hook_chpwd" && $_mise_f != "_mise_hook" ]]; then | ||
| _mise_chpwd_functions+=("$_mise_f") | ||
| fi | ||
| done | ||
|
timothysparg marked this conversation as resolved.
|
||
| chpwd_functions=("${_mise_chpwd_functions[@]}") | ||
| unset _mise_chpwd_functions _mise_f | ||
| fi | ||
|
|
||
| declare -F _mise_hook_prompt_command >/dev/null && unset -f _mise_hook_prompt_command | ||
| declare -F _mise_add_prompt_command >/dev/null && unset -f _mise_add_prompt_command | ||
| declare -F _mise_hook_chpwd >/dev/null && unset -f _mise_hook_chpwd | ||
| declare -F _mise_hook >/dev/null && unset -f _mise_hook | ||
| if [ -n "${_mise_cmd_not_found_handle:-}" ]; then | ||
| eval "$_mise_cmd_not_found_handle" | ||
| unset _mise_cmd_not_found_handle | ||
| declare -F _command_not_found_handle >/dev/null && unset -f _command_not_found_handle | ||
| elif [[ "$(declare -f command_not_found_handle 2>/dev/null)" == *"hook-not-found"* ]]; then | ||
| declare -F command_not_found_handle >/dev/null && unset -f command_not_found_handle | ||
| fi | ||
| declare -F mise >/dev/null && unset -f mise | ||
|
timothysparg marked this conversation as resolved.
|
||
| unset MISE_SHELL | ||
| unset __MISE_DIFF | ||
| unset __MISE_SESSION | ||
| unset __MISE_EXE | ||
| unset __MISE_FLAGS | ||
| unset __MISE_HOOK_ENABLED | ||
| unset __MISE_BASH_CHPWD_RAN | ||
|
timothysparg marked this conversation as resolved.
|
||
| unset _mise_cmd_not_found | ||
Uh oh!
There was an error while loading. Please reload this page.