mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:23:33 +00:00
@@ -38,6 +38,7 @@ use codex_protocol::mcp_protocol::AuthMode;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use dirs::home_dir;
|
||||
use serde::Deserialize;
|
||||
use serde::de::{self, Unexpected};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
@@ -1345,6 +1346,7 @@ pub struct ConfigToml {
|
||||
pub disable_response_storage: Option<bool>,
|
||||
|
||||
/// Enable silent upgrades during startup when a newer release is available.
|
||||
#[serde(default, deserialize_with = "deserialize_option_bool_from_maybe_string")]
|
||||
pub auto_upgrade_enabled: Option<bool>,
|
||||
|
||||
/// Optional external command to spawn for end-user notifications.
|
||||
@@ -1449,6 +1451,37 @@ pub struct ConfigToml {
|
||||
pub experimental_resume: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn deserialize_option_bool_from_maybe_string<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<bool>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum BoolOrString {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
let value = Option::<BoolOrString>::deserialize(deserializer)?;
|
||||
match value {
|
||||
Some(BoolOrString::Bool(b)) => Ok(Some(b)),
|
||||
Some(BoolOrString::String(s)) => {
|
||||
let normalized = s.trim().to_ascii_lowercase();
|
||||
match normalized.as_str() {
|
||||
"true" => Ok(Some(true)),
|
||||
"false" => Ok(Some(false)),
|
||||
_ => Err(de::Error::invalid_value(
|
||||
Unexpected::Str(&s),
|
||||
&"a boolean or string 'true'/'false'",
|
||||
)),
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct ProjectConfig {
|
||||
pub trust_level: Option<String>,
|
||||
@@ -2158,6 +2191,24 @@ persistence = "none"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_upgrade_enabled_accepts_string_boolean() {
|
||||
let cfg_true = r#"auto_upgrade_enabled = "true""#;
|
||||
let parsed_true = toml::from_str::<ConfigToml>(cfg_true)
|
||||
.expect("string boolean should deserialize");
|
||||
assert_eq!(parsed_true.auto_upgrade_enabled, Some(true));
|
||||
|
||||
let cfg_false = r#"auto_upgrade_enabled = "false""#;
|
||||
let parsed_false = toml::from_str::<ConfigToml>(cfg_false)
|
||||
.expect("string boolean should deserialize");
|
||||
assert_eq!(parsed_false.auto_upgrade_enabled, Some(false));
|
||||
|
||||
let cfg_bool = r#"auto_upgrade_enabled = true"#;
|
||||
let parsed_bool = toml::from_str::<ConfigToml>(cfg_bool)
|
||||
.expect("boolean should deserialize");
|
||||
assert_eq!(parsed_bool.auto_upgrade_enabled, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tui_config_missing_notifications_field_defaults_to_disabled() {
|
||||
let cfg = r#"
|
||||
|
||||
@@ -367,7 +367,11 @@ async fn persist_overrides_with_behavior(
|
||||
}
|
||||
match value {
|
||||
Some(v) => {
|
||||
let item_value = toml_edit::value(v);
|
||||
let trimmed = v.trim();
|
||||
let item_value = match trimmed.parse::<bool>() {
|
||||
Ok(parsed_bool) => toml_edit::value(parsed_bool),
|
||||
Err(_) => toml_edit::value(v),
|
||||
};
|
||||
apply_toml_edit_override_segments(&mut doc, segments_to_apply, item_value);
|
||||
mutated = true;
|
||||
}
|
||||
@@ -550,6 +554,26 @@ model = "gpt-5"
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn persist_overrides_writes_boolean_literals() {
|
||||
let tmpdir = tempdir().expect("tmp");
|
||||
let codex_home = tmpdir.path();
|
||||
|
||||
persist_overrides(codex_home, None, &[(&["auto_upgrade_enabled"], "true")])
|
||||
.await
|
||||
.expect("persist");
|
||||
|
||||
let contents = read_config(codex_home).await;
|
||||
assert!(contents.contains("auto_upgrade_enabled = true"));
|
||||
|
||||
persist_overrides(codex_home, None, &[(&["auto_upgrade_enabled"], "false")])
|
||||
.await
|
||||
.expect("persist");
|
||||
|
||||
let contents = read_config(codex_home).await;
|
||||
assert!(contents.contains("auto_upgrade_enabled = false"));
|
||||
}
|
||||
|
||||
/// Verifies a scalar key becomes a table when nested keys are written.
|
||||
#[tokio::test]
|
||||
async fn persist_overrides_replaces_scalar_with_table() {
|
||||
|
||||
@@ -7364,11 +7364,10 @@ fn update_rate_limit_resets(
|
||||
let codex_home = self.config.codex_home.clone();
|
||||
let profile = self.config.active_profile.clone();
|
||||
tokio::spawn(async move {
|
||||
let value = if enabled { "true".to_string() } else { "false".to_string() };
|
||||
if let Err(err) = codex_core::config_edit::persist_overrides(
|
||||
&codex_home,
|
||||
profile.as_deref(),
|
||||
&[(&["auto_upgrade_enabled"], value.as_str())],
|
||||
&[(&["auto_upgrade_enabled"], if enabled { "true" } else { "false" })],
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user