1use std::path::Path;
2
3use anyhow::{Result, anyhow};
4use gpui::{AsyncAppContext, actions};
5use util::ResultExt;
6
7actions!(cli, [ Install ]);
8
9
10pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
11 let cli_path = cx.platform().path_for_auxiliary_executable("cli")?;
12 let link_path = Path::new("/usr/local/bin/zed");
13 let bin_dir_path = link_path.parent().unwrap();
14
15 // Don't re-create symlink if it points to the same CLI binary.
16 if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) {
17 return Ok(());
18 }
19
20 // If the symlink is not there or is outdated, first try replacing it
21 // without escalating.
22 smol::fs::remove_file(link_path).await.log_err();
23 if smol::fs::unix::symlink(&cli_path, link_path)
24 .await
25 .log_err()
26 .is_some()
27 {
28 return Ok(());
29 }
30
31 // The symlink could not be created, so use osascript with admin privileges
32 // to create it.
33 let status = smol::process::Command::new("osascript")
34 .args([
35 "-e",
36 &format!(
37 "do shell script \" \
38 mkdir -p \'{}\' && \
39 ln -sf \'{}\' \'{}\' \
40 \" with administrator privileges",
41 bin_dir_path.to_string_lossy(),
42 cli_path.to_string_lossy(),
43 link_path.to_string_lossy(),
44 ),
45 ])
46 .stdout(smol::process::Stdio::inherit())
47 .stderr(smol::process::Stdio::inherit())
48 .output()
49 .await?
50 .status;
51 if status.success() {
52 Ok(())
53 } else {
54 Err(anyhow!("error running osascript"))
55 }
56}