diff --git a/config.example.toml b/config.example.toml index c86fc99d..b5afade3 100644 --- a/config.example.toml +++ b/config.example.toml @@ -47,6 +47,12 @@ # Run inside tmux (default: false) # run_in_tmux = true +# Changes the way topgrade interacts with +# the tmux session, creating the session +# and only attaching to it if not inside tmux +# (default: "attach_if_not_in_session", allowed values: "attach_if_not_in_session", "attach_always") +# tmux_session_mode = "attach_if_not_in_session" + # Cleanup temporary or old files (default: false) # cleanup = true diff --git a/src/config.rs b/src/config.rs index c529013a..a6b821bb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -403,6 +403,8 @@ pub struct Misc { run_in_tmux: Option, + tmux_session_mode: Option, + cleanup: Option, notify_each_step: Option, @@ -419,6 +421,19 @@ pub struct Misc { log_filters: Option>, } +#[derive(Clone, Copy, Debug, Deserialize, ValueEnum)] +#[clap(rename_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum TmuxSessionMode { + AttachIfNotInSession, + AttachAlways, +} + +pub struct TmuxConfig { + pub args: Vec, + pub session_mode: TmuxSessionMode, +} + #[derive(Deserialize, Default, Debug, Merge)] #[serde(deny_unknown_fields)] pub struct Lensfun { @@ -967,6 +982,15 @@ impl Config { .unwrap_or(false) } + /// The preferred way to run the new tmux session. + fn tmux_session_mode(&self) -> TmuxSessionMode { + self.config_file + .misc + .as_ref() + .and_then(|misc| misc.tmux_session_mode) + .unwrap_or(TmuxSessionMode::AttachIfNotInSession) + } + /// Tell whether we should perform cleanup steps. pub fn cleanup(&self) -> bool { self.opt.cleanup @@ -1024,8 +1048,16 @@ impl Config { self.config_file.git.as_ref().and_then(|git| git.arguments.as_ref()) } + pub fn tmux_config(&self) -> Result { + let args = self.tmux_arguments()?; + Ok(TmuxConfig { + args, + session_mode: self.tmux_session_mode(), + }) + } + /// Extra Tmux arguments - pub fn tmux_arguments(&self) -> Result> { + fn tmux_arguments(&self) -> Result> { let args = &self .config_file .misc diff --git a/src/main.rs b/src/main.rs index 6217bde0..750020be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -118,7 +118,7 @@ fn run() -> Result<()> { if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() { #[cfg(unix)] { - tmux::run_in_tmux(config.tmux_arguments()?)?; + tmux::run_in_tmux(config.tmux_config()?)?; return Ok(()); } } diff --git a/src/steps/tmux.rs b/src/steps/tmux.rs index f8286fd4..ad021eec 100644 --- a/src/steps/tmux.rs +++ b/src/steps/tmux.rs @@ -7,6 +7,8 @@ use color_eyre::eyre::Context; use color_eyre::eyre::Result; use crate::command::CommandExt; +use crate::config::TmuxConfig; +use crate::config::TmuxSessionMode; use crate::terminal::print_separator; use crate::HOME_DIR; use crate::{ @@ -131,7 +133,7 @@ impl Tmux { } } -pub fn run_in_tmux(args: Vec) -> Result<()> { +pub fn run_in_tmux(config: TmuxConfig) -> Result<()> { let command = { let mut command = vec![ String::from("env"), @@ -144,25 +146,39 @@ pub fn run_in_tmux(args: Vec) -> Result<()> { shell_words::join(command) }; - let tmux = Tmux::new(args); + let tmux = Tmux::new(config.args); // Find an unused session and run `topgrade` in it with the current command's arguments. let session_name = "topgrade"; let window_name = "topgrade"; let session = tmux.new_unique_session(session_name, window_name, &command)?; - // Only attach to the newly-created session if we're not currently in a tmux session. - if env::var("TMUX").is_err() { - let err = tmux.build().args(["attach-session", "-t", &session]).exec(); - Err(eyre!("{err}")).context("Failed to `execvp(3)` tmux") - } else { - println!("Topgrade launched in a new tmux session"); - Ok(()) - } + let is_inside_tmux = env::var("TMUX").is_ok(); + let err = match config.session_mode { + TmuxSessionMode::AttachIfNotInSession => { + if is_inside_tmux { + // Only attach to the newly-created session if we're not currently in a tmux session. + println!("Topgrade launched in a new tmux session"); + return Ok(()); + } else { + tmux.build().args(["attach-client", "-t", &session]).exec() + } + } + + TmuxSessionMode::AttachAlways => { + if is_inside_tmux { + tmux.build().args(["switch-client", "-t", &session]).exec() + } else { + tmux.build().args(["attach-client", "-t", &session]).exec() + } + } + }; + + Err(eyre!("{err}")).context("Failed to `execvp(3)` tmux") } pub fn run_command(ctx: &ExecutionContext, window_name: &str, command: &str) -> Result<()> { - let tmux = Tmux::new(ctx.config().tmux_arguments()?); + let tmux = Tmux::new(ctx.config().tmux_config()?.args); match ctx.get_tmux_session() { Some(session_name) => {