feat: migrate to new constraint-based loading strategy

This commit is contained in:
Michael Bolin
2025-12-17 22:08:09 -08:00
parent 02c4fcca80
commit ebaef21d54
12 changed files with 694 additions and 370 deletions

View File

@@ -2,7 +2,6 @@ use super::CONFIG_TOML_FILE;
use super::ConfigToml;
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::config_loader::ConfigLayerEntry;
use crate::config_loader::ConfigLayerStack;
use crate::config_loader::LoaderOverrides;
use crate::config_loader::load_config_layers_state;
@@ -20,6 +19,7 @@ use codex_app_server_protocol::ConfigWriteResponse;
use codex_app_server_protocol::MergeStrategy;
use codex_app_server_protocol::OverriddenMetadata;
use codex_app_server_protocol::WriteStatus;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde_json::Value as JsonValue;
use std::path::Path;
use std::path::PathBuf;
@@ -146,7 +146,13 @@ impl ConfigService {
Ok(ConfigReadResponse {
config,
origins: layers.origins(),
layers: params.include_layers.then(|| layers.layers_high_to_low()),
layers: params.include_layers.then(|| {
layers
.layers_high_to_low()
.iter()
.map(|layer| layer.as_layer())
.collect()
}),
})
}
@@ -194,11 +200,14 @@ impl ConfigService {
expected_version: Option<String>,
edits: Vec<(String, JsonValue, MergeStrategy)>,
) -> Result<ConfigWriteResponse, ConfigServiceError> {
let allowed_path = self.codex_home.join(CONFIG_TOML_FILE);
let provided_path = file_path
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| allowed_path.clone());
let allowed_path =
AbsolutePathBuf::resolve_path_against_base(CONFIG_TOML_FILE, &self.codex_home)
.map_err(|err| ConfigServiceError::io("failed to resolve user config path", err))?;
let provided_path = match file_path {
Some(path) => AbsolutePathBuf::from_absolute_path(PathBuf::from(path))
.map_err(|err| ConfigServiceError::io("failed to resolve user config path", err))?,
None => allowed_path.clone(),
};
if !paths_match(&allowed_path, &provided_path) {
return Err(ConfigServiceError::write(
@@ -211,9 +220,16 @@ impl ConfigService {
.load_layers_state()
.await
.map_err(|err| ConfigServiceError::io("failed to load configuration", err))?;
let user_layer = layers.get_user_layer().ok_or_else(|| {
// TODO(mbolin): Support creating the user layer if it does not exist.
ConfigServiceError::write(
ConfigWriteErrorCode::UserLayerNotFound,
"user layer not found and we do not support creating it yet",
)
})?;
if let Some(expected) = expected_version.as_deref()
&& expected != layers.user.version
&& expected != user_layer.version
{
return Err(ConfigServiceError::write(
ConfigWriteErrorCode::ConfigVersionConflict,
@@ -221,7 +237,7 @@ impl ConfigService {
));
}
let mut user_config = layers.user.config.clone();
let mut user_config = user_layer.config.clone();
let mut parsed_segments = Vec::new();
let mut config_edits = Vec::new();
@@ -273,7 +289,7 @@ impl ConfigService {
)
})?;
let updated_layers = layers.with_user_config(user_config.clone());
let updated_layers = layers.with_user_config(&provided_path, user_config.clone());
let effective = updated_layers.effective_config();
validate_config(&effective).map_err(|err| {
ConfigServiceError::write(
@@ -296,16 +312,19 @@ impl ConfigService {
.map(|_| WriteStatus::OkOverridden)
.unwrap_or(WriteStatus::Ok);
let file_path = provided_path
.canonicalize()
.unwrap_or(provided_path.clone())
.display()
.to_string();
Ok(ConfigWriteResponse {
status,
version: updated_layers.user.version.clone(),
file_path,
version: updated_layers
.get_user_layer()
.ok_or_else(|| {
ConfigServiceError::write(
ConfigWriteErrorCode::UserLayerNotFound,
"user layer not found in updated layers",
)
})?
.version
.clone(),
file_path: provided_path,
overridden_metadata: overridden,
})
}
@@ -470,15 +489,15 @@ fn validate_config(value: &TomlValue) -> Result<(), toml::de::Error> {
Ok(())
}
fn paths_match(expected: &Path, provided: &Path) -> bool {
fn paths_match<P: AsRef<Path>, Q: AsRef<Path>>(expected: P, provided: Q) -> bool {
if let (Ok(expanded_expected), Ok(expanded_provided)) = (
path_utils::normalize_for_path_comparison(expected),
path_utils::normalize_for_path_comparison(provided),
path_utils::normalize_for_path_comparison(&expected),
path_utils::normalize_for_path_comparison(&provided),
) {
return expanded_expected == expanded_provided;
expanded_expected == expanded_provided
} else {
expected.as_ref() == provided.as_ref()
}
expected == provided
}
fn value_at_path<'a>(root: &'a TomlValue, segments: &[String]) -> Option<&'a TomlValue> {
@@ -501,10 +520,25 @@ fn value_at_path<'a>(root: &'a TomlValue, segments: &[String]) -> Option<&'a Tom
fn override_message(layer: &ConfigLayerSource) -> String {
match layer {
ConfigLayerSource::Mdm { .. } => "Overridden by managed policy (mdm)".to_string(),
ConfigLayerSource::System { .. } => "Overridden by managed config (system)".to_string(),
ConfigLayerSource::Mdm { domain, key: _ } => {
format!("Overridden by managed policy (MDM): {domain}")
}
ConfigLayerSource::System { file } => {
format!("Overridden by managed config (system): {}", file.display())
}
ConfigLayerSource::SessionFlags => "Overridden by session flags".to_string(),
ConfigLayerSource::User { .. } => "Overridden by user config".to_string(),
ConfigLayerSource::User { file } => {
format!("Overridden by user config: {}", file.display())
}
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file } => {
format!(
"Overridden by legacy managed_config.toml: {}",
file.display()
)
}
ConfigLayerSource::LegacyManagedConfigTomlFromMdm => {
"Overridden by legacy managed configuration from MDM".to_string()
}
}
}
@@ -513,7 +547,10 @@ fn compute_override_metadata(
effective: &TomlValue,
segments: &[String],
) -> Option<OverriddenMetadata> {
let user_value = value_at_path(&layers.user.config, segments);
let user_value = match layers.get_user_layer() {
Some(user_layer) => value_at_path(&user_layer.config, segments),
None => return None,
};
let effective_value = value_at_path(effective, segments);
if user_value.is_some() && user_value == effective_value {
@@ -524,8 +561,7 @@ fn compute_override_metadata(
return None;
}
let effective_layer = find_effective_layer(layers, segments);
let overriding_layer = effective_layer.unwrap_or_else(|| layers.user.metadata());
let overriding_layer = find_effective_layer(layers, segments)?;
let message = override_message(&overriding_layer.name);
Some(OverriddenMetadata {
@@ -554,23 +590,13 @@ fn find_effective_layer(
layers: &ConfigLayerStack,
segments: &[String],
) -> Option<ConfigLayerMetadata> {
let check =
|state: &ConfigLayerEntry| value_at_path(&state.config, segments).map(|_| state.metadata());
for layer in layers.layers_high_to_low() {
if let Some(meta) = value_at_path(&layer.config, segments).map(|_| layer.metadata()) {
return Some(meta);
}
}
if let Some(mdm) = &layers.mdm
&& let Some(meta) = check(mdm)
{
return Some(meta);
}
if let Some(system) = &layers.system
&& let Some(meta) = check(system)
{
return Some(meta);
}
if let Some(meta) = check(&layers.session_flags) {
return Some(meta);
}
check(&layers.user)
None
}
#[cfg(test)]
@@ -713,18 +739,18 @@ remote_compaction = true
.get("approval_policy")
.expect("origin")
.name,
ConfigLayerSource::System {
file: managed_file.clone(),
}
ConfigLayerSource::LegacyManagedConfigTomlFromFile {
file: managed_file.clone()
},
);
let layers = response.layers.expect("layers present");
assert_eq!(layers.len(), 2, "expected two layers");
assert_eq!(
layers.first().unwrap().name,
ConfigLayerSource::System { file: managed_file }
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file: managed_file }
);
assert_eq!(layers.get(1).unwrap().name, ConfigLayerSource::SessionFlags);
assert_eq!(
layers.last().unwrap().name,
layers.get(1).unwrap().name,
ConfigLayerSource::User { file: user_file }
);
}
@@ -779,7 +805,9 @@ remote_compaction = true
.get("approval_policy")
.expect("origin")
.name,
ConfigLayerSource::System { file: managed_file }
ConfigLayerSource::LegacyManagedConfigTomlFromFile {
file: managed_file.clone()
}
);
assert_eq!(result.status, WriteStatus::Ok);
assert!(result.overridden_metadata.is_none());
@@ -909,14 +937,14 @@ remote_compaction = true
assert_eq!(response.config.model.as_deref(), Some("system"));
assert_eq!(
response.origins.get("model").expect("origin").name,
ConfigLayerSource::System {
file: managed_file.clone(),
}
ConfigLayerSource::LegacyManagedConfigTomlFromFile {
file: managed_file.clone()
},
);
let layers = response.layers.expect("layers");
assert_eq!(
layers.first().unwrap().name,
ConfigLayerSource::System { file: managed_file }
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file: managed_file }
);
assert_eq!(layers.get(1).unwrap().name, ConfigLayerSource::SessionFlags);
assert_eq!(
@@ -959,7 +987,7 @@ remote_compaction = true
let overridden = result.overridden_metadata.expect("overridden metadata");
assert_eq!(
overridden.overriding_layer.name,
ConfigLayerSource::System { file: managed_file }
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file: managed_file }
);
assert_eq!(overridden.effective_value, serde_json::json!("never"));
}