diff --git a/Cargo.lock b/Cargo.lock index 7213f9a48a..cbbcbc7914 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3929,6 +3929,17 @@ dependencies = [ "util", ] +[[package]] +name = "install_cli2" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui2", + "log", + "smol", + "util", +] + [[package]] name = "instant" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index 6bab473f2c..f8ce95ea6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "crates/gpui2", "crates/gpui2_macros", "crates/install_cli", + "crates/install_cli2", "crates/journal", "crates/language", "crates/language2", diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 65b021413a..75f202e710 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -30,6 +30,7 @@ use std::{ marker::PhantomData, mem, ops::{Deref, DerefMut}, + path::PathBuf, sync::{atomic::Ordering::SeqCst, Arc, Weak}, time::Duration, }; @@ -722,6 +723,10 @@ where pub fn open_url(&self, url: &str) { self.platform().open_url(url); } + + pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { + self.platform().path_for_auxiliary_executable(name) + } } impl MainThread { diff --git a/crates/install_cli2/Cargo.toml b/crates/install_cli2/Cargo.toml new file mode 100644 index 0000000000..0dd1b907fd --- /dev/null +++ b/crates/install_cli2/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "install_cli2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/install_cli2.rs" + +[features] +test-support = [] + +[dependencies] +smol.workspace = true +anyhow.workspace = true +log.workspace = true +gpui2 = { path = "../gpui2" } +util = { path = "../util" } diff --git a/crates/install_cli2/src/install_cli2.rs b/crates/install_cli2/src/install_cli2.rs new file mode 100644 index 0000000000..ecdf2a0f2a --- /dev/null +++ b/crates/install_cli2/src/install_cli2.rs @@ -0,0 +1,57 @@ +use anyhow::{anyhow, Result}; +use gpui2::AsyncAppContext; +use std::path::Path; +use util::ResultExt; + +// todo!() +// actions!(cli, [Install]); + +pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { + let cli_path = cx + .run_on_main(|cx| cx.path_for_auxiliary_executable("cli"))? + .await?; + 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("/usr/bin/osascript") + .args([ + "-e", + &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")) + } +}