From 852fcbd700734a288f442f89fd93846798765f07 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 21 Mar 2019 12:00:45 -0700 Subject: [PATCH] Automatically update PATH for the user --- install/src/command.rs | 91 +++++++++++++++++++++++++++++++++++++++++- install/src/main.rs | 15 ++++++- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/install/src/command.rs b/install/src/command.rs index 02df9bd06..3d62362f3 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -275,10 +275,100 @@ pub fn init( data_dir: &str, json_rpc_url: &str, update_manifest_pubkey: &Pubkey, + no_modify_path: bool, ) -> Result<(), String> { let config = Config::new(data_dir, json_rpc_url, update_manifest_pubkey); config.save(config_file)?; update(config_file)?; + + let mut modified_rcfiles = false; + let shell_export_string = format!( + r#"export PATH="{}:$PATH""#, + config.bin_dir().to_str().unwrap() + ); + + if !no_modify_path { + // Look for sh, bash, and zsh rc files + let mut rcfiles = vec![dirs::home_dir().map(|p| p.join(".profile"))]; + if let Ok(shell) = std::env::var("SHELL") { + if shell.contains("zsh") { + let zdotdir = std::env::var("ZDOTDIR") + .ok() + .map(PathBuf::from) + .or_else(dirs::home_dir); + let zprofile = zdotdir.map(|p| p.join(".zprofile")); + rcfiles.push(zprofile); + } + } + + if let Some(bash_profile) = dirs::home_dir().map(|p| p.join(".bash_profile")) { + // Only update .bash_profile if it exists because creating .bash_profile + // will cause .profile to not be read + if bash_profile.exists() { + rcfiles.push(Some(bash_profile)); + } + } + let rcfiles = rcfiles.into_iter().filter_map(|f| f.filter(|f| f.exists())); + + // For each rc file, append a PATH entry if not already present + for rcfile in rcfiles { + if !rcfile.exists() { + continue; + } + + fn read_file(path: &Path) -> io::Result { + let mut file = fs::OpenOptions::new().read(true).open(path)?; + let mut contents = String::new(); + io::Read::read_to_string(&mut file, &mut contents)?; + Ok(contents) + } + + match read_file(&rcfile) { + Err(err) => { + println!("Unable to read {:?}: {}", rcfile, err); + } + Ok(contents) => { + if !contents.contains(&shell_export_string) { + println!( + "Adding {} to {}", + style(&shell_export_string).italic(), + style(rcfile.to_str().unwrap()).bold() + ); + + fn append_file(dest: &Path, line: &str) -> io::Result<()> { + use std::io::Write; + let mut dest_file = fs::OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(dest)?; + + writeln!(&mut dest_file, "{}", line)?; + + dest_file.sync_data()?; + + Ok(()) + } + append_file(&rcfile, &shell_export_string).unwrap_or_else(|err| { + format!("Unable to append to {:?}: {}", rcfile, err); + }); + modified_rcfiles = true; + } + } + } + } + } + + if modified_rcfiles { + println!( + "\n{}\n {}\n", + style("Close and reopen your terminal to apply the PATH changes or run the following to use it now:").bold().blue(), + shell_export_string + ); + } else { + check_env_path_for_bin_dir(&config); + } + Ok(()) } @@ -473,7 +563,6 @@ pub fn update(config_file: &str) -> Result { config.current_update_manifest = Some(update_manifest); config.save(config_file)?; - check_env_path_for_bin_dir(&config); println!(" {}{}", SPARKLE, style("Update successful").bold()); Ok(true) } diff --git a/install/src/main.rs b/install/src/main.rs index e41f46692..1c0531bcd 100644 --- a/install/src/main.rs +++ b/install/src/main.rs @@ -70,6 +70,11 @@ fn main() -> Result<(), String> { .validator(url_validator) .help("JSON RPC URL for the solana cluster"), ) + .arg( + Arg::with_name("no_modify_path") + .long("no-modify-path") + .help("Don't configure the PATH environment variable"), + ) .arg({ let arg = Arg::with_name("update_manifest_pubkey") .short("p") @@ -176,7 +181,15 @@ fn main() -> Result<(), String> { .parse::() .unwrap(); let data_dir = matches.value_of("data_dir").unwrap(); - command::init(config_file, data_dir, json_rpc_url, &update_manifest_pubkey) + let no_modify_path = matches.is_present("no_modify_path"); + + command::init( + config_file, + data_dir, + json_rpc_url, + &update_manifest_pubkey, + no_modify_path, + ) } ("info", Some(matches)) => { let local_info_only = matches.is_present("local_info_only");