Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/command_line.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ When `HERDR_ENV=1` and `HERDR_PANE_ID` are present, the script includes a guarde
| Variable | Description |
|----------|-------------|
| `HOME` | Used to resolve default tries path (`$HOME/src/tries`) |
| `SHELL` | Used by `init` to detect shell type |
| `SHELL` | Fallback used by `init` when the invoking shell cannot be detected |
| `NO_COLOR` | If set, disables colors (equivalent to `--no-colors`) |
| `HERDR_ENV`, `HERDR_PANE_ID`, `HERDR_WORKSPACE_ID` | Identify the current Herdr panel and workspace for renaming |
| `CMUX_SOCKET_PATH`, `CMUX_BUNDLE_ID` | Identify cmux for tab renaming |
Expand Down
6 changes: 3 additions & 3 deletions spec/init_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The shell function wrapper is necessary because:

## Shell Detection

The init command should detect the user's shell via the `$SHELL` environment variable and output the appropriate function syntax.
The init command should detect the shell that invokes it from the parent process and output the appropriate function syntax. If the parent process cannot be determined or is not a shell, it should fall back to the user's configured shell in `$SHELL`.

Supported shells:
- **Bash/Zsh**: POSIX-compatible function syntax
Expand Down Expand Up @@ -96,8 +96,8 @@ Test that init produces valid shell syntax:
# Test Bash syntax
bash -n <(try init)

# Test Fish syntax (if fish is available)
fish -n <(SHELL=/usr/bin/fish try init)
# Test Fish syntax, even when the login shell is different
env SHELL=/bin/zsh fish -c 'try init | fish --no-execute'
```

Test that the wrapper works correctly:
Expand Down
8 changes: 6 additions & 2 deletions spec/tests/test_14_init_shells.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ else
fail "bash function should include --path with specified path" "--path '$TEST_TRIES'" "$output" "init_spec.md"
fi

# Test: init with fish shell emits fish function
output=$(SHELL=/usr/bin/fish try_run init "$TEST_TRIES" 2>&1)
# Test: init invoked by fish emits fish function
FAKE_PS_DIR=$(mktemp -d)
printf '#!/bin/sh\nprintf "fish\\n"\n' > "$FAKE_PS_DIR/ps"
chmod +x "$FAKE_PS_DIR/ps"
output=$(PATH="$FAKE_PS_DIR:$PATH" SHELL=/bin/zsh try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "function try"; then
pass
else
fail "init with fish should emit fish function" "function try" "$output" "init_spec.md"
fi
rm -rf "$FAKE_PS_DIR"

# Test: init output contains the real, full path to try binary
output=$(SHELL=/bin/bash try_run init "$TEST_TRIES" 2>&1)
Expand Down
37 changes: 34 additions & 3 deletions spec/tests/test_34_shell_init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,45 @@

section "shell-init"

# Test: SHELL=fish emits fish function
output=$(SHELL=/usr/local/bin/fish try_run init "$TEST_TRIES" 2>&1)
# Test: the invoking shell takes precedence over the login shell in $SHELL
FAKE_PS_DIR=$(mktemp -d)
printf '#!/bin/sh\nprintf "fish\\n"\n' > "$FAKE_PS_DIR/ps"
chmod +x "$FAKE_PS_DIR/ps"
output=$(PATH="$FAKE_PS_DIR:$PATH" SHELL=/bin/zsh try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "function try"; then
pass
else
fail "SHELL=fish should emit fish function" "function try" "$output" "shell_init"
fail "fish parent should override SHELL=zsh" "function try" "$output" "shell_init"
fi

# Test: a non-fish parent also takes precedence over SHELL=fish
printf '#!/bin/sh\nprintf "zsh\\n"\n' > "$FAKE_PS_DIR/ps"
output=$(PATH="$FAKE_PS_DIR:$PATH" SHELL=/usr/local/bin/fish try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "try() {"; then
pass
else
fail "zsh parent should override SHELL=fish" "try() {" "$output" "shell_init"
fi

# Test: SHELL is used when the parent shell cannot be detected
printf '#!/bin/sh\n' > "$FAKE_PS_DIR/ps"
output=$(PATH="$FAKE_PS_DIR:$PATH" SHELL=/usr/local/bin/fish try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "function try"; then
pass
else
fail "SHELL=fish should be the fallback" "function try" "$output" "shell_init"
fi

# Test: a non-shell parent (e.g. a script runner) is ignored in favor of SHELL
printf '#!/bin/sh\nprintf "ruby\\n"\n' > "$FAKE_PS_DIR/ps"
output=$(PATH="$FAKE_PS_DIR:$PATH" SHELL=/usr/local/bin/fish try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "function try"; then
pass
else
fail "non-shell parent should fall back to SHELL=fish" "function try" "$output" "shell_init"
fi
rm -rf "$FAKE_PS_DIR"

# Test: SHELL=zsh emits bash/zsh function
output=$(SHELL=/bin/zsh try_run init "$TEST_TRIES" 2>&1)
if echo "$output" | grep -q "try() {"; then
Expand Down
4 changes: 2 additions & 2 deletions spec/tests/test_36_shell_eval.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fi

if command -v nix-shell >/dev/null 2>&1; then
# Test: fish can eval the init output and defines try function
fish_out=$(nix-shell -p fish --run "SHELL=fish fish -c 'eval ($TRY_BIN_PATH init --path $EVAL_DIR | string collect); type try'" 2>&1)
fish_out=$(nix-shell -p fish --run "SHELL=/bin/zsh fish -c 'eval ($TRY_BIN_PATH init --path $EVAL_DIR | string collect); type try'" 2>&1)
if echo "$fish_out" | grep -qi "try is a function\|function try"; then
pass
else
Expand All @@ -81,7 +81,7 @@ if command -v nix-shell >/dev/null 2>&1; then
fi

# Test: fish init output is valid fish syntax (no parse errors)
fish_syntax=$(SHELL=fish "$TRY_BIN_PATH" init --path "$EVAL_DIR" 2>&1)
fish_syntax=$(nix-shell -p fish --run "SHELL=/bin/zsh fish -c '$TRY_BIN_PATH init --path $EVAL_DIR'" 2>&1)
fish_parse=$(echo "$fish_syntax" | nix-shell -p fish --run "fish --no-execute" 2>&1)
if [ $? -eq 0 ]; then
pass
Expand Down
8 changes: 3 additions & 5 deletions try.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1642,12 +1642,10 @@ def resolve_unique_name_with_versioning(tries_path, date_prefix, base)
end

# shell detection for init wrapper
# Check $SHELL first (user's configured shell), then parent process as fallback
# Check the invoking shell first; fall back to $SHELL when the parent isn't a shell
def fish?
shell = ENV["SHELL"].to_s
if shell.empty?
shell = (`ps c -p #{Process.ppid} -o 'ucomm='`.strip rescue "").to_s
end
shell = (`ps c -p #{Process.ppid} -o 'ucomm=' 2>/dev/null`.strip rescue "").to_s
shell = ENV["SHELL"].to_s unless shell.match?(/\A-?\w*sh\z/)
shell.include?('fish')
end

Expand Down