From 5d7d1d49f45177b34bf54da5be5f30edfe2fe549 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Sun, 31 May 2026 22:37:43 -0700 Subject: [PATCH] fix(core): parse zsh export option groups generically --- codex-rs/core/src/shell_snapshot.rs | 12 ++++-------- codex-rs/core/src/shell_snapshot_tests.rs | 13 ++++++++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/codex-rs/core/src/shell_snapshot.rs b/codex-rs/core/src/shell_snapshot.rs index 55a69c61b0..f77793afa8 100644 --- a/codex-rs/core/src/shell_snapshot.rs +++ b/codex-rs/core/src/shell_snapshot.rs @@ -342,14 +342,10 @@ export_lines=$(export -p | awk ' line=$0 name=line sub(/^(export|declare -x|typeset -x) /, "", name) - if (name ~ /^-[A-Za-z]*T[A-Za-z]* [A-Za-z_][A-Za-z0-9_]* [A-Za-z_][A-Za-z0-9_]*=/) { - # Zsh prints tied parameters like PATH/path as `export -T NAME tied=(...)`; - # the option group may include flags such as `-UT`. - sub(/^-[A-Za-z]*T[A-Za-z]* /, "", name) - sub(/ .*/, "", name) - } else { - sub(/=.*/, "", name) - } + # Zsh prefixes attributed exports with an option group and prints tied + # parameters with another name, as in `export -UT PATH path=(...)`. + sub(/^-[^ ]+ /, "", name) + sub(/[ =].*/, "", name) if (name ~ /^(EXCLUDED_EXPORTS)$/) { next } diff --git a/codex-rs/core/src/shell_snapshot_tests.rs b/codex-rs/core/src/shell_snapshot_tests.rs index d098e73e9c..ac2e990921 100644 --- a/codex-rs/core/src/shell_snapshot_tests.rs +++ b/codex-rs/core/src/shell_snapshot_tests.rs @@ -199,7 +199,7 @@ fn zsh_snapshot_preserves_tied_path_exports() -> Result<()> { let expected_path = format!("{}:/usr/bin:/bin", tool_dir.display()); std::fs::write( dir.path().join(".zshrc"), - format!("{zshrc_prefix}export PATH='{expected_path}'\n"), + format!("{zshrc_prefix}export PATH='{expected_path}'\ninteger -x SNAPSHOT_NUMBER=7\n"), )?; let output = Command::new("/bin/zsh") @@ -214,13 +214,20 @@ fn zsh_snapshot_preserves_tied_path_exports() -> Result<()> { stdout.contains(expected_export), "snapshot should include {expected_export:?}; stdout={stdout:?}" ); + assert!( + stdout.contains("export -i SNAPSHOT_NUMBER=7"), + "snapshot should include attributed integer export; stdout={stdout:?}" + ); let snapshot_path = dir.path().join("snapshot.sh"); std::fs::write(&snapshot_path, stdout.as_bytes())?; let validate = Command::new("/bin/zsh") .arg("-fc") - .arg("PATH=/should/not/survive; . \"$1\"; print -r -- \"$PATH\"") + .arg( + "PATH=/should/not/survive; SNAPSHOT_NUMBER=9; . \"$1\"; \ + print -r -- \"$PATH\"; print -r -- \"$SNAPSHOT_NUMBER\"", + ) .arg("zsh") .arg(&snapshot_path) .output()?; @@ -232,7 +239,7 @@ fn zsh_snapshot_preserves_tied_path_exports() -> Result<()> { assert_eq!( String::from_utf8_lossy(&validate.stdout).trim(), - expected_path + format!("{expected_path}\n7") ); }