fix(cli): avoid default env overrides for profiles (#2119)
* fix(cli): avoid default env overrides for profiles * test(cli): lock auth mode profile handoff behavior * test(cli): restore prompt flag coverage --------- Co-authored-by: liushiao <liushiao@bytedance.com>
This commit is contained in:
+134
-40
@@ -1464,33 +1464,28 @@ fn build_tui_command(
|
||||
);
|
||||
}
|
||||
|
||||
cmd.env("DEEPSEEK_MODEL", &resolved_runtime.model);
|
||||
cmd.env("DEEPSEEK_BASE_URL", &resolved_runtime.base_url);
|
||||
cmd.env("DEEPSEEK_PROVIDER", resolved_runtime.provider.as_str());
|
||||
if let Some(auth_mode) = resolved_runtime.auth_mode.as_ref() {
|
||||
cmd.env("DEEPSEEK_AUTH_MODE", auth_mode);
|
||||
if let Some(provider) = cli.provider {
|
||||
let provider: ProviderKind = provider.into();
|
||||
cmd.env("DEEPSEEK_PROVIDER", provider.as_str());
|
||||
}
|
||||
if !resolved_runtime.http_headers.is_empty() {
|
||||
let encoded = resolved_runtime
|
||||
.http_headers
|
||||
.iter()
|
||||
.map(|(name, value)| format!("{}={}", name.trim(), value.trim()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
cmd.env("DEEPSEEK_HTTP_HEADERS", encoded);
|
||||
}
|
||||
if let Some(api_key) = resolved_runtime.api_key.as_ref() {
|
||||
if matches!(
|
||||
resolved_runtime.api_key_source,
|
||||
Some(RuntimeApiKeySource::Keyring)
|
||||
) && let Some(api_key) = resolved_runtime.api_key.as_ref()
|
||||
{
|
||||
// TUI reloads auth_mode from config/profile, but it does not re-query the
|
||||
// platform keyring on normal startup. Bridge only the recovered secret;
|
||||
// replaying auth_mode here would turn it back into a profile override.
|
||||
cmd.env("DEEPSEEK_API_KEY", api_key);
|
||||
for var in provider_env_vars(resolved_runtime.provider) {
|
||||
if *var != "DEEPSEEK_API_KEY" {
|
||||
cmd.env(var, api_key);
|
||||
}
|
||||
}
|
||||
let source = resolved_runtime
|
||||
.api_key_source
|
||||
.unwrap_or(RuntimeApiKeySource::Env)
|
||||
.as_env_value();
|
||||
cmd.env("DEEPSEEK_API_KEY_SOURCE", source);
|
||||
cmd.env(
|
||||
"DEEPSEEK_API_KEY_SOURCE",
|
||||
RuntimeApiKeySource::Keyring.as_env_value(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(model) = cli.model.as_ref() {
|
||||
@@ -2630,14 +2625,6 @@ mod tests {
|
||||
command_env(&cmd, "DEEPSEEK_PROVIDER").as_deref(),
|
||||
Some("openai")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_MODEL").as_deref(),
|
||||
Some("glm-5")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_BASE_URL").as_deref(),
|
||||
Some("https://openai-compatible.example/v4")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_API_KEY").as_deref(),
|
||||
Some("resolved-openai-key")
|
||||
@@ -2650,10 +2637,7 @@ mod tests {
|
||||
command_env(&cmd, "DEEPSEEK_API_KEY_SOURCE").as_deref(),
|
||||
Some("keyring")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_AUTH_MODE").as_deref(),
|
||||
Some("api_key")
|
||||
);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_AUTH_MODE"), None);
|
||||
let args: Vec<String> = cmd
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
@@ -2665,6 +2649,55 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_tui_command_does_not_export_default_runtime_overrides_for_profiles() {
|
||||
let _lock = env_lock();
|
||||
let dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let custom = dir
|
||||
.path()
|
||||
.join(format!("custom-tui{}", std::env::consts::EXE_SUFFIX));
|
||||
std::fs::write(&custom, b"").unwrap();
|
||||
let custom_str = custom.to_string_lossy().into_owned();
|
||||
let _bin = ScopedEnvVar::set("DEEPSEEK_TUI_BIN", &custom_str);
|
||||
|
||||
let cli = parse_ok(&["deepseek", "--profile", "google"]);
|
||||
let mut resolved_headers = std::collections::BTreeMap::new();
|
||||
resolved_headers.insert("X-From-Base".to_string(), "base".to_string());
|
||||
let resolved = ResolvedRuntimeOptions {
|
||||
provider: ProviderKind::Deepseek,
|
||||
model: "deepseek-v4-pro".to_string(),
|
||||
api_key: Some("config-file-key".to_string()),
|
||||
api_key_source: Some(RuntimeApiKeySource::ConfigFile),
|
||||
base_url: "https://api.deepseek.com/beta".to_string(),
|
||||
auth_mode: Some("api_key".to_string()),
|
||||
output_mode: None,
|
||||
log_level: None,
|
||||
telemetry: false,
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
http_headers: resolved_headers,
|
||||
};
|
||||
|
||||
let cmd = build_tui_command(&cli, &resolved, Vec::new()).expect("command");
|
||||
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_PROVIDER"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_MODEL"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_BASE_URL"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_API_KEY"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_API_KEY_SOURCE"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_AUTH_MODE"), None);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_HTTP_HEADERS"), None);
|
||||
let args: Vec<String> = cmd
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect();
|
||||
assert!(
|
||||
args.windows(2).any(|pair| pair == ["--profile", "google"]),
|
||||
"expected profile forwarding in args: {args:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_tui_command_allows_moonshot_and_forwards_kimi_key() {
|
||||
let _lock = env_lock();
|
||||
@@ -2689,7 +2722,7 @@ mod tests {
|
||||
provider: ProviderKind::Moonshot,
|
||||
model: "kimi-k2.6".to_string(),
|
||||
api_key: Some("resolved-kimi-key".to_string()),
|
||||
api_key_source: Some(RuntimeApiKeySource::Env),
|
||||
api_key_source: Some(RuntimeApiKeySource::Keyring),
|
||||
base_url: "https://api.moonshot.ai/v1".to_string(),
|
||||
auth_mode: Some("api_key".to_string()),
|
||||
output_mode: None,
|
||||
@@ -2710,10 +2743,6 @@ mod tests {
|
||||
command_env(&cmd, "DEEPSEEK_MODEL").as_deref(),
|
||||
Some("kimi-k2.6")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_BASE_URL").as_deref(),
|
||||
Some("https://api.moonshot.ai/v1")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_API_KEY").as_deref(),
|
||||
Some("resolved-kimi-key")
|
||||
@@ -2728,12 +2757,67 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_API_KEY_SOURCE").as_deref(),
|
||||
Some("env")
|
||||
Some("keyring")
|
||||
);
|
||||
assert_eq!(command_env(&cmd, "DEEPSEEK_AUTH_MODE"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_tui_command_exports_explicit_provider_model_and_base_url() {
|
||||
let _lock = env_lock();
|
||||
let dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let custom = dir
|
||||
.path()
|
||||
.join(format!("custom-tui{}", std::env::consts::EXE_SUFFIX));
|
||||
std::fs::write(&custom, b"").unwrap();
|
||||
let custom_str = custom.to_string_lossy().into_owned();
|
||||
let _bin = ScopedEnvVar::set("DEEPSEEK_TUI_BIN", &custom_str);
|
||||
|
||||
let cli = parse_ok(&[
|
||||
"deepseek",
|
||||
"--profile",
|
||||
"google",
|
||||
"--provider",
|
||||
"openai",
|
||||
"--model",
|
||||
"glm-5",
|
||||
"--base-url",
|
||||
"https://openai-compatible.example/v4",
|
||||
]);
|
||||
let resolved = ResolvedRuntimeOptions {
|
||||
provider: ProviderKind::Openai,
|
||||
model: "glm-5".to_string(),
|
||||
api_key: None,
|
||||
api_key_source: None,
|
||||
base_url: "https://openai-compatible.example/v4".to_string(),
|
||||
auth_mode: None,
|
||||
output_mode: None,
|
||||
log_level: None,
|
||||
telemetry: false,
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let cmd = build_tui_command(&cli, &resolved, Vec::new()).expect("command");
|
||||
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_PROVIDER").as_deref(),
|
||||
Some("openai")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_MODEL").as_deref(),
|
||||
Some("glm-5")
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_BASE_URL").as_deref(),
|
||||
Some("https://openai-compatible.example/v4")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_tui_command_forwards_provider_env_vars_for_all_providers() {
|
||||
fn build_tui_command_forwards_provider_keyring_env_vars_for_all_providers() {
|
||||
let _lock = env_lock();
|
||||
let dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let custom = dir
|
||||
@@ -2788,7 +2872,7 @@ mod tests {
|
||||
provider,
|
||||
model: "test-model".to_string(),
|
||||
api_key: Some("test-key".to_string()),
|
||||
api_key_source: Some(RuntimeApiKeySource::Env),
|
||||
api_key_source: Some(RuntimeApiKeySource::Keyring),
|
||||
base_url: "http://localhost:8000/v1".to_string(),
|
||||
auth_mode: Some("api_key".to_string()),
|
||||
output_mode: None,
|
||||
@@ -2815,6 +2899,16 @@ mod tests {
|
||||
"{flag}: {var} not forwarded"
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_API_KEY_SOURCE").as_deref(),
|
||||
Some("keyring"),
|
||||
"{flag}: expected keyring source bridge"
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&cmd, "DEEPSEEK_AUTH_MODE"),
|
||||
None,
|
||||
"{flag}: auth mode should come from config/profile, not env handoff"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user