Files
codex/codex-rs/config/src/fingerprint.rs
T
jif cf12c86dc5 Simplify configuration fingerprint canonicalization (#49295)
## What changed

Replace the custom recursive JSON canonicalizer with `serde_json::Value::sort_all_objects()` before hashing configuration values, preserving existing fingerprints.

## Testing

Add a regression test that checks reordered keys in nested tables and tables inside arrays produce the same existing fingerprint.

GitOrigin-RevId: 890e79865a1d690adea3b35f19fcc6c30dbd17ce
2026-09-29 16:10:27 +00:00

71 lines
2.2 KiB
Rust

use crate::ConfigLayerMetadata;
use crate::merge::is_structured_feature_path;
use serde_json::Value as JsonValue;
use sha2::Digest;
use sha2::Sha256;
use std::collections::HashMap;
use toml::Value as TomlValue;
pub(super) fn record_origins(
value: &TomlValue,
meta: &ConfigLayerMetadata,
path: &mut Vec<String>,
origins: &mut HashMap<String, ConfigLayerMetadata>,
include: &impl Fn(&[String]) -> bool,
) {
match value {
TomlValue::Table(table) => {
for (key, val) in table {
path.push(key.clone());
record_origins(val, meta, path, origins, include);
path.pop();
}
}
TomlValue::Array(items) => {
for (idx, item) in (0_i32..).zip(items.iter()) {
path.push(idx.to_string());
record_origins(item, meta, path, origins, include);
path.pop();
}
}
_ => {
if !path.is_empty() {
if !include(path) {
return;
}
if matches!(value, TomlValue::Boolean(_)) && is_structured_feature_path(path) {
if path
.last()
.is_some_and(|feature| feature == "network_proxy")
{
origins.insert(path.join("."), meta.clone());
}
path.push("enabled".to_string());
origins.insert(path.join("."), meta.clone());
path.pop();
return;
}
origins.insert(path.join("."), meta.clone());
}
}
}
}
pub fn version_for_toml(value: &TomlValue) -> String {
let mut json = serde_json::to_value(value).unwrap_or(JsonValue::Null);
json.sort_all_objects();
let serialized = serde_json::to_vec(&json).unwrap_or_default();
let mut hasher = Sha256::new();
hasher.update(serialized);
let hash = hasher.finalize();
let hex = hash
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>();
format!("sha256:{hex}")
}
#[cfg(test)]
#[path = "fingerprint_tests.rs"]
mod tests;