Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/commands/up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub async fn command(args: Args) -> Result<()> {
// login flow, then reload configs and continue with `up`. This
// turns the previously cryptic "no token" error path into the
// canonical first-run experience.
let came_from_unauth_prompt = configs.get_railway_auth_token().is_none();
let came_from_unauth_prompt = !configs.has_auth_credentials();
if came_from_unauth_prompt {
prompt_unauth_and_login(&args).await?;
configs = Configs::new()?;
Expand Down
40 changes: 38 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,13 @@ impl Configs {
.filter(|t| !t.is_empty()))
}

/// True when any CLI-supported credential is present. This includes
/// project tokens, so use this for auth preflight checks only; commands
/// that need user/workspace auth should call `get_railway_auth_token`.
pub fn has_auth_credentials(&self) -> bool {
Self::get_railway_token().is_some() || self.get_railway_auth_token().is_some()
}

pub fn has_oauth_token(&self) -> bool {
self.root_config.user.access_token.is_some()
}
Expand Down Expand Up @@ -736,6 +743,11 @@ mod tests {
F: FnOnce() -> R,
{
let _guard = ENV_LOCK.lock().unwrap();
let previous: Vec<(&str, Option<String>)> = vars
.iter()
.map(|(key, _)| (*key, std::env::var(key).ok()))
.collect();

// SAFETY: tests run sequentially under ENV_LOCK, so no concurrent mutation.
unsafe {
for (key, val) in vars {
Expand All @@ -747,13 +759,23 @@ mod tests {
}
let result = f();
unsafe {
for (key, _) in vars {
std::env::remove_var(key);
for (key, val) in previous {
match val {
Some(v) => std::env::set_var(key, v),
None => std::env::remove_var(key),
}
}
}
result
}

fn empty_configs() -> Configs {
Configs {
root_config_path: std::path::PathBuf::new(),
root_config: RailwayConfig::default(),
}
}

#[test]
fn env_var_project_id_only_returns_none_environment() {
let result = with_env_vars(
Expand Down Expand Up @@ -784,4 +806,18 @@ mod tests {
"unexpected error: {err}"
);
}

#[test]
fn auth_credentials_accept_project_token() {
let configs = empty_configs();
let has_credentials = with_env_vars(
&[
("RAILWAY_TOKEN", Some("project-token")),
("RAILWAY_API_TOKEN", None),
],
|| configs.has_auth_credentials(),
);

assert!(has_credentials);
}
}
Loading