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