-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Read paths from an interactive & login shell #5774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
07981df
3a0938b
6d670a3
67e7796
9393073
bcaffbc
645af80
1e64341
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| pub mod analyze; | ||
| mod editor_models; | ||
| mod lang; | ||
| pub mod paths; | ||
| mod shell; | ||
| mod text_editor; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| use anyhow::Result; | ||
| use std::env; | ||
| use std::path::PathBuf; | ||
| use tokio::process::Command; | ||
| use tokio::sync::OnceCell; | ||
|
|
||
| static SHELL_PATH_DIRS: OnceCell<Result<Vec<PathBuf>, anyhow::Error>> = OnceCell::const_new(); | ||
|
|
||
| pub async fn get_shell_path_dirs() -> Result<&'static Vec<PathBuf>> { | ||
| let result = SHELL_PATH_DIRS | ||
| .get_or_init(|| async { | ||
| get_shell_path_async() | ||
| .await | ||
| .map(|path| env::split_paths(&path).collect()) | ||
| }) | ||
| .await; | ||
|
|
||
| match result { | ||
| Ok(dirs) => Ok(dirs), | ||
| Err(e) => Err(anyhow::anyhow!( | ||
| "Failed to get shell PATH directories: {}", | ||
| e | ||
| )), | ||
| } | ||
| } | ||
|
|
||
| async fn get_shell_path_async() -> Result<String> { | ||
| let shell = env::var("SHELL").unwrap_or_else(|_| { | ||
| if cfg!(windows) { | ||
| "cmd".to_string() | ||
| } else { | ||
| "/bin/bash".to_string() | ||
| } | ||
| }); | ||
|
|
||
| { | ||
| #[cfg(windows)] | ||
|
||
| { | ||
| get_windows_path_async(&shell).await | ||
| } | ||
| #[cfg(not(windows))] | ||
| { | ||
| get_unix_path_async(&shell).await | ||
| } | ||
| } | ||
| .or_else(|e| { | ||
| tracing::warn!( | ||
| "Failed to get PATH from shell ({}), falling back to current PATH", | ||
| e | ||
| ); | ||
| env::var("PATH").map_err(|_| anyhow::anyhow!("No PATH variable available")) | ||
| }) | ||
| } | ||
|
|
||
| #[cfg(not(windows))] | ||
| async fn get_unix_path_async(shell: &str) -> Result<String> { | ||
| let flags = if shell.ends_with("fish") { | ||
| vec!["-l", "-c"] | ||
| } else { | ||
| vec!["-l", "-i", "-c"] | ||
| }; | ||
| let output = Command::new(shell) | ||
| .args(flags) | ||
| .output() | ||
| .await | ||
| .map_err(|e| anyhow::anyhow!("Failed to execute shell command: {}", e))?; | ||
|
|
||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| return Err(anyhow::anyhow!("Shell command failed: {}", stderr)); | ||
| } | ||
|
|
||
| let path = String::from_utf8(output.stdout) | ||
| .map_err(|e| anyhow::anyhow!("Invalid UTF-8 in shell output: {}", e))? | ||
| .trim() | ||
| .to_string(); | ||
|
|
||
|
||
| if path.is_empty() { | ||
| return Err(anyhow::anyhow!("Shell returned empty PATH")); | ||
| } | ||
|
|
||
| Ok(path) | ||
| } | ||
|
|
||
| #[cfg(windows)] | ||
| async fn get_windows_path_async(shell: &str) -> Result<String> { | ||
| let shell_name = std::path::Path::new(shell) | ||
| .file_stem() | ||
| .and_then(|s| s.to_str()) | ||
| .unwrap_or("cmd"); | ||
|
|
||
| let output = match shell_name { | ||
| "pwsh" | "powershell" => { | ||
| Command::new(shell) | ||
| .args(["-NoLogo", "-Command", "$env:PATH"]) | ||
| .output() | ||
| .await | ||
| } | ||
| _ => { | ||
| Command::new(shell) | ||
| .args(["/c", "echo %PATH%"]) | ||
| .output() | ||
| .await | ||
| } | ||
| }; | ||
|
|
||
| let output = output.map_err(|e| anyhow::anyhow!("Failed to execute shell command: {}", e))?; | ||
|
|
||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
|
||
| return Err(anyhow::anyhow!("Shell command failed: {}", stderr)); | ||
| } | ||
|
|
||
| let path = String::from_utf8(output.stdout) | ||
| .map_err(|e| anyhow::anyhow!("Invalid UTF-8 in shell output: {}", e))? | ||
| .trim() | ||
| .to_string(); | ||
|
|
||
| if path.is_empty() { | ||
| return Err(anyhow::anyhow!("Shell returned empty PATH")); | ||
| } | ||
|
|
||
| Ok(path) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could use a macro, but then I think this is probably fine (and probably less lines of code)