use std::fmt::Display;
use crate::{
controllers::project::get_project,
errors::RailwayError,
util::prompt::{
PromptServiceInstance, fake_select, prompt_multi_options, prompt_options_skippable,
prompt_text,
},
};
use anyhow::bail;
use is_terminal::IsTerminal;
use super::{queries::project::ProjectProjectEnvironmentsEdgesNode, *};
mod changes;
mod config;
mod delete;
mod edit;
mod link;
mod list;
mod new;
#[derive(Parser)]
#[clap(
after_help = "Examples:\n\n railway environment list --json\n railway environment new staging --json\n railway environment create staging --duplicate production --json\n railway environment delete staging --yes --json\n railway environment config --environment production --json\n\nAutomation notes:\n After creating an environment, verify the linked target with `railway status --json`.\n Destructive non-interactive runs must pass the environment and --yes."
)]
pub struct Args {
pub environment: Option<String>,
#[clap(subcommand)]
command: Option<Commands>,
#[clap(long)]
pub json: bool,
}
structstruck::strike! {
#[strikethrough[derive(Parser)]]
#[allow(clippy::large_enum_variant)]
enum Commands {
Link(pub struct {
/// The environment to link to
pub environment: Option<String>,
#[clap(long)]
pub json: bool,
}),
#[clap(visible_alias = "create", visible_alias = "add")]
New(pub struct {
/// The name of the environment to create
pub name: Option<String>,
#[clap(long, short, visible_alias = "copy", visible_short_alias = 'c')]
pub duplicate: Option<String>,
#[clap(flatten)]
pub config: EnvironmentConfigOptions,
#[clap(long)]
pub json: bool,
}),
#[clap(visible_alias = "rm", visible_alias = "remove")]
Delete(pub struct {
/// Skip confirmation dialog
#[clap(short = 'y', long = "yes")]
pub bypass: bool,
pub environment: Option<String>,
#[clap(long)]
pub json: bool,
#[clap(long = "2fa-code")]
pub two_factor_code: Option<String>,
}),
#[clap(visible_alias = "update")]
Edit(pub struct {
/// The environment to edit (defaults to linked environment)
#[clap(long, short)]
pub environment: Option<String>,
#[clap(flatten)]
pub config: EnvironmentConfigOptions,
#[clap(long, short)]
pub message: Option<String>,
#[clap(long)]
pub stage: bool,
#[clap(long)]
pub json: bool,
}),
#[clap(visible_alias = "show", visible_alias = "info")]
Config(pub struct {
/// Environment to show config for (defaults to linked)
#[clap(long, short)]
pub environment: Option<String>,
#[clap(long)]
pub json: bool,
}),
#[clap(visible_alias = "ls")]
List(pub struct {
/// Show only ephemeral (PR) environments
#[clap(long, conflicts_with = "no_ephemeral")]
pub ephemeral: bool,
#[clap(long, conflicts_with = "ephemeral")]
pub no_ephemeral: bool,
#[clap(long)]
pub json: bool,
})
}
}
#[derive(Parser, Clone, Debug, Default)]
pub struct EnvironmentConfigOptions {
#[clap(long = "service-config", short = 's', number_of_values = 3, action = clap::ArgAction::Append, value_names = &["SERVICE", "PATH", "VALUE"])]
pub service_configs: Vec<String>,
#[clap(hide = true, long = "service-variable", short = 'v', number_of_values = 2, action = clap::ArgAction::Append, value_names = &["SERVICE", "KEY=VALUE"])]
pub service_variables: Vec<String>,
}
impl EnvironmentConfigOptions {
pub fn get_all_service_configs(&self) -> Vec<String> {
let mut configs = self.service_configs.clone();
for chunk in self.service_variables.chunks(2) {
if chunk.len() == 2 {
let service = &chunk[0];
let key_value = &chunk[1];
if let Some((key, value)) = key_value.split_once('=') {
configs.push(service.clone());
configs.push(format!("variables.{}.value", key));
configs.push(value.to_string());
}
}
}
configs
}
}
pub async fn command(args: Args) -> Result<()> {
match args.command {
Some(Commands::Link(link_args)) => {
link::link_environment(link_args.environment, link_args.json).await
}
Some(Commands::New(args)) => new::new_environment(args).await,
Some(Commands::Delete(args)) => delete::delete_environment(args).await,
Some(Commands::Edit(args)) => edit::edit_environment(args).await,
Some(Commands::Config(args)) => config::command(args).await,
Some(Commands::List(args)) => list::command(args).await,
None => link::link_environment(args.environment, args.json).await,
}
}
#[derive(Debug, Clone)]
pub struct Environment<'a>(&'a ProjectProjectEnvironmentsEdgesNode);
impl Display for Environment<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.name)
}
}