From b3f2b7a92cb66bfc2f407a1369c0fb52b65f7cc4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 20 Apr 2022 10:22:20 +0200 Subject: [PATCH] Use osascript to escalate privileges and copy the CLI to /usr/local/bin --- crates/zed/src/zed.rs | 78 +++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index caecfd5a92..53a0875248 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4,7 +4,7 @@ pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; -use anyhow::Context; +use anyhow::{anyhow, Context, Result}; use breadcrumbs::Breadcrumbs; use chat_panel::ChatPanel; pub use client; @@ -16,7 +16,7 @@ use gpui::{ actions, geometry::vector::vec2f, platform::{WindowBounds, WindowOptions}, - ModelHandle, ViewContext, + AsyncAppContext, ModelHandle, ViewContext, }; use lazy_static::lazy_static; pub use lsp; @@ -26,7 +26,10 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde_json::to_string_pretty; use settings::Settings; -use std::{path::PathBuf, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; use util::ResultExt; pub use workspace; use workspace::{AppState, Workspace, WorkspaceParams}; @@ -69,25 +72,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); }); cx.add_global_action(move |_: &InstallCommandLineTool, cx| { - cx.spawn(|cx| async move { - log::info!("installing command line launcher"); - let cli_path = cx - .platform() - .path_for_auxiliary_executable("cli") - .log_err()?; - let link_path = "/opt/homebrew/bin/zed"; - smol::fs::unix::symlink(cli_path.as_path(), link_path) - .await - .context("failed to install cli symlink") - .log_err()?; - log::info!( - "created symlink {} -> {}", - link_path, - cli_path.to_string_lossy() - ); - Some(()) - }) - .detach(); + cx.spawn(|cx| async move { install_cli(&cx).await.context("error creating CLI symlink") }) + .detach_and_log_err(cx); }); cx.add_action({ let app_state = app_state.clone(); @@ -253,6 +239,54 @@ fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) { cx.platform().quit(); } +async fn install_cli(cx: &AsyncAppContext) -> Result<()> { + let cli_path = cx.platform().path_for_auxiliary_executable("cli")?; + let link_path = Path::new("/usr/local/bin/zed"); + let bin_dir_path = link_path.parent().unwrap(); + + // Don't re-create symlink if it points to the same CLI binary. + if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) { + return Ok(()); + } + + // If the symlink is not there or is outdated, first try replacing it + // without escalating. + smol::fs::remove_file(link_path).await.log_err(); + if smol::fs::unix::symlink(&cli_path, link_path) + .await + .log_err() + .is_some() + { + return Ok(()); + } + + // The symlink could not be created, so use osascript with admin privileges + // to create it. + let status = smol::process::Command::new("osascript") + .args([ + "-e", + &dbg!(format!( + "do shell script \" \ + mkdir -p \'{}\' && \ + ln -sf \'{}\' \'{}\' \ + \" with administrator privileges", + bin_dir_path.to_string_lossy(), + cli_path.to_string_lossy(), + link_path.to_string_lossy(), + )), + ]) + .stdout(smol::process::Stdio::inherit()) + .stderr(smol::process::Stdio::inherit()) + .output() + .await? + .status; + if status.success() { + Ok(()) + } else { + Err(anyhow!("error running osascript")) + } +} + #[cfg(test)] mod tests { use super::*;