diff --git a/crates/openshell-cli/src/auth.rs b/crates/openshell-cli/src/auth.rs index 53d35ae1..6325ebf9 100644 --- a/crates/openshell-cli/src/auth.rs +++ b/crates/openshell-cli/src/auth.rs @@ -108,23 +108,37 @@ pub async fn browser_auth_flow(gateway_endpoint: &str) -> Result { gateway_endpoint.to_string(), )); + // Allow suppressing the browser popup via environment variable (useful for + // CI, e2e tests, and headless environments). + let no_browser = std::env::var("OPENSHELL_NO_BROWSER") + .map(|v| v == "1" || v.eq_ignore_ascii_case("true")) + .unwrap_or(false); + // Prompt the user before opening the browser. eprintln!(" Confirmation code: {code}"); eprintln!(" Verify this code matches your browser before clicking Connect."); eprintln!(); - eprint!("Press Enter to open the browser for authentication..."); - std::io::stderr().flush().ok(); - let mut _input = String::new(); - std::io::stdin().read_line(&mut _input).ok(); - - if let Err(e) = open_browser(&auth_url) { - debug!(error = %e, "failed to open browser"); - eprintln!("Could not open browser automatically."); + + if no_browser { + eprintln!("Browser opening suppressed (OPENSHELL_NO_BROWSER is set)."); eprintln!("Open this URL in your browser:"); eprintln!(" {auth_url}"); eprintln!(); } else { - eprintln!("Browser opened."); + eprint!("Press Enter to open the browser for authentication..."); + std::io::stderr().flush().ok(); + let mut _input = String::new(); + std::io::stdin().read_line(&mut _input).ok(); + + if let Err(e) = open_browser(&auth_url) { + debug!(error = %e, "failed to open browser"); + eprintln!("Could not open browser automatically."); + eprintln!("Open this URL in your browser:"); + eprintln!(" {auth_url}"); + eprintln!(); + } else { + eprintln!("Browser opened."); + } } // Wait for the callback or timeout. diff --git a/e2e/rust/tests/cf_auth_smoke.rs b/e2e/rust/tests/cf_auth_smoke.rs index 671c117a..43a2f96c 100644 --- a/e2e/rust/tests/cf_auth_smoke.rs +++ b/e2e/rust/tests/cf_auth_smoke.rs @@ -21,8 +21,9 @@ async fn run_isolated(args: &[&str]) -> (String, i32) { .env("XDG_CONFIG_HOME", tmpdir.path()) .env("HOME", tmpdir.path()) .env_remove("OPENSHELL_GATEWAY") - // `gateway add` may enter the browser auth flow, which prompts on stdin. - // Use a closed stdin so auth is skipped instead of hanging the test. + // Suppress browser popup during auth flow. + .env("OPENSHELL_NO_BROWSER", "1") + // Use a closed stdin so auth prompts don't hang the test. .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()); @@ -43,8 +44,9 @@ async fn run_with_config(tmpdir: &std::path::Path, args: &[&str]) -> (String, i3 .env("XDG_CONFIG_HOME", tmpdir) .env("HOME", tmpdir) .env_remove("OPENSHELL_GATEWAY") - // `gateway add` may enter the browser auth flow, which prompts on stdin. - // Use a closed stdin so auth is skipped instead of hanging the test. + // Suppress browser popup during auth flow. + .env("OPENSHELL_NO_BROWSER", "1") + // Use a closed stdin so auth prompts don't hang the test. .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped());