use anyhow::bail;
use is_terminal::IsTerminal;
use crate::{
controllers::variables::{get_all_plugin_variables, get_service_variables},
util::prompt::{prompt_select, PromptService},
};
use super::{queries::project::ProjectProject, *};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(short, long)]
service: Option<String>,
#[clap(short, long)]
environment: Option<String>,
#[clap(trailing_var_arg = true)]
args: Vec<String>,
}
enum ServiceOrPlugins {
Service(String),
Plugins(Vec<String>),
}
async fn get_service_or_plugins(
configs: &Configs,
project: &ProjectProject,
service_arg: Option<String>,
) -> Result<ServiceOrPlugins> {
let linked_project = configs.get_linked_project().await?;
let services = project.services.edges.iter().collect::<Vec<_>>();
let service = if let Some(service_arg) = service_arg {
let service_id = services
.iter()
.find(|service| service.node.name == service_arg || service.node.id == service_arg);
if let Some(service_id) = service_id {
ServiceOrPlugins::Service(service_id.node.id.to_owned())
} else {
bail!("Service not found");
}
} else if let Some(service) = linked_project.service {
ServiceOrPlugins::Service(service)
} else {
if services.is_empty() {
ServiceOrPlugins::Plugins(
project
.plugins
.edges
.iter()
.map(|plugin| plugin.node.id.to_owned())
.collect(),
)
} else if services.len() == 1 {
services
.first()
.map(|service| service.node.id.to_owned())
.map(ServiceOrPlugins::Service)
.unwrap()
} else {
if std::io::stdout().is_terminal() {
let prompt_services: Vec<_> =
services.iter().map(|s| PromptService(&s.node)).collect();
let service =
prompt_select("Select a service to pull variables from", prompt_services)?;
ServiceOrPlugins::Service(service.0.id.clone())
} else {
bail!("Multiple services found. Please specify a service to pull variables from.")
}
}
};
Ok(service)
}
pub async fn command(args: Args, _json: bool) -> Result<()> {
let configs = Configs::new()?;
let client = GQLClient::new_authorized(&configs)?;
let linked_project = configs.get_linked_project().await?;
let vars = queries::project::Variables {
id: linked_project.project.to_owned(),
};
let res = post_graphql::<queries::Project, _>(&client, configs.get_backboard(), vars).await?;
let body = res.data.context("Failed to get project (query project)")?;
let environment = args
.environment
.clone()
.unwrap_or(linked_project.environment.clone());
let environment_id = body
.project
.environments
.edges
.iter()
.find(|env| env.node.name == environment || env.node.id == environment)
.map(|env| env.node.id.to_owned())
.context("Environment not found")?;
let service = get_service_or_plugins(&configs, &body.project, args.service).await?;
let variables = match service {
ServiceOrPlugins::Service(service_id) => {
get_service_variables(
&client,
&configs,
linked_project.project.clone(),
environment_id,
service_id,
)
.await?
}
ServiceOrPlugins::Plugins(plugin_ids) => {
get_all_plugin_variables(
&client,
&configs,
linked_project.project.clone(),
environment_id,
&plugin_ids,
)
.await?
}
};
ctrlc::set_handler(move || {
})?;
let exit_status =
tokio::process::Command::new(args.args.first().context("No command provided")?)
.args(args.args[1..].iter())
.envs(variables)
.status()
.await
.context("Failed to spawn command")?;
if exit_status.success() {
println!("Looking good? Run `railway up` to deploy your changes!");
}
if let Some(code) = exit_status.code() {
std::process::exit(code);
}
Ok(())
}