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 inside tmux (default: false)
|
||||||
# run_in_tmux = true
|
# 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 temporary or old files (default: false)
|
||||||
# cleanup = true
|
# cleanup = true
|
||||||
|
|
||||||
|
|||||||
@@ -403,6 +403,8 @@ pub struct Misc {
|
|||||||
|
|
||||||
run_in_tmux: Option<bool>,
|
run_in_tmux: Option<bool>,
|
||||||
|
|
||||||
|
tmux_session_mode: Option<TmuxSessionMode>,
|
||||||
|
|
||||||
cleanup: Option<bool>,
|
cleanup: Option<bool>,
|
||||||
|
|
||||||
notify_each_step: Option<bool>,
|
notify_each_step: Option<bool>,
|
||||||
@@ -419,6 +421,19 @@ pub struct Misc {
|
|||||||
log_filters: Option<Vec<String>>,
|
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)]
|
#[derive(Deserialize, Default, Debug, Merge)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Lensfun {
|
pub struct Lensfun {
|
||||||
@@ -967,6 +982,15 @@ impl Config {
|
|||||||
.unwrap_or(false)
|
.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.
|
/// Tell whether we should perform cleanup steps.
|
||||||
pub fn cleanup(&self) -> bool {
|
pub fn cleanup(&self) -> bool {
|
||||||
self.opt.cleanup
|
self.opt.cleanup
|
||||||
@@ -1024,8 +1048,16 @@ impl Config {
|
|||||||
self.config_file.git.as_ref().and_then(|git| git.arguments.as_ref())
|
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
|
/// Extra Tmux arguments
|
||||||
pub fn tmux_arguments(&self) -> Result<Vec<String>> {
|
fn tmux_arguments(&self) -> Result<Vec<String>> {
|
||||||
let args = &self
|
let args = &self
|
||||||
.config_file
|
.config_file
|
||||||
.misc
|
.misc
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ fn run() -> Result<()> {
|
|||||||
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
|
if config.run_in_tmux() && env::var("TOPGRADE_INSIDE_TMUX").is_err() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
tmux::run_in_tmux(config.tmux_arguments()?)?;
|
tmux::run_in_tmux(config.tmux_config()?)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use color_eyre::eyre::Context;
|
|||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
use crate::command::CommandExt;
|
use crate::command::CommandExt;
|
||||||
|
use crate::config::TmuxConfig;
|
||||||
|
use crate::config::TmuxSessionMode;
|
||||||
use crate::terminal::print_separator;
|
use crate::terminal::print_separator;
|
||||||
use crate::HOME_DIR;
|
use crate::HOME_DIR;
|
||||||
use crate::{
|
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 command = {
|
||||||
let mut command = vec![
|
let mut command = vec![
|
||||||
String::from("env"),
|
String::from("env"),
|
||||||
@@ -144,25 +146,39 @@ pub fn run_in_tmux(args: Vec<String>) -> Result<()> {
|
|||||||
shell_words::join(command)
|
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.
|
// Find an unused session and run `topgrade` in it with the current command's arguments.
|
||||||
let session_name = "topgrade";
|
let session_name = "topgrade";
|
||||||
let window_name = "topgrade";
|
let window_name = "topgrade";
|
||||||
let session = tmux.new_unique_session(session_name, window_name, &command)?;
|
let session = tmux.new_unique_session(session_name, window_name, &command)?;
|
||||||
|
|
||||||
|
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.
|
// 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");
|
println!("Topgrade launched in a new tmux session");
|
||||||
Ok(())
|
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<()> {
|
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() {
|
match ctx.get_tmux_session() {
|
||||||
Some(session_name) => {
|
Some(session_name) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user