Skip to content
This repository has been archived by the owner on Dec 21, 2024. It is now read-only.

Commit

Permalink
feat: add sidekick show-term command
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFlurry committed Jul 10, 2024
1 parent 5f2ee58 commit 9d5d5b2
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 200 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rivet-cli"
version = "1.3.2"
version = "1.4.0"
authors = ["Rivet Gaming, Inc. <[email protected]>"]
edition = "2018"
license = "MIT"
Expand Down
208 changes: 10 additions & 198 deletions cli/src/commands/sidekick/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use console::Term;
use global_error::prelude::*;
use serde::Serialize;
use serde_json::{json, Value};
use std::process::Command;

use crate::util::{
global_config,
Expand All @@ -20,6 +19,7 @@ pub mod get_logs_link;
pub mod get_namespace_dev_token;
pub mod get_namespace_pub_token;
pub mod get_versions_link;
pub mod show_term;
pub mod unlink;
pub mod util;
pub mod wait_for_login;
Expand All @@ -32,6 +32,11 @@ pub trait SideKickHandler: Serialize {

#[derive(Parser)]
pub enum SubCommand {
/// Run an arbritrary command in a terminal window. Primarily used for showing logs from
/// arbirtrary commands.
///
/// Prefer using the `--show-terminal` flag for Rivet-specific commands.
ShowTerm(show_term::Opts),
/// Get the link for the user to sign in
GetLink(get_link::Opts),
/// Long poll the server to check if the user has signed in
Expand Down Expand Up @@ -76,52 +81,6 @@ pub enum PreExecuteHandled {
No,
}

struct Terminal {
/// The name of the terminal emulator command
name: &'static str,
/// The flag to pass the command to the terminal emulator
prompt_str: &'static [&'static str],
}

/// Terminals that don't work (note, more work might make them work):
///
/// - guake (runs the whole window, doesn't handle closing)
/// - upterm (doesn't have an arg to pass a command it)
/// - x-terminal-emulator
/// - tilda (doesn't show automatically)
/// - terminator (issues running the command)
/// - xfce4-terminal (issues running the command)
const TERMINALS: [Terminal; 7] = [
Terminal {
name: "kitty",
prompt_str: &["-e"],
},
Terminal {
name: "konsole",
prompt_str: &["-e"],
},
Terminal {
name: "gnome-terminal",
prompt_str: &["--"],
},
Terminal {
name: "st",
prompt_str: &["-e"],
},
Terminal {
name: "tilix",
prompt_str: &["-e"],
},
Terminal {
name: "urxvt",
prompt_str: &["-e"],
},
Terminal {
name: "xterm",
prompt_str: &["-e"],
},
];

impl SubCommand {
/// These commands run before a token is required, so they don't have access
/// to ctx
Expand All @@ -132,6 +91,7 @@ impl SubCommand {
) -> GlobalResult<PreExecuteHandled> {
let mut handled = PreExecuteHandled::Yes;
let response = match self {
SubCommand::ShowTerm(opts) => serialize_output(opts.execute().await),
SubCommand::GetLink(opts) => serialize_output(opts.execute().await),
SubCommand::WaitForLogin(opts) => serialize_output(opts.execute().await),
SubCommand::CheckLoginState => serialize_output(self.validate_token(&token)),
Expand Down Expand Up @@ -183,7 +143,8 @@ impl SubCommand {
.await?;

let response = match self {
SubCommand::GetLink(_)
SubCommand::ShowTerm(_)
| SubCommand::GetLink(_)
| SubCommand::CheckLoginState
| SubCommand::WaitForLogin(_)
| SubCommand::GenerateConfig(_)
Expand Down Expand Up @@ -262,157 +223,8 @@ impl SubCommand {
// Add the binary path back as the first argument
args.insert(0, binary_path.to_str().unwrap().to_string());

#[cfg(target_os = "windows")]
Command::new("cmd.exe")
.arg("/C")
.args(args)
.spawn()
.expect("cmd.exe failed to start");

#[cfg(target_os = "macos")]
{
// This script will run from home, so we need top change to the
// project directory before running the command.
let current_dir = std::env::current_dir()?
.into_os_string()
.into_string()
.unwrap();

// Create the content for the script
let script_path = format!("{}/script.command", current_dir);
let command_to_run = format!(
"cd \"{}\" && {} && rm \"{}\"",
current_dir,
args.join(" "),
script_path
);

// Write the script content to the script file
std::fs::write(&script_path, format!("#!/bin/bash\n{}", command_to_run))?;
std::fs::set_permissions(
&script_path,
std::os::unix::fs::PermissionsExt::from_mode(0o755),
)?;

// Use `open` to run the script
std::process::Command::new("open")
.arg(&script_path)
.spawn()
.expect("Failed to open script");
}

#[cfg(target_os = "linux")]
{
// TODO(forest): For Linux, the code is trying to find an
// available terminal emulator from a predefined list and
// then run the command in it. However, the way to run a
// command in a terminal emulator can vary between different
// emulators. The -e flag used here might not work for all
// of them.
let mut command = None;

for terminal in TERMINALS {
if which::which(terminal.name).is_ok() {
command = Some(terminal);
break;
}
}

match command {
Some(terminal) => {
// See if they have bash installed. If not, fallback to sh
let shell = if which::which("bash").is_ok() {
"bash"
} else {
"sh"
};

// Insert the flag --inside-terminal right after `sidekick`
// in the args. The only args before it are the binary path
// to the binary and `sidekick` itself, so it can go at the
// 2nd index.
args.insert(2, "--inside-terminal".to_string());

// Add a "press any key to continue" message to the end of
// the arguments to be run
args.append(
vec![
"&&",
"read",
"-n",
"1",
"-s",
"-r",
"-p",
"\"Press any key to continue\"",
]
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.as_mut(),
);

args = vec![args.join(" ")];

Command::new(terminal.name)
// This is the flag to run a command in the
// terminal. Most will use -e, but some might use
// something different.
.args(terminal.prompt_str)
// We pass everything to a shell manually so that we can
// pass an entire string of the rest of the commands.
// This is more consistant across terminals on linux.
.arg(shell)
.arg("-c")
.args(&args)
.spawn()
.expect("Terminal emulator failed to start");
}
None => {
panic!("No terminal emulator found");
}
}
}
crate::util::show_term::show_term(&args).await?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use std::fs;
use std::path::Path;
use std::process::Command;

use super::TERMINALS;

#[test]
#[ignore]
/// This test makes sure that the configuration to run a command in each
/// terminal works. It shouldn't run in CI, since it would be difficult to
/// configure. It can be run locally if each terminal in the const is
/// installed.
fn test_terminals() {
for terminal in TERMINALS {
let file_name = format!("{}.txt", terminal.name);

let mut args = Vec::new();

args.push(format!("touch {}", file_name));

let output = Command::new(terminal.name)
.args(terminal.prompt_str)
.args(&args)
.output()
.expect("Failed to execute command");

assert!(output.status.success(), "Command failed: {}", terminal.name);

let file_path = Path::new(&file_name);
assert!(file_path.exists(), "File does not exist: {}", file_name);

// Clean up the file
fs::remove_file(file_path).expect("Failed to remove file");
}
}
}
30 changes: 30 additions & 0 deletions cli/src/commands/sidekick/show_term.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use clap::Parser;
use global_error::prelude::*;
use serde::Serialize;

use crate::util::{show_term, struct_fmt};

use super::SideKickHandler;

#[derive(Parser)]
pub struct Opts {
#[clap(index = 1, multiple_values = true)]
args: Vec<String>,
}

#[derive(Serialize)]
pub struct Output {
pid: u32,
}

impl SideKickHandler for Output {}

impl Opts {
pub async fn execute(&self) -> GlobalResult<Output> {
let cmd = show_term::show_term(&self.args).await?;

let output = Output { pid: cmd.id() };
struct_fmt::print_opt(None, &output)?;
Ok(output)
}
}
1 change: 1 addition & 0 deletions cli/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod cmd;
pub mod download;
pub mod global_config;
pub mod lz4;
pub mod show_term;
pub mod os;
pub mod paths;
pub mod struct_fmt;
Expand Down
Loading

0 comments on commit 9d5d5b2

Please sign in to comment.