From e10df4ba10e6f87e1d56230453a035087f4883ff Mon Sep 17 00:00:00 2001 From: Dylan Hurd Date: Mon, 2 Mar 2026 12:08:17 -0700 Subject: [PATCH] fix(core) shell_snapshot multiline exports (#12642) ## Summary Codex discovered this one - shell_snapshot tests were breaking on my machine because I had a multiline env var. We should handle these! ## Testing - [x] existing tests pass - [x] Updated unit tests --- codex-rs/core/src/shell_snapshot.rs | 64 +++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/codex-rs/core/src/shell_snapshot.rs b/codex-rs/core/src/shell_snapshot.rs index 7dc122e93..291129f97 100644 --- a/codex-rs/core/src/shell_snapshot.rs +++ b/codex-rs/core/src/shell_snapshot.rs @@ -360,19 +360,17 @@ alias_count=$(alias -p | wc -l | tr -d ' ') echo "# aliases $alias_count" alias -p echo '' -export_lines=$(export -p | awk ' -/^(export|declare -x|typeset -x) / { - line=$0 - name=line - sub(/^(export|declare -x|typeset -x) /, "", name) - sub(/=.*/, "", name) - if (name ~ /^(EXCLUDED_EXPORTS)$/) { - next - } - if (name ~ /^[A-Za-z_][A-Za-z0-9_]*$/) { - print line - } -}') +export_lines=$( + while IFS= read -r name; do + if [[ "$name" =~ ^(EXCLUDED_EXPORTS)$ ]]; then + continue + fi + if [[ ! "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then + continue + fi + declare -xp "$name" 2>/dev/null || true + done < <(compgen -e) +) export_count=$(printf '%s\n' "$export_lines" | sed '/^$/d' | wc -l | tr -d ' ') echo "# exports $export_count" if [ -n "$export_lines" ]; then @@ -671,6 +669,46 @@ mod tests { Ok(()) } + #[cfg(unix)] + #[test] + fn bash_snapshot_preserves_multiline_exports() -> Result<()> { + let multiline_cert = "-----BEGIN CERTIFICATE-----\nabc\n-----END CERTIFICATE-----"; + let output = Command::new("/bin/bash") + .arg("-c") + .arg(bash_snapshot_script()) + .env("BASH_ENV", "/dev/null") + .env("MULTILINE_CERT", multiline_cert) + .output()?; + + assert!(output.status.success()); + + let stdout = String::from_utf8_lossy(&output.stdout); + assert!( + stdout.contains("MULTILINE_CERT=") || stdout.contains("MULTILINE_CERT"), + "snapshot should include the multiline export name" + ); + + let dir = tempdir()?; + let snapshot_path = dir.path().join("snapshot.sh"); + std::fs::write(&snapshot_path, stdout.as_bytes())?; + + let validate = Command::new("/bin/bash") + .arg("-c") + .arg("set -e; . \"$1\"") + .arg("bash") + .arg(&snapshot_path) + .env("BASH_ENV", "/dev/null") + .output()?; + + assert!( + validate.status.success(), + "snapshot validation failed: {}", + String::from_utf8_lossy(&validate.stderr) + ); + + Ok(()) + } + #[cfg(unix)] #[tokio::test] async fn try_new_creates_and_deletes_snapshot_file() -> Result<()> {