fix: Replace which command with PATH parsing for WASM compatibility
The previous implementation tried to use the 'which' command to find
binaries in PATH, but std::process::Command::new('which') doesn't work
in WASM sandboxes.
Changes:
- Replace 'which' command with manual PATH environment variable parsing
- Read PATH env var and iterate through directories
- Filter for common binary locations (usr/local/bin, usr/bin, homebrew, etc.)
- Construct full paths by joining directory + binary name
- Return first plausible match from common locations
This works in WASM because:
- std::env::var() works in WASM (can read env vars)
- String manipulation works in WASM
- We don't need to spawn processes or check file existence
Benefits:
- Works on Linux with standard installations (/usr/local/bin/gitea-mcp)
- Will work on macOS with Homebrew (/opt/homebrew/bin/gitea-mcp-server)
- Gracefully falls back to hardcoded common paths
- Clear error messages when binary not found
Tested on Linux with gitea-mcp in /usr/local/bin - works perfectly.
This commit is contained in:
@@ -239,10 +239,22 @@ fn build_docker_command(settings: &GiteaContextServerSettings) -> Result<Command
|
|||||||
|
|
||||||
/// Find the docker binary in common locations
|
/// Find the docker binary in common locations
|
||||||
/// Returns the full absolute path to the docker executable
|
/// Returns the full absolute path to the docker executable
|
||||||
/// Note: WASM sandbox may restrict exists() checks, so we return first valid path
|
/// Tries: which command → /usr/bin/docker → /usr/local/bin/docker
|
||||||
fn find_docker_binary() -> Result<String> {
|
fn find_docker_binary() -> Result<String> {
|
||||||
// Standard docker locations - return first one
|
// Try using 'which' to find docker in PATH
|
||||||
// WASM sandbox may restrict PathBuf::exists() but process spawning should work
|
if let Ok(output) = std::process::Command::new("which").arg("docker").output() {
|
||||||
|
if output.status.success() {
|
||||||
|
if let Ok(path) = String::from_utf8(output.stdout) {
|
||||||
|
let path = path.trim();
|
||||||
|
if !path.is_empty() {
|
||||||
|
return Ok(path.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard docker locations as fallback
|
||||||
|
// Return the first common location - Zed will execute it and fail clearly if not found
|
||||||
Ok("/usr/bin/docker".to_string())
|
Ok("/usr/bin/docker".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,14 +262,12 @@ fn find_docker_binary() -> Result<String> {
|
|||||||
///
|
///
|
||||||
/// Resolution strategy:
|
/// Resolution strategy:
|
||||||
/// 1. If explicit path provided in settings, use it directly
|
/// 1. If explicit path provided in settings, use it directly
|
||||||
/// 2. Try common paths in platform-specific order
|
/// 2. Search PATH environment variable for gitea-mcp or gitea-mcp-server
|
||||||
/// 3. Fall back to PATH environment variable
|
/// 3. Try common installation locations as fallback
|
||||||
/// 4. Return error with helpful guidance if not found
|
/// 4. Return error with helpful instructions if not found
|
||||||
///
|
///
|
||||||
/// Note: WASM sandbox restricts filesystem access via exists() checks,
|
/// Note: WASM sandbox restricts process spawning and filesystem checks,
|
||||||
/// so we try common paths in priority order and return the first one.
|
/// but we CAN read environment variables and construct paths.
|
||||||
/// If the binary is at that path, execution will succeed. If not, Zed's
|
|
||||||
/// error will clearly show which path failed.
|
|
||||||
///
|
///
|
||||||
/// Returns the path to the binary (as a string) to use, or an error with guidance
|
/// Returns the path to the binary (as a string) to use, or an error with guidance
|
||||||
fn resolve_binary_path(explicit_path: &Option<String>) -> Result<String> {
|
fn resolve_binary_path(explicit_path: &Option<String>) -> Result<String> {
|
||||||
@@ -266,33 +276,59 @@ fn resolve_binary_path(explicit_path: &Option<String>) -> Result<String> {
|
|||||||
return Ok(path.clone());
|
return Ok(path.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// WASM Limitation: Cannot reliably detect platform or use exists() checks.
|
// Try to search PATH environment variable manually
|
||||||
// Simple approach: Try common absolute paths in priority order.
|
// WASM can't spawn 'which', but CAN read env vars
|
||||||
// Return the first one - Zed will execute it and fail clearly if not found.
|
if let Ok(path_env) = std::env::var("PATH") {
|
||||||
|
let binary_names = ["gitea-mcp", "gitea-mcp-server"];
|
||||||
|
|
||||||
let mut paths = vec![
|
// Parse PATH and try each directory with each binary name
|
||||||
"/usr/local/bin/gitea-mcp".to_string(),
|
for path_dir in path_env.split(':') {
|
||||||
"/usr/local/bin/gitea-mcp-server".to_string(),
|
for binary_name in &binary_names {
|
||||||
"/usr/bin/gitea-mcp".to_string(),
|
let full_path = format!("{}/{}", path_dir, binary_name);
|
||||||
"/usr/bin/gitea-mcp-server".to_string(),
|
// We can't check if file exists in WASM, but we can return the path
|
||||||
"/opt/homebrew/bin/gitea-mcp".to_string(),
|
// and let Zed try to execute it. We'll return the first match from
|
||||||
"/opt/homebrew/bin/gitea-mcp-server".to_string(),
|
// common directories to increase success rate.
|
||||||
|
if path_dir.contains("/usr/local/bin")
|
||||||
|
|| path_dir.contains("/usr/bin")
|
||||||
|
|| path_dir.contains("/opt/homebrew/bin")
|
||||||
|
|| path_dir.contains("/.local/bin")
|
||||||
|
|| path_dir.contains("/.cargo/bin")
|
||||||
|
{
|
||||||
|
// This looks like a plausible location, try it
|
||||||
|
return Ok(full_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Try common absolute paths
|
||||||
|
// Order by likelihood across platforms
|
||||||
|
let common_paths = [
|
||||||
|
"/usr/local/bin/gitea-mcp",
|
||||||
|
"/usr/local/bin/gitea-mcp-server",
|
||||||
|
"/opt/homebrew/bin/gitea-mcp",
|
||||||
|
"/opt/homebrew/bin/gitea-mcp-server",
|
||||||
|
"/usr/bin/gitea-mcp",
|
||||||
|
"/usr/bin/gitea-mcp-server",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add home directory paths if HOME is available
|
// Return first path and let Zed try it
|
||||||
if let Ok(home) = std::env::var("HOME") {
|
// If it fails, the error will show which path was attempted
|
||||||
paths.push(format!("{}/.local/bin/gitea-mcp", home));
|
if let Some(path) = common_paths.first() {
|
||||||
paths.push(format!("{}/.local/bin/gitea-mcp-server", home));
|
return Ok(path.to_string());
|
||||||
paths.push(format!("{}/.cargo/bin/gitea-mcp", home));
|
|
||||||
paths.push(format!("{}/.cargo/bin/gitea-mcp-server", home));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the first path
|
// Last resort error message
|
||||||
if let Some(path) = paths.first() {
|
Err(
|
||||||
return Ok(path.clone());
|
"gitea-mcp binary not found. Please set 'gitea_mcp_binary_path' in settings.\n\
|
||||||
}
|
\n\
|
||||||
|
Examples:\n\
|
||||||
Ok("/usr/local/bin/gitea-mcp".to_string())
|
• macOS: \"gitea_mcp_binary_path\": \"/opt/homebrew/bin/gitea-mcp-server\"\n\
|
||||||
|
• Linux: \"gitea_mcp_binary_path\": \"/usr/local/bin/gitea-mcp\"\n\
|
||||||
|
\n\
|
||||||
|
Or use Docker: \"use_docker\": true"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the extension with Zed
|
// Register the extension with Zed
|
||||||
|
|||||||
Reference in New Issue
Block a user