mirror of
https://github.com/openai/codex.git
synced 2026-05-05 22:01:37 +03:00
feat: change ConfigLayerName into a disjoint union rather than a simple enum (#8095)
This attempts to tighten up the types related to "config layers." Currently, `ConfigLayerEntry` is defined as follows:bef36f4ae7/codex-rs/core/src/config_loader/state.rs (L19-L25)but the `source` field is a bit of a lie, as: - for `ConfigLayerName::Mdm`, it is `"com.openai.codex/config_toml_base64"` - for `ConfigLayerName::SessionFlags`, it is `"--config"` - for `ConfigLayerName::User`, it is `"config.toml"` (just the file name, not the path to the `config.toml` on disk that was read) - for `ConfigLayerName::System`, it seems like it is usually `/etc/codex/managed_config.toml` in practice, though on Windows, it is `%CODEX_HOME%/managed_config.toml`:bef36f4ae7/codex-rs/core/src/config_loader/layer_io.rs (L84-L101)All that is to say, in three out of the four `ConfigLayerName`, `source` is a `PathBuf` that is not an absolute path (or even a true path). This PR tries to uplevel things by eliminating `source` from `ConfigLayerEntry` and turning `ConfigLayerName` into a disjoint union named `ConfigLayerSource` that has the appropriate metadata for each variant, favoring the use of `AbsolutePathBuf` where appropriate: ```rust pub enum ConfigLayerSource { /// Managed preferences layer delivered by MDM (macOS only). #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] Mdm { domain: String, key: String }, /// Managed config layer from a file (usually `managed_config.toml`). #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] System { file: AbsolutePathBuf }, /// Session-layer overrides supplied via `-c`/`--config`. SessionFlags, /// User config layer from a file (usually `config.toml`). #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] User { file: AbsolutePathBuf }, } ```
This commit is contained in:
@@ -11,7 +11,7 @@ use crate::path_utils;
|
||||
use codex_app_server_protocol::Config as ApiConfig;
|
||||
use codex_app_server_protocol::ConfigBatchWriteParams;
|
||||
use codex_app_server_protocol::ConfigLayerMetadata;
|
||||
use codex_app_server_protocol::ConfigLayerName;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_app_server_protocol::ConfigReadParams;
|
||||
use codex_app_server_protocol::ConfigReadResponse;
|
||||
use codex_app_server_protocol::ConfigValueWriteParams;
|
||||
@@ -499,12 +499,12 @@ fn value_at_path<'a>(root: &'a TomlValue, segments: &[String]) -> Option<&'a Tom
|
||||
Some(current)
|
||||
}
|
||||
|
||||
fn override_message(layer: &ConfigLayerName) -> String {
|
||||
fn override_message(layer: &ConfigLayerSource) -> String {
|
||||
match layer {
|
||||
ConfigLayerName::Mdm => "Overridden by managed policy (mdm)".to_string(),
|
||||
ConfigLayerName::System => "Overridden by managed config (system)".to_string(),
|
||||
ConfigLayerName::SessionFlags => "Overridden by session flags".to_string(),
|
||||
ConfigLayerName::User => "Overridden by user config".to_string(),
|
||||
ConfigLayerSource::Mdm { .. } => "Overridden by managed policy (mdm)".to_string(),
|
||||
ConfigLayerSource::System { .. } => "Overridden by managed config (system)".to_string(),
|
||||
ConfigLayerSource::SessionFlags => "Overridden by session flags".to_string(),
|
||||
ConfigLayerSource::User { .. } => "Overridden by user config".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +578,7 @@ mod tests {
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
use codex_app_server_protocol::AskForApproval;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -679,16 +680,19 @@ remote_compaction = true
|
||||
#[tokio::test]
|
||||
async fn read_includes_origins_and_layers() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
std::fs::write(tmp.path().join(CONFIG_TOML_FILE), "model = \"user\"").unwrap();
|
||||
let user_path = tmp.path().join(CONFIG_TOML_FILE);
|
||||
std::fs::write(&user_path, "model = \"user\"").unwrap();
|
||||
let user_file = AbsolutePathBuf::try_from(user_path.clone()).expect("user file");
|
||||
|
||||
let managed_path = tmp.path().join("managed_config.toml");
|
||||
std::fs::write(&managed_path, "approval_policy = \"never\"").unwrap();
|
||||
let managed_file = AbsolutePathBuf::try_from(managed_path.clone()).expect("managed file");
|
||||
|
||||
let service = ConfigService::with_overrides(
|
||||
tmp.path().to_path_buf(),
|
||||
vec![],
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(managed_path),
|
||||
managed_config_path: Some(managed_path.clone()),
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: None,
|
||||
},
|
||||
@@ -709,12 +713,20 @@ remote_compaction = true
|
||||
.get("approval_policy")
|
||||
.expect("origin")
|
||||
.name,
|
||||
ConfigLayerName::System
|
||||
ConfigLayerSource::System {
|
||||
file: managed_file.clone(),
|
||||
}
|
||||
);
|
||||
let layers = response.layers.expect("layers present");
|
||||
assert_eq!(layers.first().unwrap().name, ConfigLayerName::System);
|
||||
assert_eq!(layers.get(1).unwrap().name, ConfigLayerName::SessionFlags);
|
||||
assert_eq!(layers.last().unwrap().name, ConfigLayerName::User);
|
||||
assert_eq!(
|
||||
layers.first().unwrap().name,
|
||||
ConfigLayerSource::System { file: managed_file }
|
||||
);
|
||||
assert_eq!(layers.get(1).unwrap().name, ConfigLayerSource::SessionFlags);
|
||||
assert_eq!(
|
||||
layers.last().unwrap().name,
|
||||
ConfigLayerSource::User { file: user_file }
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -728,12 +740,13 @@ remote_compaction = true
|
||||
|
||||
let managed_path = tmp.path().join("managed_config.toml");
|
||||
std::fs::write(&managed_path, "approval_policy = \"never\"").unwrap();
|
||||
let managed_file = AbsolutePathBuf::try_from(managed_path.clone()).expect("managed file");
|
||||
|
||||
let service = ConfigService::with_overrides(
|
||||
tmp.path().to_path_buf(),
|
||||
vec![],
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(managed_path),
|
||||
managed_config_path: Some(managed_path.clone()),
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: None,
|
||||
},
|
||||
@@ -766,7 +779,7 @@ remote_compaction = true
|
||||
.get("approval_policy")
|
||||
.expect("origin")
|
||||
.name,
|
||||
ConfigLayerName::System
|
||||
ConfigLayerSource::System { file: managed_file }
|
||||
);
|
||||
assert_eq!(result.status, WriteStatus::Ok);
|
||||
assert!(result.overridden_metadata.is_none());
|
||||
@@ -775,7 +788,8 @@ remote_compaction = true
|
||||
#[tokio::test]
|
||||
async fn version_conflict_rejected() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
std::fs::write(tmp.path().join(CONFIG_TOML_FILE), "model = \"user\"").unwrap();
|
||||
let user_path = tmp.path().join(CONFIG_TOML_FILE);
|
||||
std::fs::write(&user_path, "model = \"user\"").unwrap();
|
||||
|
||||
let service = ConfigService::new(tmp.path().to_path_buf(), vec![]);
|
||||
let error = service
|
||||
@@ -832,7 +846,7 @@ remote_compaction = true
|
||||
tmp.path().to_path_buf(),
|
||||
vec![],
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(managed_path),
|
||||
managed_config_path: Some(managed_path.clone()),
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: None,
|
||||
},
|
||||
@@ -862,10 +876,13 @@ remote_compaction = true
|
||||
#[tokio::test]
|
||||
async fn read_reports_managed_overrides_user_and_session_flags() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
std::fs::write(tmp.path().join(CONFIG_TOML_FILE), "model = \"user\"").unwrap();
|
||||
let user_path = tmp.path().join(CONFIG_TOML_FILE);
|
||||
std::fs::write(&user_path, "model = \"user\"").unwrap();
|
||||
let user_file = AbsolutePathBuf::try_from(user_path.clone()).expect("user file");
|
||||
|
||||
let managed_path = tmp.path().join("managed_config.toml");
|
||||
std::fs::write(&managed_path, "model = \"system\"").unwrap();
|
||||
let managed_file = AbsolutePathBuf::try_from(managed_path.clone()).expect("managed file");
|
||||
|
||||
let cli_overrides = vec![(
|
||||
"model".to_string(),
|
||||
@@ -876,7 +893,7 @@ remote_compaction = true
|
||||
tmp.path().to_path_buf(),
|
||||
cli_overrides,
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(managed_path),
|
||||
managed_config_path: Some(managed_path.clone()),
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: None,
|
||||
},
|
||||
@@ -892,12 +909,20 @@ remote_compaction = true
|
||||
assert_eq!(response.config.model.as_deref(), Some("system"));
|
||||
assert_eq!(
|
||||
response.origins.get("model").expect("origin").name,
|
||||
ConfigLayerName::System
|
||||
ConfigLayerSource::System {
|
||||
file: managed_file.clone(),
|
||||
}
|
||||
);
|
||||
let layers = response.layers.expect("layers");
|
||||
assert_eq!(layers.first().unwrap().name, ConfigLayerName::System);
|
||||
assert_eq!(layers.get(1).unwrap().name, ConfigLayerName::SessionFlags);
|
||||
assert_eq!(layers.get(2).unwrap().name, ConfigLayerName::User);
|
||||
assert_eq!(
|
||||
layers.first().unwrap().name,
|
||||
ConfigLayerSource::System { file: managed_file }
|
||||
);
|
||||
assert_eq!(layers.get(1).unwrap().name, ConfigLayerSource::SessionFlags);
|
||||
assert_eq!(
|
||||
layers.get(2).unwrap().name,
|
||||
ConfigLayerSource::User { file: user_file }
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -907,12 +932,13 @@ remote_compaction = true
|
||||
|
||||
let managed_path = tmp.path().join("managed_config.toml");
|
||||
std::fs::write(&managed_path, "approval_policy = \"never\"").unwrap();
|
||||
let managed_file = AbsolutePathBuf::try_from(managed_path.clone()).expect("managed file");
|
||||
|
||||
let service = ConfigService::with_overrides(
|
||||
tmp.path().to_path_buf(),
|
||||
vec![],
|
||||
LoaderOverrides {
|
||||
managed_config_path: Some(managed_path),
|
||||
managed_config_path: Some(managed_path.clone()),
|
||||
#[cfg(target_os = "macos")]
|
||||
managed_preferences_base64: None,
|
||||
},
|
||||
@@ -931,7 +957,10 @@ remote_compaction = true
|
||||
|
||||
assert_eq!(result.status, WriteStatus::OkOverridden);
|
||||
let overridden = result.overridden_metadata.expect("overridden metadata");
|
||||
assert_eq!(overridden.overriding_layer.name, ConfigLayerName::System);
|
||||
assert_eq!(
|
||||
overridden.overriding_layer.name,
|
||||
ConfigLayerSource::System { file: managed_file }
|
||||
);
|
||||
assert_eq!(overridden.effective_value, serde_json::json!("never"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user