From 591368d80bd297c841a53507f400a344e773e2b2 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Wed, 7 Oct 2026 21:01:32 +0800 Subject: [PATCH] fix(pane): don't hand the AppImage runtime's ARGV0 to the pane (#1100) (#1111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AppImage runtime exports `ARGV0` (the path the image was launched by) into tty7's environment, and every pane inherited it. zsh treats an exported `ARGV0` as the argv[0] of each external command it runs, so in a pane every program was told it was `tty7-….AppImage`. Volta's shims choose the tool from argv[0], so `node`, `npm` and every global package failed with `Could not find executable "tty7-….AppImage"`. Turning shell integration off would not have helped. `env -u TTY7_SHELL_INTEGRATION node` only worked because `env` execs node with a correct argv[0], and `env -u` with any variable name works just as well. Panes now start without `ARGV0`. The removal happens before the configured `env` block is applied, so an explicit setting there still wins. Reproduced on Linux (Debian arm64, zsh, Volta 2.0.2) with tty7-server started under the environment an AppImage launch leaves behind: the shims fail with the line removed and work with it in place. --- CHANGELOG.md | 7 +++++++ crates/tty7-core/src/daemon/pane.rs | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739658c2..9fd7f031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Volta shims, and anything else that reads its own argv[0], work again in + the Linux AppImage** (#1100). The AppImage runtime exports `ARGV0` into + tty7's environment and every pane inherited it. zsh gives an exported + `ARGV0` to every external command as its argv[0], so `node` started up as + `tty7-….AppImage` and Volta answered `Could not find executable`. Panes now + start without `ARGV0`. An `env` entry in the config can still set it. + - **The macOS traffic lights sit on the same line as the tiles beside them.** The buttons AppKit draws are 14 points tall and gpui hangs the top of the frame `y` points below the window's top edge, so the lights' centre falls diff --git a/crates/tty7-core/src/daemon/pane.rs b/crates/tty7-core/src/daemon/pane.rs index 9b89e952..8c5dac72 100644 --- a/crates/tty7-core/src/daemon/pane.rs +++ b/crates/tty7-core/src/daemon/pane.rs @@ -578,6 +578,16 @@ fn apply_common_command_setup( #[cfg(unix)] cmd.env("PWD", dir); } + // The AppImage runtime exports `ARGV0` (the path the image was launched + // by) into tty7's own environment, and the pane would inherit it. zsh + // treats an exported `ARGV0` as the argv[0] of every external command it + // runs, so each program in the pane is told it is `tty7-….AppImage` — and + // multiplexers like Volta's shims, which pick the tool from argv[0], fail + // with "Could not find executable" (#1100). Nothing outside a single + // command line ever wants it set, so the pane starts without it; the + // configured `env` block, applied below, can still put it back. + cmd.env_remove("ARGV0"); + let extra_env = crate::core::config::extra_env(); // Windows hands every process a private copy of the environment at spawn @@ -4137,6 +4147,17 @@ mod tests { /// `cwd()` alone loses that: the shell falls back to `getcwd()` and one /// tab reads `/tmp/x` while the tab opened from it reads `/private/tmp/x`. /// `PWD` is what carries the name across, and what the shell checks. + /// An AppImage launch leaves `ARGV0` in the daemon's environment; a zsh + /// that inherits it renames every command it runs, which breaks Volta's + /// shims (#1100). + #[test] + fn the_appimage_runtimes_argv0_does_not_reach_the_pane() { + let mut cmd = CommandBuilder::new("/bin/sh"); + cmd.env("ARGV0", "./tty7-26.9.5-linux-x86_64.AppImage"); + apply_common_command_setup(&mut cmd, &None, 1, None, "sh"); + assert_eq!(cmd.get_env("ARGV0"), None); + } + #[cfg(unix)] #[test] fn an_inherited_directory_keeps_the_name_it_was_reached_by() {