use crate::error::{Error, ErrorKind}; use crate::executor::RunType; use crate::terminal::print_separator; use crate::utils::{which, Check, PathExt}; use directories::BaseDirs; use failure::ResultExt; use std::env; use std::io; use std::os::unix::process::CommandExt; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; pub fn run_tpm(base_dirs: &BaseDirs, run_type: RunType) -> Result<(), Error> { let tpm = base_dirs .home_dir() .join(".tmux/plugins/tpm/bin/update_plugins") .require()?; print_separator("tmux plugins"); run_type.execute(&tpm).arg("all").check_run() } struct Tmux { tmux: PathBuf, args: Option>, } impl Tmux { fn new(args: &Option) -> Self { Self { tmux: which("tmux").expect("Could not find tmux"), args: args .as_ref() .map(|args| args.split_whitespace().map(String::from).collect()), } } fn build(&self) -> Command { let mut command = Command::new(&self.tmux); if let Some(args) = self.args.as_ref() { command.args(args).env_remove("TMUX"); } command } fn has_session(&self, session_name: &str) -> Result { Ok(self .build() .args(&["has-session", "-t", session_name]) .output()? .status .success()) } fn new_session(&self, session_name: &str) -> Result { Ok(self .build() .args(&["new-session", "-d", "-s", session_name, "-n", "dummy"]) .spawn()? .wait()? .success()) } fn run_in_session(&self, command: &str) -> Result<(), Error> { self.build() .args(&["new-window", "-a", "-t", "topgrade:1", command]) .spawn() .context(ErrorKind::ProcessExecution)? .wait() .context(ErrorKind::ProcessExecution)? .check()?; Ok(()) } } pub fn run_in_tmux(args: &Option) -> ! { let command = { let mut command = vec![ String::from("env"), String::from("TOPGRADE_KEEP_END=1"), String::from("TOPGRADE_INSIDE_TMUX=1"), ]; command.extend(env::args()); command.join(" ") }; let tmux = Tmux::new(args); if !tmux.has_session("topgrade").expect("Error detecting a tmux session") { tmux.new_session("topgrade").expect("Error creating a tmux session"); } tmux.run_in_session(&command).expect("Error running topgrade in tmux"); tmux.build() .args(&["kill-window", "-t", "topgrade:dummy"]) .output() .expect("Error killing the dummy tmux window"); if env::var("TMUX").is_err() { let err = tmux.build().args(&["attach", "-t", "topgrade"]).exec(); panic!("{:?}", err); } else { println!("Topgrade launched in a new tmux session"); exit(0); } } pub fn run_remote_topgrade(hostname: &str, ssh: &Path, tmux_args: &Option) -> Result<(), Error> { let command = format!( "{ssh} -t {hostname} env TOPGRADE_PREFIX={hostname} TOPGRADE_KEEP_END=1 topgrade", ssh = ssh.display(), hostname = hostname ); Tmux::new(tmux_args) .build() .args(&["new-window", "-a", "-t", "topgrade:1", &command]) .env_remove("TMUX") .spawn() .context(ErrorKind::ProcessExecution)? .wait() .context(ErrorKind::ProcessExecution)? .check() }