Skip to content

fix(pty-proxy): resolve SHELL path in proxied zsh sessions#3607

Open
hangrymuppet wants to merge 1 commit into
atuinsh:mainfrom
hangrymuppet:issue/pty-proxy-bad-shell-env
Open

fix(pty-proxy): resolve SHELL path in proxied zsh sessions#3607
hangrymuppet wants to merge 1 commit into
atuinsh:mainfrom
hangrymuppet:issue/pty-proxy-bad-shell-env

Conversation

@hangrymuppet

Copy link
Copy Markdown

Fixes self-reported #3606; regression introduced after #3548

Thank you for maintaining this awesome piece of work!

Checks

  • I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle
  • I have checked that there are no existing pull requests for the same thing

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Fossier: Manual Review Requested

@hangrymuppet is a new contributor. A maintainer should review this PR before merging.

Score Breakdown

Total Score: 58.2/100 | Confidence: 74% | Outcome: REVIEW

Signal Value Score Weight
account_age 4397 1.00 0.09
public_repos 32 1.00 0.05
contribution_history 37 0.18 0.05
follower_ratio 0.0 0.00 0.05
bot_signals False 0.50 0.04
open_prs_elsewhere 0 FAILED (Search failed) 0.09
closed_prs_elsewhere 0 FAILED (Search failed) 0.10
merged_prs_elsewhere 0 FAILED (Search failed) 0.08
prior_interaction 0 0.00 0.08
activity_velocity 0 1.00 0.08
pr_content ... 1.00 0.08
commit_email no_email 0.50 0.04
pr_description ... 0.70 0.05
repo_stars 30518 0.30 0.04
org_membership 0 0.20 0.03
commit_verification ... 1.00 0.04
contributor_stars 0 0.00 0.04

@greptile-apps

greptile-apps Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes how proxied zsh sessions choose the shell path. The main changes are:

  • Normalize ZSH_ARGZERO before forwarding it.
  • Prefer a matching executable $SHELL for bare zsh names.
  • Fall back to resolving the zsh name through command -v.
  • Update the generated-script unit test expectations.

Confidence Score: 4/5

The changed flow looks mergeable after tightening the zsh path selection.

  • The generated script can prefer $SHELL by basename when it may not be the zsh binary that launched the current session.
  • The issue is limited to zsh setups with multiple installed zsh paths or a stale login shell value.

crates/atuin-pty-proxy/src/pty_proxy.rs

Important Files Changed

Filename Overview
crates/atuin-pty-proxy/src/pty_proxy.rs Updates zsh shell-path resolution in the generated pty-proxy init script and adjusts string-based tests.

Reviews (1): Last reviewed commit: "fix(pty-proxy): resolve SHELL path in pr..." | Re-trigger Greptile

Comment thread crates/atuin-pty-proxy/src/pty_proxy.rs Outdated
@hangrymuppet hangrymuppet force-pushed the issue/pty-proxy-bad-shell-env branch from 2c7fc8b to 520ceea Compare July 11, 2026 02:14
    - Add target-specific parent process executable path resolution (via
      proc_pidpath on macOS and /proc on Linux/Solaris/Illumos).
    - Dynamically embed the resolved absolute path of the running zsh
      binary in the generated init script.
    - Resolve relative shell names to absolute paths in Rust before
      spawning, ensuring $SHELL is populated correctly.
    - Add a unit test to verify target-specific process path resolution
      logic on the active OS.

    Fixes atuinsh#3606
    Link: atuinsh#3606
@hangrymuppet hangrymuppet force-pushed the issue/pty-proxy-bad-shell-env branch from 520ceea to 8bd8ab5 Compare July 11, 2026 02:20
Comment on lines +38 to +55
let shell_path = if let Some(ref path) = options.shell {
if path.is_absolute() {
Some(path.clone())
} else if let Ok(paths) = std::env::var("PATH") {
// Resolve via $PATH
std::env::split_paths(&paths)
.map(|p| p.join(path))
.find(|p| p.is_file())
// Fallback if not found in $PATH
.or_else(|| Some(path.clone()))
} else {
// No $PATH variable
Some(path.clone())
}
} else {
// No shell specified
None
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let shell_path = if let Some(ref path) = options.shell {
if path.is_absolute() {
Some(path.clone())
} else if let Ok(paths) = std::env::var("PATH") {
// Resolve via $PATH
std::env::split_paths(&paths)
.map(|p| p.join(path))
.find(|p| p.is_file())
// Fallback if not found in $PATH
.or_else(|| Some(path.clone()))
} else {
// No $PATH variable
Some(path.clone())
}
} else {
// No shell specified
None
};
let shell_path = match options.shell {
Some(ref path) => if path.is_absolute() {
Some(path.clone())
} else if let Ok(paths) = std::env::var("PATH") {
// Resolve via $PATH
std::env::split_paths(&paths)
.map(|p| p.join(path))
.find(|p| p.is_file())
// Fallback if not found in $PATH
.or_else(|| Some(path.clone()))
} else {
// No $PATH variable
Some(path.clone())
}
// No shell specified
None => None,
};

Or even use options.shell.as_ref().map(|path| { /* ... */ }) maybe ?

fn get_parent_shell_path(target_shell: Shell) -> Option<String> {
#[cfg(unix)]
{
let ppid = unsafe { libc::getppid() };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let ppid = unsafe { libc::getppid() };
let ppid = std::os::unix::process::parent_id();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change it's also possible to make the libc dep macos-only, see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies

#[cfg(target_os = "macos")]
let path = {
let mut buf = [0u8; 4096];
let res = unsafe {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add // Safety: comments on every unsafe block ?

Comment on lines +188 to +193
if let Some(path) = path
&& let Some(detected_shell) = shell_from_name(&path.to_string_lossy())
&& detected_shell == target_shell
{
return Some(path.to_string_lossy().into_owned());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let Some(path) = path
&& let Some(detected_shell) = shell_from_name(&path.to_string_lossy())
&& detected_shell == target_shell
{
return Some(path.to_string_lossy().into_owned());
}
if let Some(path) = path
&& let path = path.to_string_lossy()
&& let Some(detected_shell) = shell_from_name(&path)
&& detected_shell == target_shell
{
return Some(path.into_owned());
}

Avoids doing the to_string_lossy() twice (which does a UTF-8 check each time)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants