mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Make project trust lookup paths explicit and defer root resolution (#47924)
## What changed - Export `ProjectTrustPath` and make `ProjectTrustLookup::from_paths` accept an explicit working directory and optional repository root instead of an arbitrary sequence of path pairs. - Defer repository-root canonicalization until neither working-directory key matches, avoiding unnecessary filesystem access while preserving lookup precedence. ## Testing Extend the active-project regression test to cover repository-root fallback and ensure a working-directory entry without a trust level takes precedence over a trusted root. Adapt the existing POSIX and Windows lookup tests to the new API. GitOrigin-RevId: ba5dc0df1ef9d5109933df00c055eef04434601f
This commit is contained in:
@@ -866,10 +866,14 @@ impl ConfigToml {
|
||||
repo_root: Option<&Path>,
|
||||
) -> Option<ProjectConfig> {
|
||||
self.projects.as_ref()?;
|
||||
self.get_active_project_for_lookup(&crate::ProjectTrustLookup::from_native(
|
||||
resolved_cwd,
|
||||
repo_root,
|
||||
))
|
||||
// Only canonicalize the repository root when neither cwd key matches.
|
||||
std::iter::once(resolved_cwd)
|
||||
.chain(repo_root)
|
||||
.find_map(|path| {
|
||||
self.get_active_project_for_lookup(&crate::ProjectTrustLookup::from_native_path(
|
||||
path,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ pub use plugin_edit::set_user_plugin_enabled;
|
||||
pub use project_root_markers::default_project_root_markers;
|
||||
pub use project_root_markers::project_root_markers_from_config;
|
||||
pub use project_trust::ProjectTrustLookup;
|
||||
pub use project_trust::ProjectTrustPath;
|
||||
pub use requirements_exec_policy::RequirementsExecPolicyDecisionToml;
|
||||
pub use requirements_exec_policy::RequirementsExecPolicyParseError;
|
||||
pub use requirements_exec_policy::RequirementsExecPolicyPatternTokenToml;
|
||||
|
||||
@@ -6,6 +6,13 @@ use codex_utils_path::normalize_for_path_comparison;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use std::path::Path;
|
||||
|
||||
/// Original and executor-normalized canonical spellings of one native path.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ProjectTrustPath {
|
||||
pub original: String,
|
||||
pub canonical: Option<String>,
|
||||
}
|
||||
|
||||
/// Ordered project lookup keys for one executor working directory.
|
||||
///
|
||||
/// Resolve this independently of configuration layers, then apply it after all
|
||||
@@ -18,23 +25,22 @@ pub struct ProjectTrustLookup {
|
||||
}
|
||||
|
||||
impl ProjectTrustLookup {
|
||||
pub(crate) fn from_native(cwd: &Path, repo_root: Option<&Path>) -> Self {
|
||||
pub(crate) fn from_native_path(path: &Path) -> Self {
|
||||
Self::from_paths(
|
||||
PathConvention::native(),
|
||||
std::iter::once(cwd).chain(repo_root).map(|path| {
|
||||
(
|
||||
path.to_string_lossy().into_owned(),
|
||||
normalize_for_path_comparison(path)
|
||||
.ok()
|
||||
.map(|path| path.to_string_lossy().into_owned()),
|
||||
)
|
||||
}),
|
||||
ProjectTrustPath {
|
||||
original: path.to_string_lossy().into_owned(),
|
||||
canonical: normalize_for_path_comparison(path)
|
||||
.ok()
|
||||
.map(|path| path.to_string_lossy().into_owned()),
|
||||
},
|
||||
/*repo_root*/ None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build lookup keys from `(original, canonical)` native path spellings.
|
||||
/// Build lookup keys for a cwd and optional repository root.
|
||||
///
|
||||
/// Supply the cwd first, followed by the optional repository root. Each
|
||||
/// Cwd keys always take precedence over repository-root keys. Each
|
||||
/// canonical spelling is tried before its original spelling; `None` uses
|
||||
/// only the original. This method performs no filesystem access or path
|
||||
/// resolution. Callers must supply executor-normalized canonical spellings,
|
||||
@@ -43,10 +49,15 @@ impl ProjectTrustLookup {
|
||||
/// matched without ASCII case distinctions; POSIX keys remain case-sensitive.
|
||||
pub fn from_paths(
|
||||
convention: PathConvention,
|
||||
paths: impl IntoIterator<Item = (String, Option<String>)>,
|
||||
cwd: ProjectTrustPath,
|
||||
repo_root: Option<ProjectTrustPath>,
|
||||
) -> Self {
|
||||
let mut keys = Vec::new();
|
||||
for (original, canonical) in paths {
|
||||
for ProjectTrustPath {
|
||||
original,
|
||||
canonical,
|
||||
} in std::iter::once(cwd).chain(repo_root)
|
||||
{
|
||||
let original = normalize_lookup_key(&original, convention);
|
||||
let canonical = canonical
|
||||
.map(|key| normalize_lookup_key(&key, convention))
|
||||
|
||||
@@ -45,10 +45,14 @@ fn assert_lookup_order(lookup: &ProjectTrustLookup, entries: &[(&str, Option<Tru
|
||||
fn posix_lookup_preserves_cwd_root_alias_order_and_case() {
|
||||
let lookup = ProjectTrustLookup::from_paths(
|
||||
PathConvention::Posix,
|
||||
[
|
||||
("/alias/src".into(), Some("/repo/src".into())),
|
||||
("/alias".into(), Some("/repo".into())),
|
||||
],
|
||||
ProjectTrustPath {
|
||||
original: "/alias/src".into(),
|
||||
canonical: Some("/repo/src".into()),
|
||||
},
|
||||
Some(ProjectTrustPath {
|
||||
original: "/alias".into(),
|
||||
canonical: Some("/repo".into()),
|
||||
}),
|
||||
);
|
||||
assert_lookup_order(
|
||||
&lookup,
|
||||
@@ -68,8 +72,14 @@ fn posix_lookup_preserves_cwd_root_alias_order_and_case() {
|
||||
|
||||
#[test]
|
||||
fn windows_prefers_exact_normalized_key_then_sorted_aliases() {
|
||||
let lookup =
|
||||
ProjectTrustLookup::from_paths(PathConvention::Windows, [(r"C:\Repo".into(), None)]);
|
||||
let lookup = ProjectTrustLookup::from_paths(
|
||||
PathConvention::Windows,
|
||||
ProjectTrustPath {
|
||||
original: r"C:\Repo".into(),
|
||||
canonical: None,
|
||||
},
|
||||
/*repo_root*/ None,
|
||||
);
|
||||
assert_lookup_order(
|
||||
&lookup,
|
||||
&[
|
||||
|
||||
@@ -10425,14 +10425,14 @@ trust_level = "trusted"
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn active_project_does_not_match_configured_alias_for_canonical_cwd() -> anyhow::Result<()> {
|
||||
async fn active_project_preserves_cwd_alias_and_repo_root_precedence() -> anyhow::Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
let project_root = tmp.path().join("project");
|
||||
let alias_root = tmp.path().join("project_alias");
|
||||
std::fs::create_dir_all(&project_root)?;
|
||||
std::os::unix::fs::symlink(&project_root, &alias_root)?;
|
||||
|
||||
let config = ConfigToml {
|
||||
let mut config = ConfigToml {
|
||||
projects: Some(HashMap::from([(
|
||||
alias_root.to_string_lossy().to_string(),
|
||||
ProjectConfig {
|
||||
@@ -10447,6 +10447,28 @@ async fn active_project_does_not_match_configured_alias_for_canonical_cwd() -> a
|
||||
None
|
||||
);
|
||||
|
||||
let trusted_root = ProjectConfig {
|
||||
trust_level: Some(TrustLevel::Trusted),
|
||||
};
|
||||
config.projects.as_mut().unwrap().insert(
|
||||
tmp.path().to_string_lossy().into_owned(),
|
||||
trusted_root.clone(),
|
||||
);
|
||||
assert_eq!(
|
||||
config.get_active_project(&project_root, Some(tmp.path())),
|
||||
Some(trusted_root)
|
||||
);
|
||||
|
||||
let empty_cwd = ProjectConfig { trust_level: None };
|
||||
config.projects.as_mut().unwrap().insert(
|
||||
project_root.to_string_lossy().into_owned(),
|
||||
empty_cwd.clone(),
|
||||
);
|
||||
assert_eq!(
|
||||
config.get_active_project(&project_root, Some(tmp.path())),
|
||||
Some(empty_cwd)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user