diff --git a/desktop-client/scripts/build-tunnel-helper.sh b/desktop-client/scripts/build-tunnel-helper.sh index 00e7f09..ce5deb6 100644 --- a/desktop-client/scripts/build-tunnel-helper.sh +++ b/desktop-client/scripts/build-tunnel-helper.sh @@ -76,4 +76,21 @@ else cargo build --manifest-path "${HELPER_DIR}/Cargo.toml" --release --target "${TARGET}" fi cp "${HELPER_DIR}/target/${TARGET}/release/${OUTPUT_NAME}" "${OUTPUT_DIR}/${OUTPUT_NAME}" + +if [ "${TARGET}" = "aarch64-apple-darwin" ]; then + WG_BIN="$(command -v wg || true)" + WG_QUICK_BIN="$(command -v wg-quick || true)" + + if [ -z "${WG_BIN}" ] || [ -z "${WG_QUICK_BIN}" ]; then + echo "macOS bundle requires wg and wg-quick to be installed on the build machine." + echo "Install them with:" + echo " brew install wireguard-tools" + exit 1 + fi + + cp "${WG_BIN}" "${OUTPUT_DIR}/wg" + cp "${WG_QUICK_BIN}" "${OUTPUT_DIR}/wg-quick" + chmod +x "${OUTPUT_DIR}/wg" "${OUTPUT_DIR}/wg-quick" +fi + echo "Bundled ${OUTPUT_NAME} into ${OUTPUT_DIR}" diff --git a/desktop-client/tunnel-helper/src/main.rs b/desktop-client/tunnel-helper/src/main.rs index 8a8d84b..909a9bf 100644 --- a/desktop-client/tunnel-helper/src/main.rs +++ b/desktop-client/tunnel-helper/src/main.rs @@ -529,7 +529,9 @@ fn connect_direct(profile: &Path) -> Result<(), String> { #[cfg(target_os = "macos")] { - let command = format!("wg-quick up '{}'", profile.display()); + let wg_quick = find_wg_quick()?; + let path_prefix = macos_command_path_prefix(&wg_quick); + let command = format!("PATH='{}:$PATH' '{}' up '{}'", path_prefix, wg_quick.display(), profile.display()); let status = Command::new("osascript") .arg("-e") .arg(format!("do shell script \"{}\" with administrator privileges", command)) @@ -567,7 +569,9 @@ fn disconnect_direct(profile: &Path) -> Result<(), String> { #[cfg(target_os = "macos")] { - let command = format!("wg-quick down '{}'", profile.display()); + let wg_quick = find_wg_quick()?; + let path_prefix = macos_command_path_prefix(&wg_quick); + let command = format!("PATH='{}:$PATH' '{}' down '{}'", path_prefix, wg_quick.display(), profile.display()); let status = Command::new("osascript") .arg("-e") .arg(format!("do shell script \"{}\" with administrator privileges", command)) @@ -808,9 +812,59 @@ fn find_wg_cli() -> Result { #[cfg(target_os = "macos")] fn find_wg_cli() -> Result { + if let Some(path) = bundled_macos_tool("wg") { + return Ok(path); + } + + let candidates = [ + PathBuf::from("/opt/homebrew/bin/wg"), + PathBuf::from("/usr/local/bin/wg"), + PathBuf::from("/usr/bin/wg"), + ]; + + if let Some(path) = candidates.into_iter().find(|path| path.exists()) { + return Ok(path); + } + Ok(PathBuf::from("wg")) } +#[cfg(target_os = "macos")] +fn find_wg_quick() -> Result { + if let Some(path) = bundled_macos_tool("wg-quick") { + return Ok(path); + } + + let candidates = [ + PathBuf::from("/opt/homebrew/bin/wg-quick"), + PathBuf::from("/usr/local/bin/wg-quick"), + PathBuf::from("/usr/bin/wg-quick"), + ]; + + candidates + .into_iter() + .find(|path| path.exists()) + .or_else(|| Some(PathBuf::from("wg-quick"))) + .ok_or_else(|| "required macOS tunnel runtime is not available".to_string()) +} + +#[cfg(target_os = "macos")] +fn bundled_macos_tool(name: &str) -> Option { + let current_exe = std::env::current_exe().ok()?; + let dir = current_exe.parent()?; + let path = dir.join(name); + path.exists().then_some(path) +} + +#[cfg(target_os = "macos")] +fn macos_command_path_prefix(tool_path: &Path) -> String { + let parent = tool_path + .parent() + .map(|path| path.display().to_string()) + .unwrap_or_else(|| "/opt/homebrew/bin".to_string()); + format!("{parent}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin") +} + #[cfg(target_os = "windows")] fn find_windows_wireguard() -> Result { let candidates = [