diff --git a/src/commands/add.rs b/src/commands/add.rs index 194ca99..3d3d6f5 100644 --- a/src/commands/add.rs +++ b/src/commands/add.rs @@ -320,7 +320,7 @@ async fn create_service( let vars = mutations::service_create::Variables { name: service, project_id: linked_project.project.clone(), - environment_id: linked_project.environment.clone(), + environment_id: linked_project.environment_id()?.to_string(), source: Some(source), variables, branch, diff --git a/src/commands/bucket.rs b/src/commands/bucket.rs index c4de5b2..efdd71b 100644 --- a/src/commands/bucket.rs +++ b/src/commands/bucket.rs @@ -137,10 +137,10 @@ pub async fn command(args: Args) -> Result<()> { ensure_project_and_environment_exist(&client, &configs, &linked_project).await?; let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let environment_input = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment_input = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let environment = get_matched_environment(&project, environment_input)?; let environment_config = fetch_environment_config(&client, &configs, &environment.id, false) .await? diff --git a/src/commands/connect.rs b/src/commands/connect.rs index 655017f..84031a2 100644 --- a/src/commands/connect.rs +++ b/src/commands/connect.rs @@ -38,10 +38,10 @@ pub async fn command(args: Args) -> Result<()> { let configs = Configs::new()?; let client = GQLClient::new_authorized(&configs)?; let linked_project = configs.get_linked_project().await?; - let environment = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let project = get_project(&client, &configs, linked_project.project.clone()).await?; diff --git a/src/commands/deploy.rs b/src/commands/deploy.rs index 56f6d14..4ed660e 100644 --- a/src/commands/deploy.rs +++ b/src/commands/deploy.rs @@ -172,7 +172,7 @@ pub async fn fetch_and_create( let mutation_vars = mutations::template_deploy::Variables { project_id: linked_project.project.clone(), - environment_id: linked_project.environment.clone(), + environment_id: linked_project.environment_id()?.to_string(), template_id: details.template.id.clone(), serialized_config: serde_json::to_value(&config).context("Failed to serialize config")?, }; diff --git a/src/commands/deployment.rs b/src/commands/deployment.rs index 642e535..ecd6339 100644 --- a/src/commands/deployment.rs +++ b/src/commands/deployment.rs @@ -108,7 +108,10 @@ async fn list_deployments( }; let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let environment = environment.unwrap_or(linked_project.environment.clone()); + let environment = match environment { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let environment_id = get_matched_environment(&project, environment)?.id; let service_id = if let Some(service_name_or_id) = service { diff --git a/src/commands/dev.rs b/src/commands/dev.rs index 991d25c..d8f4be1 100644 --- a/src/commands/dev.rs +++ b/src/commands/dev.rs @@ -260,7 +260,7 @@ async fn configure_command(args: ConfigureArgs) -> Result<()> { .collect(); let project_id = linked_project.project.clone(); - let environment_id = linked_project.environment.clone(); + let environment_id = linked_project.environment_id()?.to_string(); let env_response = fetch_environment_config(&client, &configs, &environment_id, false).await?; let config = env_response.config; @@ -841,10 +841,10 @@ async fn up_command(args: UpArgs) -> Result<()> { .collect(); let project_id = linked_project.project.clone(); - let environment_id = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment_id = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let env_response = fetch_environment_config(&client, &configs, &environment_id, true).await?; let env_name = env_response.name; diff --git a/src/commands/domain.rs b/src/commands/domain.rs index 5541e3f..77b8e51 100644 --- a/src/commands/domain.rs +++ b/src/commands/domain.rs @@ -61,7 +61,7 @@ async fn create_service_domain(service_name: Option, json: bool) -> Resu let vars = queries::domains::Variables { project_id: linked_project.project.clone(), - environment_id: linked_project.environment.clone(), + environment_id: linked_project.environment_id()?.to_string(), service_id: service.id.clone(), }; @@ -80,7 +80,7 @@ async fn create_service_domain(service_name: Option, json: bool) -> Resu let vars = mutations::service_domain_create::Variables { service_id: service.id.clone(), - environment_id: linked_project.environment.clone(), + environment_id: linked_project.environment_id()?.to_string(), }; let domain = post_graphql::(&client, configs.get_backboard(), vars) @@ -264,7 +264,7 @@ async fn create_custom_domain( let vars = mutations::custom_domain_create::Variables { input: mutations::custom_domain_create::CustomDomainCreateInput { domain: domain.clone(), - environment_id: linked_project.environment.clone(), + environment_id: linked_project.environment_id()?.to_string(), project_id: linked_project.project.clone(), service_id: service.id.clone(), target_port: port.map(|p| p as i64), diff --git a/src/commands/down.rs b/src/commands/down.rs index 6c63804..5465af9 100644 --- a/src/commands/down.rs +++ b/src/commands/down.rs @@ -36,10 +36,10 @@ pub async fn command(args: Args) -> Result<()> { let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let environment = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let services = project.services.edges.iter().collect::>(); @@ -72,11 +72,13 @@ pub async fn command(args: Args) -> Result<()> { let linked_project_name = linked_project .name - .expect("Linked project is missing the name"); + .as_deref() + .unwrap_or(linked_project.project.as_str()); let linked_environment_name = linked_project .environment_name - .expect("Linked environment is missing the name"); + .as_deref() + .unwrap_or(linked_project.environment.as_deref().unwrap_or("unknown")); let linked_project_environment = format!( "{} environment of project {}", diff --git a/src/commands/environment/config.rs b/src/commands/environment/config.rs index 4cdbdb5..841d194 100644 --- a/src/commands/environment/config.rs +++ b/src/commands/environment/config.rs @@ -178,7 +178,7 @@ fn resolve_environment( bail!(RailwayError::EnvironmentNotFound(env_input.clone())) } } else { - let env_id = linked_project.environment.clone(); + let env_id = linked_project.environment_id()?.to_string(); let env_name = project .environments .edges diff --git a/src/commands/environment/edit.rs b/src/commands/environment/edit.rs index 9e01a88..85262ac 100644 --- a/src/commands/environment/edit.rs +++ b/src/commands/environment/edit.rs @@ -178,7 +178,7 @@ fn resolve_environment( } } else { // Use linked environment - let env_id = linked_project.environment.clone(); + let env_id = linked_project.environment_id()?.to_string(); let env_name = project .environments .edges diff --git a/src/commands/functions/mod.rs b/src/commands/functions/mod.rs index de43413..a360862 100644 --- a/src/commands/functions/mod.rs +++ b/src/commands/functions/mod.rs @@ -111,10 +111,10 @@ pub async fn command(args: Args) -> Result<()> { let client = GQLClient::new_authorized(&configs)?; let linked_project = configs.get_linked_project().await?; let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let environment_input = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment_input = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let environment = project .environments .edges diff --git a/src/commands/logs.rs b/src/commands/logs.rs index 2eec41d..e7507db 100644 --- a/src/commands/logs.rs +++ b/src/commands/logs.rs @@ -294,10 +294,10 @@ pub async fn command(args: Args) -> Result<()> { let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let environment = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let services = project.services.edges.iter().collect::>(); diff --git a/src/commands/mcp/handler.rs b/src/commands/mcp/handler.rs index 92d1526..1bafad6 100644 --- a/src/commands/mcp/handler.rs +++ b/src/commands/mcp/handler.rs @@ -81,7 +81,7 @@ impl RailwayMcp { .map_err(|e| McpError::internal_error(format!("Failed to get project: {e}"), None))?; let env_id_or_name = environment_id - .or_else(|| linked.as_ref().map(|l| l.environment.clone())) + .or_else(|| linked.as_ref().and_then(|l| l.environment.clone())) .ok_or_else(|| { let available = format_environments(&project); McpError::invalid_params( @@ -689,7 +689,7 @@ impl RailwayMcp { .map_err(|e| McpError::internal_error(format!("Failed to get project: {e}"), None))?; // Filter to services present in the linked environment (matches CLI behavior) - let environment_id = linked.as_ref().map(|l| l.environment.as_str()); + let environment_id = linked.as_ref().and_then(|l| l.environment.as_deref()); let env_service_ids = environment_id .and_then(|eid| project.environments.edges.iter().find(|e| e.node.id == eid)) .map(|e| { diff --git a/src/commands/mcp/tools/storage.rs b/src/commands/mcp/tools/storage.rs index b097e9c..b4cc509 100644 --- a/src/commands/mcp/tools/storage.rs +++ b/src/commands/mcp/tools/storage.rs @@ -264,7 +264,7 @@ impl RailwayMcp { let linked = self.configs.get_linked_project().await.ok(); let environment_id = params .environment_id - .or_else(|| linked.as_ref().map(|l| l.environment.clone())) + .or_else(|| linked.as_ref().and_then(|l| l.environment.clone())) .ok_or_else(|| { McpError::invalid_params( "environment_id is required when updating mount_path.", diff --git a/src/commands/open.rs b/src/commands/open.rs index 1cfe035..1b83ece 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -21,7 +21,8 @@ pub async fn command(args: Args) -> Result<()> { let url = format!( "https://{hostname}/project/{}?environmentId={}", - linked_project.project, linked_project.environment + linked_project.project, + linked_project.environment_id()? ); if args.print || !std::io::stdout().is_terminal() { diff --git a/src/commands/redeploy.rs b/src/commands/redeploy.rs index 79298f3..383e827 100644 --- a/src/commands/redeploy.rs +++ b/src/commands/redeploy.rs @@ -49,9 +49,10 @@ pub async fn command(args: Args) -> Result<()> { .ok_or_else(|| anyhow!(RailwayError::ServiceNotFound(service_id)))?; let service_in_env = - find_service_instance(&project, &linked_project.environment, &service.node.id).ok_or_else( - || anyhow!("The service specified doesn't exist in the current environment"), - )?; + find_service_instance(&project, linked_project.environment_id()?, &service.node.id) + .ok_or_else(|| { + anyhow!("The service specified doesn't exist in the current environment") + })?; let Some(ref latest) = service_in_env.latest_deployment else { bail!("No deployment found for service") diff --git a/src/commands/restart.rs b/src/commands/restart.rs index bff14cf..a370320 100644 --- a/src/commands/restart.rs +++ b/src/commands/restart.rs @@ -59,9 +59,10 @@ pub async fn command(args: Args) -> Result<()> { .ok_or_else(|| anyhow!(RailwayError::ServiceNotFound(service_id)))?; let service_in_env = - find_service_instance(&project, &linked_project.environment, &service.node.id).ok_or_else( - || anyhow!("The service specified doesn't exist in the current environment"), - )?; + find_service_instance(&project, linked_project.environment_id()?, &service.node.id) + .ok_or_else(|| { + anyhow!("The service specified doesn't exist in the current environment") + })?; let Some(ref latest) = service_in_env.latest_deployment else { bail!("No deployment found for service") diff --git a/src/commands/run.rs b/src/commands/run.rs index b5a4ee4..cc5d732 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -118,10 +118,14 @@ pub async fn command(args: Args) -> Result<()> { let environment = args .environment .clone() - .or_else(|| linked_project.as_ref().map(|lp| lp.environment.clone())) + .or_else(|| { + linked_project + .as_ref() + .and_then(|lp| lp.environment.clone()) + }) .ok_or_else(|| { anyhow::anyhow!( - "No environment specified. Use --environment or run `railway link` first" + "No environment specified. Set RAILWAY_ENVIRONMENT_ID, use --environment, or run `railway environment` to link one." ) })?; diff --git a/src/commands/scale.rs b/src/commands/scale.rs index eda4999..39a5c7c 100644 --- a/src/commands/scale.rs +++ b/src/commands/scale.rs @@ -53,10 +53,10 @@ pub async fn command(args: Args) -> Result<()> { ) .await? .project; - let environment = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let (existing, service_id) = get_existing_config(&args, &linked_project, &project, &environment)?; let new_config = convert_hashmap_to_map( diff --git a/src/commands/service.rs b/src/commands/service.rs index 4d8de6e..f198b85 100644 --- a/src/commands/service.rs +++ b/src/commands/service.rs @@ -114,7 +114,7 @@ async fn link_command(args: LinkArgs) -> Result<()> { ensure_project_and_environment_exist(&client, &configs, &linked_project).await?; - let service_ids_in_env = get_service_ids_in_env(&project, &linked_project.environment); + let service_ids_in_env = get_service_ids_in_env(&project, linked_project.environment_id()?); let services: Vec<_> = project .services .edges @@ -156,7 +156,7 @@ async fn status_command(args: StatusArgs) -> Result<()> { let env = get_matched_environment(&project, env_name)?; env.id } else { - linked_project.environment.clone() + linked_project.environment_id()?.to_string() }; let environment_name = project diff --git a/src/commands/shell.rs b/src/commands/shell.rs index cb66abe..413b97f 100644 --- a/src/commands/shell.rs +++ b/src/commands/shell.rs @@ -63,18 +63,18 @@ pub async fn command(args: Args) -> Result<()> { &client, &configs, linked_project.project.clone(), - linked_project.environment.clone(), + linked_project.environment_id()?.to_string(), service_id.node.id.clone(), ) .await?; all_variables.append(&mut variables); - } else if let Some(service) = linked_project.service { + } else if let Some(ref service) = linked_project.service { let mut variables = get_service_variables( &client, &configs, linked_project.project.clone(), - linked_project.environment.clone(), + linked_project.environment_id()?.to_string(), service.clone(), ) .await?; diff --git a/src/commands/ssh/common.rs b/src/commands/ssh/common.rs index 0f98259..265516e 100644 --- a/src/commands/ssh/common.rs +++ b/src/commands/ssh/common.rs @@ -119,7 +119,11 @@ pub async fn get_ssh_connect_params( let environment = if let Some(env) = args.environment { env } else { - linked_project.as_ref().unwrap().environment.clone() + linked_project + .as_ref() + .unwrap() + .environment_id()? + .to_string() }; let environment_id = get_matched_environment(&project, environment)?.id; diff --git a/src/commands/status.rs b/src/commands/status.rs index 06d38c0..59a6a83 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -20,19 +20,18 @@ pub async fn command(args: Args) -> Result<()> { if !args.json { println!("Project: {}", project.name.purple().bold()); - println!( - "Environment: {}", + if let Some(env_name) = linked_project.environment.as_deref().and_then(|eid| { project .environments .edges .iter() - .map(|env| &env.node) - .find(|env| env.id == linked_project.environment) - .context("Environment not found!")? - .name - .blue() - .bold() - ); + .find(|env| env.node.id == eid) + .map(|env| env.node.name.as_str()) + }) { + println!("Environment: {}", env_name.blue().bold()); + } else { + println!("Environment: {}", "None".red().bold()); + } if let Some(linked_service) = linked_project.service { if let Some(service) = project diff --git a/src/commands/up.rs b/src/commands/up.rs index 90d8952..46c6b1d 100644 --- a/src/commands/up.rs +++ b/src/commands/up.rs @@ -99,10 +99,14 @@ pub async fn command(args: Args) -> Result<()> { let environment = args .environment .clone() - .or_else(|| linked_project.as_ref().map(|lp| lp.environment.clone())) + .or_else(|| { + linked_project + .as_ref() + .and_then(|lp| lp.environment.clone()) + }) .ok_or_else(|| { anyhow::anyhow!( - "No environment specified. Use --environment or run `railway link` first" + "No environment specified. Set RAILWAY_ENVIRONMENT_ID, use --environment, or run `railway environment` to link one." ) })?; let environment_id = get_matched_environment(&project, environment)?.id; diff --git a/src/commands/volume.rs b/src/commands/volume.rs index 11d387f..8d7c30c 100644 --- a/src/commands/volume.rs +++ b/src/commands/volume.rs @@ -134,10 +134,10 @@ pub async fn command(args: Args) -> Result<()> { let project = get_project(&client, &configs, linked_project.project.clone()).await?; let service = args.service.or_else(|| linked_project.service.clone()); - let environment = args - .environment - .clone() - .unwrap_or(linked_project.environment.clone()); + let environment = match args.environment.clone() { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; match args.command { Commands::Add(a) => add(service, environment, a.mount_path, project, a.json).await?, diff --git a/src/config.rs b/src/config.rs index 06cca40..a1050f1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,11 +24,22 @@ pub struct LinkedProject { pub project_path: String, pub name: Option, pub project: String, - pub environment: String, + pub environment: Option, pub environment_name: Option, pub service: Option, } +impl LinkedProject { + /// Returns the environment ID, or an error if no environment is linked. + pub fn environment_id(&self) -> Result<&str> { + self.environment.as_deref().ok_or_else(|| { + anyhow!( + "No environment specified. Set RAILWAY_ENVIRONMENT_ID, use --environment, or run `railway environment` to link one." + ) + }) + } +} + #[derive(Serialize, Deserialize, Debug, Default)] #[serde_with::skip_serializing_none] #[serde(rename_all = "camelCase")] @@ -302,44 +313,47 @@ impl Configs { project_path: self.get_current_directory()?, name: Some(data.project_token.project.name), project: data.project_token.project.id, - environment: data.project_token.environment.id, + environment: Some(data.project_token.environment.id), environment_name: Some(data.project_token.environment.name), service: project.cloned().and_then(|p| p.service), }; return Ok(project); } - let has_project_id = Self::get_railway_project_id().is_some(); - let has_environment_id = Self::get_railway_environment_id().is_some(); - - if has_project_id != has_environment_id { - bail!( - "Both RAILWAY_PROJECT_ID and RAILWAY_ENVIRONMENT_ID must be set together. {} is missing.", - if has_project_id { - "RAILWAY_ENVIRONMENT_ID" - } else { - "RAILWAY_PROJECT_ID" - } - ); - } - - if let (Some(project_id), Some(environment_id)) = ( - Self::get_railway_project_id(), - Self::get_railway_environment_id(), - ) { + if let Some(resolved) = Self::resolve_env_var_project()? { if self.get_railway_auth_token().is_none() { bail!(RailwayError::Unauthorized); } - let service_id = - Self::get_railway_service_id().or_else(|| project.cloned().and_then(|p| p.service)); + // Only merge local config when it targets the same project, + // to avoid silently mixing project A's environment with project B. + // Walk ancestor directories so nested dirs still find the local link. + let local = self + .get_local_linked_project() + .ok() + .filter(|p| p.project == resolved.project_id); + let service_id = Self::get_railway_service_id() + .or_else(|| local.as_ref().and_then(|p| p.service.clone())); + + let env_from_override = resolved.environment_id.is_some(); + let environment = resolved + .environment_id + .or_else(|| local.as_ref().and_then(|p| p.environment.clone())); + // Only carry the local environment name when we fell back to the + // local environment ID. If the override supplied its own ID, the + // local name would refer to a different environment. + let environment_name = if !env_from_override && environment.is_some() { + local.as_ref().and_then(|p| p.environment_name.clone()) + } else { + None + }; return Ok(LinkedProject { project_path: self.get_current_directory()?, name: None, - project: project_id, - environment: environment_id, - environment_name: None, + project: resolved.project_id, + environment, + environment_name, service: service_id, }); } @@ -368,7 +382,7 @@ impl Configs { project_path: path.clone(), name, project: project_id, - environment: environment_id, + environment: Some(environment_id), environment_name, service: None, }; @@ -516,4 +530,93 @@ impl Configs { Ok(()) } + + /// Resolves env-var-based project targeting. Returns: + /// - `Ok(Some(...))` if RAILWAY_PROJECT_ID is set (with optional environment) + /// - `Ok(None)` if neither env var is set (fall through to local config) + /// - `Err(...)` if RAILWAY_ENVIRONMENT_ID is set without RAILWAY_PROJECT_ID + fn resolve_env_var_project() -> Result> { + let project_id = Self::get_railway_project_id(); + let environment_id = Self::get_railway_environment_id(); + + match (project_id, environment_id) { + (Some(project_id), env_id) => Ok(Some(ResolvedEnvVarProject { + project_id, + environment_id: env_id, + })), + (None, Some(_)) => { + bail!("RAILWAY_ENVIRONMENT_ID cannot be set without RAILWAY_PROJECT_ID.") + } + (None, None) => Ok(None), + } + } +} + +#[derive(Debug)] +struct ResolvedEnvVarProject { + project_id: String, + environment_id: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Mutex; + + // Env var tests must run sequentially to avoid races. + static ENV_LOCK: Mutex<()> = Mutex::new(()); + + fn with_env_vars(vars: &[(&str, Option<&str>)], f: F) -> R + where + F: FnOnce() -> R, + { + let _guard = ENV_LOCK.lock().unwrap(); + // SAFETY: tests run sequentially under ENV_LOCK, so no concurrent mutation. + unsafe { + for (key, val) in vars { + match val { + Some(v) => std::env::set_var(key, v), + None => std::env::remove_var(key), + } + } + } + let result = f(); + unsafe { + for (key, _) in vars { + std::env::remove_var(key); + } + } + result + } + + #[test] + fn env_var_project_id_only_returns_none_environment() { + let result = with_env_vars( + &[ + ("RAILWAY_PROJECT_ID", Some("proj-123")), + ("RAILWAY_ENVIRONMENT_ID", None), + ], + Configs::resolve_env_var_project, + ); + let resolved = result.unwrap().expect("should return Some"); + assert_eq!(resolved.project_id, "proj-123"); + assert!(resolved.environment_id.is_none()); + } + + #[test] + fn env_var_environment_id_without_project_id_is_rejected() { + let result = with_env_vars( + &[ + ("RAILWAY_PROJECT_ID", None), + ("RAILWAY_ENVIRONMENT_ID", Some("env-456")), + ], + Configs::resolve_env_var_project, + ); + let err = result.unwrap_err(); + assert!( + err.to_string() + .contains("RAILWAY_ENVIRONMENT_ID cannot be set without RAILWAY_PROJECT_ID"), + "unexpected error: {err}" + ); + } } diff --git a/src/controllers/project.rs b/src/controllers/project.rs index b440a7b..fec458b 100644 --- a/src/controllers/project.rs +++ b/src/controllers/project.rs @@ -72,22 +72,24 @@ pub async fn ensure_project_and_environment_exist( bail!(RailwayError::ProjectDeleted); } - let environment = get_matched_environment( - &project, - linked_project - .environment_name - .clone() - .unwrap_or_else(|| linked_project.environment.clone()), - ); + // Only validate the environment if one is linked; callers that need an + // environment (or accept --environment) resolve and validate it themselves. + if let Some(env_id_or_name) = linked_project + .environment_name + .clone() + .or_else(|| linked_project.environment.clone()) + { + let environment = get_matched_environment(&project, env_id_or_name); - match environment { - Ok(environment) => { - if environment.deleted_at.is_some() { - bail!(RailwayError::EnvironmentDeleted); + match environment { + Ok(environment) => { + if environment.deleted_at.is_some() { + bail!(RailwayError::EnvironmentDeleted); + } } - } - Err(_) => bail!(RailwayError::EnvironmentDeleted), - }; + Err(_) => bail!(RailwayError::EnvironmentDeleted), + }; + } Ok(()) } @@ -155,7 +157,10 @@ pub async fn resolve_service_context( ensure_project_and_environment_exist(&client, &configs, &linked_project).await?; let project = get_project(&client, &configs, linked_project.project.clone()).await?; - let env = environment_arg.unwrap_or(linked_project.environment.clone()); + let env = match environment_arg { + Some(env) => env, + None => linked_project.environment_id()?.to_string(), + }; let environment_id = get_matched_environment(&project, env)?.id; let services = &project.services.edges;