mirror of
https://github.com/openai/codex.git
synced 2026-04-28 08:34:54 +00:00
253 lines
9.6 KiB
Markdown
253 lines
9.6 KiB
Markdown
# PR #2523: core: write explicit [projects] tables for trusted projects
|
|
|
|
- URL: https://github.com/openai/codex/pull/2523
|
|
- Author: nornagon-openai
|
|
- Created: 2025-08-20 18:53:28 UTC
|
|
- Updated: 2025-08-21 22:57:58 UTC
|
|
- Changes: +138/-3, Files changed: 1, Commits: 5
|
|
|
|
## Description
|
|
|
|
all of my trust_level settings in my ~/.codex/config.toml were on one line.
|
|
|
|
## Full Diff
|
|
|
|
```diff
|
|
diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs
|
|
index 4b6f8ac9ef..5b6a1ed265 100644
|
|
--- a/codex-rs/core/src/config.rs
|
|
+++ b/codex-rs/core/src/config.rs
|
|
@@ -259,10 +259,53 @@ pub fn set_project_trusted(codex_home: &Path, project_path: &Path) -> anyhow::Re
|
|
Err(e) => return Err(e.into()),
|
|
};
|
|
|
|
- // Mark the project as trusted. toml_edit is very good at handling
|
|
- // missing properties
|
|
+ // Ensure we render a human-friendly structure:
|
|
+ //
|
|
+ // [projects]
|
|
+ // [projects."/path/to/project"]
|
|
+ // trust_level = "trusted"
|
|
+ //
|
|
+ // rather than inline tables like:
|
|
+ //
|
|
+ // [projects]
|
|
+ // "/path/to/project" = { trust_level = "trusted" }
|
|
let project_key = project_path.to_string_lossy().to_string();
|
|
- doc["projects"][project_key.as_str()]["trust_level"] = toml_edit::value("trusted");
|
|
+
|
|
+ // Ensure top-level `projects` exists as a non-inline, explicit table. If it
|
|
+ // exists but was previously represented as a non-table (e.g., inline),
|
|
+ // replace it with an explicit table.
|
|
+ {
|
|
+ let root = doc.as_table_mut();
|
|
+ let needs_table = !root.contains_key("projects")
|
|
+ || root.get("projects").and_then(|i| i.as_table()).is_none();
|
|
+ if needs_table {
|
|
+ root.insert("projects", toml_edit::table());
|
|
+ }
|
|
+ }
|
|
+ let Some(projects_tbl) = doc["projects"].as_table_mut() else {
|
|
+ return Err(anyhow::anyhow!(
|
|
+ "projects table missing after initialization"
|
|
+ ));
|
|
+ };
|
|
+
|
|
+ // Ensure the per-project entry is its own explicit table. If it exists but
|
|
+ // is not a table (e.g., an inline table), replace it with an explicit table.
|
|
+ let needs_proj_table = !projects_tbl.contains_key(project_key.as_str())
|
|
+ || projects_tbl
|
|
+ .get(project_key.as_str())
|
|
+ .and_then(|i| i.as_table())
|
|
+ .is_none();
|
|
+ if needs_proj_table {
|
|
+ projects_tbl.insert(project_key.as_str(), toml_edit::table());
|
|
+ }
|
|
+ let Some(proj_tbl) = projects_tbl
|
|
+ .get_mut(project_key.as_str())
|
|
+ .and_then(|i| i.as_table_mut())
|
|
+ else {
|
|
+ return Err(anyhow::anyhow!("project table missing for {}", project_key));
|
|
+ };
|
|
+ proj_tbl.set_implicit(false);
|
|
+ proj_tbl["trust_level"] = toml_edit::value("trusted");
|
|
|
|
// ensure codex_home exists
|
|
std::fs::create_dir_all(codex_home)?;
|
|
@@ -1178,4 +1221,96 @@ disable_response_storage = true
|
|
|
|
Ok(())
|
|
}
|
|
+
|
|
+ #[test]
|
|
+ fn test_set_project_trusted_writes_explicit_tables() -> anyhow::Result<()> {
|
|
+ let codex_home = TempDir::new().unwrap();
|
|
+ let project_dir = TempDir::new().unwrap();
|
|
+
|
|
+ // Call the function under test
|
|
+ set_project_trusted(codex_home.path(), project_dir.path())?;
|
|
+
|
|
+ // Read back the generated config.toml
|
|
+ let config_path = codex_home.path().join(CONFIG_TOML_FILE);
|
|
+ let contents = std::fs::read_to_string(&config_path)?;
|
|
+
|
|
+ // Verify it does not use inline tables for the project entry
|
|
+ assert!(
|
|
+ !contents.contains("{ trust_level"),
|
|
+ "config.toml should not use inline tables:\n{}",
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // Verify the explicit table for the project exists. toml_edit may choose
|
|
+ // either basic (double-quoted) or literal (single-quoted) strings for keys
|
|
+ // containing backslashes (e.g., on Windows). Accept both forms.
|
|
+ let path_str = project_dir.path().to_string_lossy();
|
|
+ let project_key_double = format!("[projects.\"{}\"]", path_str);
|
|
+ let project_key_single = format!("[projects.'{}']", path_str);
|
|
+ assert!(
|
|
+ contents.contains(&project_key_double) || contents.contains(&project_key_single),
|
|
+ "missing explicit project table header: expected to find `{}` or `{}` in:\n{}",
|
|
+ project_key_double,
|
|
+ project_key_single,
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // Verify the trust_level entry
|
|
+ assert!(
|
|
+ contents.contains("trust_level = \"trusted\""),
|
|
+ "missing trust_level entry in:\n{}",
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn test_set_project_trusted_converts_inline_to_explicit() -> anyhow::Result<()> {
|
|
+ let codex_home = TempDir::new().unwrap();
|
|
+ let project_dir = TempDir::new().unwrap();
|
|
+
|
|
+ // Seed config.toml with an inline project entry under [projects]
|
|
+ let config_path = codex_home.path().join(CONFIG_TOML_FILE);
|
|
+ let path_str = project_dir.path().to_string_lossy();
|
|
+ // Use a literal-quoted key so Windows backslashes don't require escaping
|
|
+ let initial = format!(
|
|
+ "[projects]\n'{}' = {{ trust_level = \"untrusted\" }}\n",
|
|
+ path_str
|
|
+ );
|
|
+ std::fs::create_dir_all(codex_home.path())?;
|
|
+ std::fs::write(&config_path, initial)?;
|
|
+
|
|
+ // Run the function; it should convert to explicit tables and set trusted
|
|
+ set_project_trusted(codex_home.path(), project_dir.path())?;
|
|
+
|
|
+ let contents = std::fs::read_to_string(&config_path)?;
|
|
+
|
|
+ // Should not contain inline table representation anymore (accept both quote styles)
|
|
+ let inline_double = format!("\"{}\" = {{ trust_level = \"trusted\" }}", path_str);
|
|
+ let inline_single = format!("'{}' = {{ trust_level = \"trusted\" }}", path_str);
|
|
+ assert!(
|
|
+ !contents.contains(&inline_double) && !contents.contains(&inline_single),
|
|
+ "config.toml should not contain inline project table anymore:\n{}",
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // And explicit child table header for the project
|
|
+ let project_key_double = format!("[projects.\"{}\"]", path_str);
|
|
+ let project_key_single = format!("[projects.'{}']", path_str);
|
|
+ assert!(
|
|
+ contents.contains(&project_key_double) || contents.contains(&project_key_single),
|
|
+ "missing explicit project table header: expected to find `{}` or `{}` in:\n{}",
|
|
+ project_key_double,
|
|
+ project_key_single,
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // And the trust level value
|
|
+ assert!(contents.contains("trust_level = \"trusted\""));
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+
|
|
+ // No test enforcing the presence of a standalone [projects] header.
|
|
}
|
|
```
|
|
|
|
## Review Comments
|
|
|
|
### codex-rs/core/src/config.rs
|
|
|
|
- Created: 2025-08-21 22:57:53 UTC | Link: https://github.com/openai/codex/pull/2523#discussion_r2292297233
|
|
|
|
```diff
|
|
@@ -1178,4 +1221,96 @@ disable_response_storage = true
|
|
|
|
Ok(())
|
|
}
|
|
+
|
|
+ #[test]
|
|
+ fn test_set_project_trusted_writes_explicit_tables() -> anyhow::Result<()> {
|
|
+ let codex_home = TempDir::new().unwrap();
|
|
+ let project_dir = TempDir::new().unwrap();
|
|
+
|
|
+ // Call the function under test
|
|
+ set_project_trusted(codex_home.path(), project_dir.path())?;
|
|
+
|
|
+ // Read back the generated config.toml
|
|
+ let config_path = codex_home.path().join(CONFIG_TOML_FILE);
|
|
+ let contents = std::fs::read_to_string(&config_path)?;
|
|
+
|
|
+ // Verify it does not use inline tables for the project entry
|
|
+ assert!(
|
|
+ !contents.contains("{ trust_level"),
|
|
+ "config.toml should not use inline tables:\n{}",
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // Verify the explicit table for the project exists. toml_edit may choose
|
|
+ // either basic (double-quoted) or literal (single-quoted) strings for keys
|
|
+ // containing backslashes (e.g., on Windows). Accept both forms.
|
|
+ let path_str = project_dir.path().to_string_lossy();
|
|
+ let project_key_double = format!("[projects.\"{}\"]", path_str);
|
|
+ let project_key_single = format!("[projects.'{}']", path_str);
|
|
+ assert!(
|
|
+ contents.contains(&project_key_double) || contents.contains(&project_key_single),
|
|
+ "missing explicit project table header: expected to find `{}` or `{}` in:\n{}",
|
|
+ project_key_double,
|
|
+ project_key_single,
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ // Verify the trust_level entry
|
|
+ assert!(
|
|
+ contents.contains("trust_level = \"trusted\""),
|
|
+ "missing trust_level entry in:\n{}",
|
|
+ contents
|
|
+ );
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn test_set_project_trusted_converts_inline_to_explicit() -> anyhow::Result<()> {
|
|
+ let codex_home = TempDir::new().unwrap();
|
|
+ let project_dir = TempDir::new().unwrap();
|
|
+
|
|
+ // Seed config.toml with an inline project entry under [projects]
|
|
+ let config_path = codex_home.path().join(CONFIG_TOML_FILE);
|
|
+ let path_str = project_dir.path().to_string_lossy();
|
|
+ // Use a literal-quoted key so Windows backslashes don't require escaping
|
|
+ let initial = format!(
|
|
+ "[projects]\n'{}' = {{ trust_level = \"untrusted\" }}\n",
|
|
+ path_str
|
|
+ );
|
|
+ std::fs::create_dir_all(codex_home.path())?;
|
|
+ std::fs::write(&config_path, initial)?;
|
|
+
|
|
+ // Run the function; it should convert to explicit tables and set trusted
|
|
+ set_project_trusted(codex_home.path(), project_dir.path())?;
|
|
+
|
|
+ let contents = std::fs::read_to_string(&config_path)?;
|
|
```
|
|
|
|
> Is there a reason why we cannot `assert_eq!()` on `contents`? What is variable in this point of the test? |