use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use crate::{client::GQLClient, config::Configs};
mod common;
mod keys;
mod native;
mod tel;
use common::*;
#[derive(Parser, Clone)]
pub struct Args {
#[clap(subcommand)]
subcommand: Option<Commands>,
#[clap(short, long)]
project: Option<String>,
#[clap(short, long)]
service: Option<String>,
#[clap(short, long)]
environment: Option<String>,
#[clap(short, long)]
#[arg(long = "deployment-instance", value_name = "deployment-instance-id")]
deployment_instance: Option<String>,
#[clap(long, value_name = "SESSION_NAME", default_missing_value = "railway", num_args = 0..=1)]
session: Option<String>,
#[clap(long, hide = true)]
native: bool,
#[clap(short = 'i', long = "identity-file", value_name = "PATH")]
identity_file: Option<PathBuf>,
#[clap(trailing_var_arg = true)]
command: Vec<String>,
}
#[derive(Parser, Clone)]
enum Commands {
Keys(keys::Args),
}
pub async fn command(args: Args) -> Result<()> {
if let Some(Commands::Keys(keys_args)) = args.subcommand {
return keys::command(keys_args).await;
}
if args.native {
eprintln!(
"Warning: --native flag is deprecated and has no effect; native SSH is now the default."
);
}
let configs = Configs::new()?;
let client = GQLClient::new_authorized(&configs)?;
if args.identity_file.is_none() {
tel::track("key_setup", native::ensure_ssh_key(&client, &configs).await).await?;
}
let ssh_target = if let Some(ref instance_id) = args.deployment_instance {
instance_id.clone()
} else {
let params = tel::track(
"resolve_target",
get_ssh_connect_params(args.clone(), &configs, &client).await,
)
.await?;
tel::track(
"instance_lookup",
native::get_service_instance_id(
&client,
&configs,
¶ms.environment_id,
¶ms.service_id,
)
.await,
)
.await?
};
let identity_file = args.identity_file.as_deref();
if let Some(session_name) = args.session {
tel::track(
"tmux_install",
native::ensure_tmux_installed(&ssh_target, identity_file),
)
.await?;
return tel::track(
"session_connect",
native::run_tmux_session(&ssh_target, &session_name, identity_file),
)
.await;
}
let command = if args.command.is_empty() {
None
} else {
Some(args.command.as_slice())
};
let exit_code = tel::track(
"spawn",
native::run_native_ssh(&ssh_target, command, identity_file),
)
.await?;
if exit_code != 0 {
tel::report_failure("exit_nonzero", &format!("ssh exited with code {exit_code}")).await;
std::process::exit(exit_code);
}
Ok(())
}