feat: tmux session attach mode (#901)
* feat: tmux session attach mode * feat: example config update * feat: move the comment down to be relevant * feat: fix tmux not attaching from non-tmux env when using create_and_switch_client * feat: make matching on tmux modes as described in suggestions * feat: make tmux_session_attach_mode private * feat: remove tmux mode cli option * feat: wrap default value in quotation marks for tmux session mode * feat: renames for tmux session management options * feat: try to make tmux session mode description better
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -403,6 +403,8 @@ pub struct Misc {
|
||||
|
||||
run_in_tmux: Option<bool>,
|
||||
|
||||
tmux_session_mode: Option<TmuxSessionMode>,
|
||||
|
||||
cleanup: Option<bool>,
|
||||
|
||||
notify_each_step: Option<bool>,
|
||||
@@ -419,6 +421,19 @@ pub struct Misc {
|
||||
log_filters: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
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<TmuxConfig> {
|
||||
let args = self.tmux_arguments()?;
|
||||
Ok(TmuxConfig {
|
||||
args,
|
||||
session_mode: self.tmux_session_mode(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extra Tmux arguments
|
||||
pub fn tmux_arguments(&self) -> Result<Vec<String>> {
|
||||
fn tmux_arguments(&self) -> Result<Vec<String>> {
|
||||
let args = &self
|
||||
.config_file
|
||||
.misc
|
||||
|
||||
@@ -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(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>) -> 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<String>) -> 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) => {
|
||||
|
||||
Reference in New Issue
Block a user