дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
fix(process): give child processes the login shell's variables the IDE lacks (#1195)
Finder/Dock-launched apps only got PATH from the login shell; children outside run configs, LSP and DAP missed rc exports such as GOMODCACHE, so go list showed only some modules. host_env now adds the shell's variables the process lacks (call-site values win); the login-shell probe leaves them out so Reload Environment stays accurate. Covers every crate that spawns through ide_core::process::prepare/host_env.
Цей коміт міститься в:
1 джерело
c87d2a6b55
коміт
5b46844f88
2 files changed
+33
-6
No files matched your search
@@ -123,12 +123,14 @@ fn taskkill_tree(child: &Child) {
|
||||
/// inherits them loads the image's (older) libraries and plugins next to its own, e.g.
|
||||
/// `curl: symbol lookup error ... nghttp2_...`. The IDE process keeps its environment
|
||||
/// (WebKit and GTK still need it). Then applies the [`set_env_overlay`] variables and
|
||||
/// those of a manual HTTP proxy ([`crate::proxy_settings::ProxySettings::child_env`]).
|
||||
/// Variables the call site has already set are kept. A no-op outside an AppImage
|
||||
/// without an overlay.
|
||||
/// those of a manual HTTP proxy ([`crate::proxy_settings::ProxySettings::child_env`]),
|
||||
/// and the login shell's variables this process lacks ([`shell_env`]: an app started from
|
||||
/// Finder/Dock doesn't have what rc files export, such as `GOPATH`, `JAVA_HOME` or
|
||||
/// `MAVEN_OPTS`). Variables the call site has already set are kept.
|
||||
pub fn host_env(cmd: &mut Command) -> &mut Command {
|
||||
let fixes = child_env_changes();
|
||||
if fixes.is_empty() {
|
||||
let shell: Vec<_> = shell_env().collect();
|
||||
if fixes.is_empty() && shell.is_empty() {
|
||||
return cmd;
|
||||
}
|
||||
let explicit: Vec<OsString> = cmd.get_envs().map(|(k, _)| k.to_owned()).collect();
|
||||
@@ -138,6 +140,9 @@ pub fn host_env(cmd: &mut Command) -> &mut Command {
|
||||
None => cmd.env_remove(key),
|
||||
};
|
||||
}
|
||||
for (key, value) in shell.into_iter().filter(|(k, _)| !explicit.contains(k)) {
|
||||
cmd.env(key, value);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
@@ -154,12 +159,13 @@ pub fn host_vars() -> impl Iterator<Item = (OsString, OsString)> {
|
||||
apply_changes(std::env::vars_os().collect(), &child_env_changes()).into_iter()
|
||||
}
|
||||
|
||||
/// [`std::env::var_os`] as a child should see it (see [`host_env`]).
|
||||
/// [`std::env::var_os`] as a child should see it (see [`host_env`]), the login shell's value
|
||||
/// included when this process lacks the variable.
|
||||
pub fn host_var_os(key: impl AsRef<OsStr>) -> Option<OsString> {
|
||||
let key = key.as_ref();
|
||||
match child_env_changes().into_iter().find(|(k, _)| k == key) {
|
||||
Some((_, fixed)) => fixed,
|
||||
None => std::env::var_os(key),
|
||||
None => std::env::var_os(key).or_else(|| shell_env().find(|(k, _)| k == key).map(|(_, v)| v)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,6 +578,15 @@ mod tests {
|
||||
assert_eq!(get("HOME"), Some(None));
|
||||
let shell: Vec<_> = shell_env().map(|(k, _)| k).collect();
|
||||
assert_eq!(shell, [OsString::from("OPENIDEA_OVERLAY_B")]);
|
||||
// Every child gets what the login shell exports and the IDE process lacks (a Finder
|
||||
// launch: `GOPATH`, `JAVA_HOME`...), unless the call site sets it.
|
||||
assert_eq!(get("OPENIDEA_OVERLAY_B"), Some(Some("b".into())));
|
||||
let mut own = Command::new("true");
|
||||
own.env("OPENIDEA_OVERLAY_B", "call site");
|
||||
host_env(&mut own);
|
||||
let value = own.get_envs().find(|(n, _)| *n == "OPENIDEA_OVERLAY_B").and_then(|(_, v)| v);
|
||||
assert_eq!(value, Some(OsStr::new("call site")));
|
||||
assert_eq!(host_var_os("OPENIDEA_OVERLAY_B"), Some("b".into()));
|
||||
assert_eq!(host_var_os("PATH"), Some("/reloaded".into()));
|
||||
assert!(host_vars().all(|(k, _)| k != "HOME"));
|
||||
assert!(host_vars().any(|(k, v)| k == "OPENIDEA_OVERLAY_A" && v == "project"));
|
||||
|
||||
@@ -177,6 +177,10 @@ pub(crate) fn read_login_env(shell: &str, timeout: std::time::Duration) -> Optio
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nor what the last read found that this process lacks: children get it, the shell must not.
|
||||
for (k, _) in ide_core::process::shell_env() {
|
||||
cmd.env_remove(k);
|
||||
}
|
||||
ide_core::process::prepare(&mut cmd);
|
||||
let mut child = cmd
|
||||
.args(["-ilc", &format!("printf '{MARK}'; /usr/bin/env -0; printf '{MARK}'")])
|
||||
@@ -257,6 +261,14 @@ mod tests {
|
||||
std::fs::write(&rc, "export OPENIDEA_TEST_RC='two words'\n").unwrap();
|
||||
let second = read_login_env(&shell, Duration::from_secs(10)).unwrap();
|
||||
assert_eq!(get(&second, "OPENIDEA_TEST_RC").as_deref(), Some("two words"));
|
||||
|
||||
// What a read found goes to children, not into the next read: an rc file that no
|
||||
// longer exports a variable drops it.
|
||||
ide_core::process::set_shell_env(second);
|
||||
std::fs::write(&rc, "export OPENIDEA_TEST_SEEN=\"${OPENIDEA_TEST_RC:-unset}\"\n").unwrap();
|
||||
let third = read_login_env(&shell, Duration::from_secs(10)).unwrap();
|
||||
assert_eq!(get(&third, "OPENIDEA_TEST_RC"), None);
|
||||
assert_eq!(get(&third, "OPENIDEA_TEST_SEEN").as_deref(), Some("unset"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Заблокувати користувача