From f108d0c8117426a005eb807eb289b783f4805ff2 Mon Sep 17 00:00:00 2001 From: CuriousTank <> Date: Wed, 28 Oct 2026 18:33:04 +0800 Subject: [PATCH] Source ~/.bashrc in bash bootstrap so user aliases are not silently dropped (#16631) The bash bootstrap emulates a login sequence (/etc/profile -> ~/.bash_profile | ~/.bash_login | ~/.profile) and never reads ~/.bashrc, even though Warp runs bash as an interactive shell. Aliases and functions that live only in ~/.bashrc therefore never exist inside Warp while working fine in other terminals. The most visible casualty: a user wrapper like alias ssh="trzsz -d ssh" is silently dropped, and the bootstrap's own ssh() function takes over interactive ssh invocations, bypassing the wrapper. Mirror the interactive startup sequence after the login files: source ~/.bashrc for interactive shells, guarded by a sentinel so users whose login rcfile already re-sources ~/.bashrc don't get it twice. Since bash resolves aliases before functions, a user ssh alias now wins over the bootstrap's ssh() function naturally; sessions without such an alias keep warpifying exactly as before. Adds an integration test (test_bash_bootstraps_sources_bashrc_aliases) with an alias living solely in ~/.bashrc, plus a ShellRcType::BashRc helper variant so tests can target ~/.bashrc. write_all_rc_files_for_test skips the new variant to keep existing 'sourced once' assertions meaningful. --- app/assets/bundled/bootstrap/bash_body.sh | 16 ++++++ crates/integration/src/bin/integration.rs | 1 + crates/integration/src/test/bootstrapping.rs | 53 +++++++++++++++++++ crates/integration/src/util.rs | 17 +++++- .../integration/shell_integration_tests.rs | 2 + 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/app/assets/bundled/bootstrap/bash_body.sh b/app/assets/bundled/bootstrap/bash_body.sh index dc3ea059d53..e96aac23e1f 100644 --- a/app/assets/bundled/bootstrap/bash_body.sh +++ b/app/assets/bundled/bootstrap/bash_body.sh @@ -1420,6 +1420,22 @@ esac source "$HOME/.profile" fi + # Warp starts bash as an interactive shell, so also mirror the + # interactive (non-login) startup sequence, which reads ~/.bashrc. + # Without this, aliases and functions defined only in ~/.bashrc never + # exist inside Warp even though they work in other terminals. Notably, + # a user alias like `alias ssh="trzsz -d ssh"` would be silently + # dropped, and the `ssh` function installed by this bootstrap script + # above would take over interactive `ssh` invocations, bypassing the + # wrapper (see issue #16631). Guard against double-sourcing for users + # whose login rcfile already re-sources ~/.bashrc (the documented + # pattern for login/interactive unification), which is harmless but + # wasteful. + if [[ $- == *i* && -f "$HOME/.bashrc" && -z ${WARP_SOURCED_BASHRC:-} ]]; then + export WARP_SOURCED_BASHRC=1 + source "$HOME/.bashrc" + fi + rcfiles_end_time="$(LC_ALL="C"; echo $EPOCHREALTIME)" fi diff --git a/crates/integration/src/bin/integration.rs b/crates/integration/src/bin/integration.rs index 4b51a919801..0aa2ad162f6 100644 --- a/crates/integration/src/bin/integration.rs +++ b/crates/integration/src/bin/integration.rs @@ -142,6 +142,7 @@ fn register_tests() -> HashMap<&'static str, BoxedBuilderFn> { register_test!(test_instant_prompt_bootstrap); register_test!(test_rc_files_only_sourced_once_during_bootstrapping); register_test!(test_cloud_agent_fallback_commands_survive_profile_path_reset); + register_test!(test_bash_bootstraps_sources_bashrc_aliases); register_test!(test_unescaped_prompt_bootstraps); register_test!(test_detect_powerlevel10k); register_test!(test_open_and_close_resource_center); diff --git a/crates/integration/src/test/bootstrapping.rs b/crates/integration/src/test/bootstrapping.rs index 607a2a06654..9a7434197b2 100644 --- a/crates/integration/src/test/bootstrapping.rs +++ b/crates/integration/src/test/bootstrapping.rs @@ -567,3 +567,56 @@ PROMPT_COMMAND=('printf "\033]0;SHAKA WHEN THE WALLS FELL\a"' 'custom_prompt') ), ) } + +/// Regression test for https://github.com/warpdotdev/warp/issues/16631. +/// +/// Warp starts bash with a custom rcfile and then sources user rcfiles in +/// login order (`/etc/profile` -> `~/.bash_profile` | `~/.bash_login` | +/// `~/.profile`). Previously, `~/.bashrc` was never sourced, so aliases and +/// functions defined only there never existed inside Warp even though they +/// work in other terminals. In particular, an `alias ssh="trzsz -d ssh"` +/// (or any other ssh wrapper) would be silently dropped and the bootstrap +/// script's own `ssh` function would take over interactive `ssh` +/// invocations, bypassing the wrapper. +/// +/// This test writes an alias solely into `~/.bashrc` (with a `.bash_profile` +/// present that does *not* re-source `.bashrc`, matching the reporter's +/// setup), then verifies after bootstrap that the alias is active and wins +/// over any same-named shell function. +pub fn test_bash_bootstraps_sources_bashrc_aliases() -> Builder { + new_builder() + .set_should_run_test(|| { + // Only run this one on bash + let (starter, _version) = current_shell_starter_and_version(); + matches!(starter.shell_type(), shell::ShellType::Bash) + }) + .with_setup(|utils| { + let dir = utils.test_dir(); + // .bash_profile exists (so login-order sourcing stops there) but does + // NOT re-source .bashrc — the exact layout from the issue report. + write_rc_files_for_test(&dir, "export WARP_TEST_LOGIN_RCFILE=1", [ShellRcType::Bash]); + // The wrapper alias under test lives only in ~/.bashrc. It shadows + // the bootstrap script's own `ssh` function: bash resolves aliases + // before functions, so if this alias is active, `ssh` cannot reach + // the function. + write_rc_files_for_test(dir, "alias ssh='echo ssh-alias-won'", [ShellRcType::BashRc]); + }) + .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) + // The login rcfile must have been sourced (sanity check of the setup). + .with_step(execute_command_for_single_terminal_in_tab( + 0, + "test \"$WARP_TEST_LOGIN_RCFILE\" = 1 && echo login-rcfile-sourced".to_string(), + ExpectedExitStatus::Success, + "login-rcfile-sourced", + )) + // If ~/.bashrc was sourced, the alias wins over the bootstrap's ssh + // function and `ssh` just echoes. Before the fix the alias never + // existed and `ssh` fell through to the bootstrap's ssh function + // (which would try to open a connection instead of printing this). + .with_step(execute_command_for_single_terminal_in_tab( + 0, + "ssh".to_string(), + ExpectedExitStatus::Success, + "ssh-alias-won", + )) +} diff --git a/crates/integration/src/util.rs b/crates/integration/src/util.rs index 42ac49d595e..cebfe759b3b 100644 --- a/crates/integration/src/util.rs +++ b/crates/integration/src/util.rs @@ -29,6 +29,10 @@ pub fn get_input_buffer( #[derive(EnumIter)] pub enum ShellRcType { Bash, + /// Bash's interactive (non-login) rcfile, `~/.bashrc`. Distinct from + /// [`ShellRcType::Bash`], which maps to the login-order files Warp + /// emulates during bootstrap (`~/.bash_profile` et al.). + BashRc, Zsh, Fish, PowerShell, @@ -39,6 +43,7 @@ impl ShellRcType { fn rc_file_paths(&self, home_dir: impl AsRef) -> Vec { let relative_paths = match self { ShellRcType::Bash => vec![Path::new(".bash_profile")], + ShellRcType::BashRc => vec![Path::new(".bashrc")], ShellRcType::Zsh => vec![Path::new(".zshrc")], ShellRcType::Fish => vec![Path::new(".config/fish/config.fish")], #[cfg(not(windows))] @@ -136,7 +141,17 @@ where P: AsRef, C: AsRef, { - write_rc_files_for_test(dir, rc_contents, ShellRcType::iter()) + // `BashRc` is bash's *second* rc file (the interactive one, alongside the + // login-order `.bash_profile` covered by `Bash`). Writing both with the + // same contents would make bash sessions source the contents twice now + // that the bootstrap mirrors the interactive startup sequence too, so + // tests that assert on rcfile side effects must opt into `.bashrc` + // explicitly via `write_rc_files_for_test`. + write_rc_files_for_test( + dir, + rc_contents, + ShellRcType::iter().filter(|rc_type| !matches!(rc_type, ShellRcType::BashRc)), + ) } /// Writes a histfile for `shell_types` to the given `dir`. diff --git a/crates/integration/tests/integration/shell_integration_tests.rs b/crates/integration/tests/integration/shell_integration_tests.rs index 6675d625ecd..da2f6051f57 100644 --- a/crates/integration/tests/integration/shell_integration_tests.rs +++ b/crates/integration/tests/integration/shell_integration_tests.rs @@ -42,6 +42,8 @@ integration_tests! { test_ps1_value_not_null_or_exit, // Tests bash-specific behavior. test_custom_ps1_expansion_bash, + // Tests bash-specific behavior: ~/.bashrc aliases survive bootstrap (#16631). + test_bash_bootstraps_sources_bashrc_aliases, test_bash_honor_ps1_expands_dynamic_prompt_once, // Tests zsh-specific behavior. test_auto_title,