Skip to content

Commit 3b617fb

Browse files
committed
Pass host env to spawn_future
1 parent 8f01337 commit 3b617fb

3 files changed

Lines changed: 86 additions & 18 deletions

File tree

src/fakers/host_env.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,46 @@ pub async fn resolve_host_env_via_runner(
3838
}
3939
}
4040

41+
/// Resolve all host environment variables via a `CommandRunner` as `KEY=VALUE` entries.
42+
///
43+
/// This is useful when spawning processes that need the host `PATH` and other vars, e.g. VTE.
44+
pub async fn resolve_host_env_list_via_runner(runner: &CommandRunner) -> io::Result<Vec<String>> {
45+
// Prefer NUL-separated output to preserve values containing newlines.
46+
let mut cmd = Command::new("env");
47+
cmd.arg("-0");
48+
49+
if let Ok(output) = runner.output(cmd).await {
50+
if output.status.success() {
51+
let vars = output
52+
.stdout
53+
.split(|b| *b == 0)
54+
.filter_map(|entry| {
55+
if entry.is_empty() {
56+
return None;
57+
}
58+
let s = String::from_utf8_lossy(entry).to_string();
59+
if s.contains('=') { Some(s) } else { None }
60+
})
61+
.collect::<Vec<_>>();
62+
if !vars.is_empty() {
63+
return Ok(vars);
64+
}
65+
}
66+
}
67+
68+
// Fallback: newline-separated output.
69+
let output = runner.output(Command::new("env")).await?;
70+
if !output.status.success() {
71+
return Err(io::Error::other("failed to resolve host environment via `env`"));
72+
}
73+
74+
Ok(String::from_utf8_lossy(&output.stdout)
75+
.lines()
76+
.filter(|line| line.contains('='))
77+
.map(ToString::to_string)
78+
.collect())
79+
}
80+
4181
#[cfg(test)]
4282
mod tests {
4383
use super::*;
@@ -60,4 +100,16 @@ mod tests {
60100
let res = block_on(resolve_host_env_via_runner(&runner, "HOME")).unwrap();
61101
assert_eq!(res, None);
62102
}
103+
104+
#[test]
105+
fn test_resolve_host_env_list_with_null_runner_nul_separated() {
106+
let mut builder = NullCommandRunnerBuilder::new();
107+
let mut cmd = Command::new("env");
108+
cmd.arg("-0");
109+
builder.cmd_full(cmd, || Ok("HOME=/fake/home\0PATH=/nix/store/bin\0".to_string()));
110+
let runner = builder.build();
111+
112+
let res = block_on(resolve_host_env_list_via_runner(&runner)).unwrap();
113+
assert_eq!(res, vec!["HOME=/fake/home", "PATH=/nix/store/bin"]);
114+
}
63115
}

src/fakers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ mod output_tracker;
55

66
pub use command::{Command, FdMode};
77
pub use command_runner::{Child, CommandRunner, CommandRunnerEvent, NullCommandRunnerBuilder};
8-
pub use host_env::resolve_host_env_via_runner;
8+
pub use host_env::{resolve_host_env_list_via_runner, resolve_host_env_via_runner};
99
pub use output_tracker::OutputTracker;

src/widgets/integrated_terminal.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use gtk::{
66
};
77
use vte4::prelude::*;
88

9+
use crate::fakers::resolve_host_env_list_via_runner;
910
use crate::i18n::gettext;
1011
use crate::gtk_utils::ColorPalette;
1112
use crate::models::Container;
@@ -159,27 +160,42 @@ impl IntegratedTerminal {
159160
}
160161

161162
imp.reload_button.set_visible(false);
162-
let root_store = self.container().root_store();
163-
164-
// Prepare the shell command via the Distrobox backend (uses injected factory)
165-
let name = self.container().name();
166-
let enter_cmd = root_store.distrobox().enter_cmd(&name);
167-
let shell = root_store.command_runner().wrap_command(enter_cmd).to_vec();
168-
169-
let fut = imp.terminal.spawn_future(
170-
vte4::PtyFlags::DEFAULT,
171-
None,
172-
&shell.iter().filter_map(|s| s.to_str()).collect::<Vec<_>>(),
173-
&[],
174-
glib::SpawnFlags::DEFAULT,
175-
|| {},
176-
10,
177-
);
178-
179163
glib::MainContext::default().spawn_local(clone!(
180164
#[weak(rename_to=this)]
181165
self,
182166
async move {
167+
let root_store = this.container().root_store();
168+
169+
// Prepare the shell command via the Distrobox backend (uses injected factory)
170+
let name = this.container().name();
171+
let enter_cmd = root_store.distrobox().enter_cmd(&name);
172+
let command_runner = root_store.command_runner();
173+
let shell = command_runner.wrap_command(enter_cmd).to_vec();
174+
let shell_args = shell
175+
.iter()
176+
.filter_map(|s| s.to_str())
177+
.collect::<Vec<_>>();
178+
179+
let host_env = match resolve_host_env_list_via_runner(&command_runner).await {
180+
Ok(env) => env,
181+
Err(err) => {
182+
eprintln!("Failed to resolve host env for terminal: {}", err);
183+
Vec::new()
184+
}
185+
};
186+
let host_env_refs = host_env.iter().map(String::as_str).collect::<Vec<_>>();
187+
188+
dbg!(&host_env_refs);
189+
let fut = this.imp().terminal.spawn_future(
190+
vte4::PtyFlags::DEFAULT,
191+
None,
192+
&shell_args,
193+
&host_env_refs,
194+
glib::SpawnFlags::DEFAULT,
195+
|| {},
196+
10,
197+
);
198+
183199
match fut.await {
184200
Ok(pid) => {
185201
this.imp().terminal_pid.set(Some(pid));

0 commit comments

Comments
 (0)