Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions codex-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ codex-prompts = { path = "prompts" }
codex-responses-api-proxy = { path = "responses-api-proxy" }
codex-response-debug-context = { path = "response-debug-context" }
codex-rmcp-client = { path = "rmcp-client" }
codex-rg = { path = "rg-shim" }
codex-rollout = { path = "rollout" }
codex-rollout-trace = { path = "rollout-trace" }
codex-sandboxing = { path = "sandboxing" }
Expand Down
22 changes: 22 additions & 0 deletions codex-rs/rg-shim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,31 @@ license.workspace = true
name = "codex-rg"
path = "src/main.rs"

[lib]
name = "codex_rg"
path = "src/lib.rs"
doctest = false

[features]
manager = [
"dep:notify",
"dep:tempfile",
"dep:tokio",
"dep:tracing",
"dep:uuid",
]

[lints]
workspace = true

[dependencies]
notify = { workspace = true, optional = true }
sha2 = { workspace = true }
tempfile = { workspace = true, optional = true }
tokio = { workspace = true, features = ["macros", "rt", "sync"], optional = true }
tracing = { workspace = true, optional = true }
uuid = { workspace = true, features = ["v4"], optional = true }

[dev-dependencies]
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
74 changes: 74 additions & 0 deletions codex-rs/rg-shim/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use sha2::Digest;
use sha2::Sha256;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;

/// Private environment key used to point the packaged shim at an
/// exec-server-owned cache generation.
pub const CACHE_ROOT_ENV: &str = "CODEX_INTERNAL_RG_CACHE_ROOT";

const FILES_FILENAME: &str = "files";
const READY_FILENAME: &str = "ready";
const ROOT_FILENAME: &str = "root";

#[derive(Clone, Debug, Eq, PartialEq)]
struct InventoryCache {
directory: PathBuf,
files: PathBuf,
ready: PathBuf,
root: PathBuf,
root_text: String,
}

impl InventoryCache {
fn new(cache_root: &Path, repository_root: &Path) -> Option<Self> {
let root_text = repository_root.to_str()?.to_string();
let key = format!("{:x}", Sha256::digest(root_text.as_bytes()));
let directory = cache_root.join(key);
Some(Self {
files: directory.join(FILES_FILENAME),
ready: directory.join(READY_FILENAME),
root: directory.join(ROOT_FILENAME),
directory,
root_text,
})
}

fn open(&self) -> Option<File> {
let generation_before = fs::read(&self.ready).ok()?;
if generation_before.is_empty() || fs::read_to_string(&self.root).ok()? != self.root_text {
return None;
}
let files = File::open(&self.files).ok()?;
let generation_after = fs::read(&self.ready).ok()?;
(generation_before == generation_after).then_some(files)
}
}

/// Opens an exact cached `rg --files` result for `cwd` when the executor has
/// published a live generation for the containing Git worktree.
pub fn open_file_inventory(cache_root: &Path, cwd: &Path) -> Option<File> {
let repository_root = find_repository_root(cwd)?;
if fs::canonicalize(cwd).ok()? != repository_root {
return None;
}
InventoryCache::new(cache_root, &repository_root)?.open()
}

pub(crate) fn find_repository_root(cwd: &Path) -> Option<PathBuf> {
let cwd = fs::canonicalize(cwd).ok()?;
cwd.ancestors()
.find(|ancestor| ancestor.join(".git").exists())
.map(Path::to_path_buf)
}

#[cfg(feature = "manager")]
mod manager;
#[cfg(feature = "manager")]
pub use manager::RgCacheManager;

#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;
52 changes: 52 additions & 0 deletions codex-rs/rg-shim/src/lib_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use super::InventoryCache;
use super::find_repository_root;
use super::open_file_inventory;
use pretty_assertions::assert_eq;
use std::fs;
use std::io::Read;
use tempfile::TempDir;

#[test]
fn finds_nearest_git_worktree_root() {
let temp_dir = TempDir::new().expect("temp dir");
let outer = temp_dir.path().join("outer");
let inner = outer.join("nested");
let child = inner.join("src");
fs::create_dir_all(outer.join(".git")).expect("outer git dir");
fs::create_dir_all(inner.join(".git")).expect("inner git dir");
fs::create_dir_all(&child).expect("child dir");

assert_eq!(
find_repository_root(&child),
Some(fs::canonicalize(inner).expect("canonical inner root"))
);
}

#[test]
fn opens_only_matching_ready_inventory_at_repository_root() {
let temp_dir = TempDir::new().expect("temp dir");
let repository = temp_dir.path().join("repository");
let child = repository.join("src");
let cache_root = temp_dir.path().join("cache");
fs::create_dir_all(repository.join(".git")).expect("git dir");
fs::create_dir_all(&child).expect("child dir");
let repository = fs::canonicalize(repository).expect("canonical repository");
let cache = InventoryCache::new(&cache_root, &repository).expect("cache layout");
fs::create_dir_all(&cache.directory).expect("cache dir");
fs::write(&cache.root, repository.to_string_lossy().as_bytes()).expect("root marker");
fs::write(&cache.files, "src/lib.rs\n").expect("inventory");

assert!(open_file_inventory(&cache_root, &repository).is_none());
fs::write(&cache.ready, "generation-1").expect("ready marker");
assert!(open_file_inventory(&cache_root, &child).is_none());

let mut inventory = String::new();
open_file_inventory(&cache_root, &repository)
.expect("ready inventory")
.read_to_string(&mut inventory)
.expect("read inventory");
assert_eq!(inventory, "src/lib.rs\n");

fs::write(&cache.root, "/different/repository").expect("mismatched root marker");
assert!(open_file_inventory(&cache_root, &repository).is_none());
}
47 changes: 36 additions & 11 deletions codex-rs/rg-shim/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use codex_rg::CACHE_ROOT_ENV;
use codex_rg::open_file_inventory;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
Expand All @@ -9,9 +13,7 @@ const PATH_DIRNAME: &str = "codex-path";
const RESOURCES_DIRNAME: &str = "codex-resources";

fn main() {
let result = std::env::current_exe()
.and_then(|current_exe| real_rg_path(&current_exe))
.and_then(run_real_rg);
let result = run();
match result {
Ok(exit_code) => std::process::exit(exit_code),
Err(error) => {
Expand All @@ -21,6 +23,33 @@ fn main() {
}
}

fn run() -> io::Result<i32> {
let current_exe = std::env::current_exe()?;
let real_rg = real_rg_path(&current_exe)?;
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
if is_default_files_request(&args)
&& std::env::var_os("RIPGREP_CONFIG_PATH").is_none()
&& let Some(cache_root) = std::env::var_os(CACHE_ROOT_ENV)
&& let Some(mut inventory) =
open_file_inventory(Path::new(&cache_root), &std::env::current_dir()?)
{
let mut stdout = io::stdout().lock();
return match io::copy(&mut inventory, &mut stdout) {
Ok(_) => {
stdout.flush()?;
Ok(0)
}
Err(error) if error.kind() == io::ErrorKind::BrokenPipe => Ok(0),
Err(error) => Err(error),
};
}
run_real_rg(real_rg, args)
}

fn is_default_files_request(args: &[OsString]) -> bool {
args == [OsStr::new("--files")]
}

fn real_rg_path(current_exe: &Path) -> io::Result<PathBuf> {
let path_dir = current_exe.parent().ok_or_else(|| {
io::Error::new(
Expand Down Expand Up @@ -60,19 +89,15 @@ fn real_rg_path(current_exe: &Path) -> io::Result<PathBuf> {
}

#[cfg(unix)]
fn run_real_rg(real_rg: PathBuf) -> io::Result<i32> {
fn run_real_rg(real_rg: PathBuf, args: Vec<OsString>) -> io::Result<i32> {
use std::os::unix::process::CommandExt;

Err(Command::new(real_rg)
.args(std::env::args_os().skip(1))
.exec())
Err(Command::new(real_rg).args(args).exec())
}

#[cfg(windows)]
fn run_real_rg(real_rg: PathBuf) -> io::Result<i32> {
let status = Command::new(real_rg)
.args(std::env::args_os().skip(1))
.status()?;
fn run_real_rg(real_rg: PathBuf, args: Vec<OsString>) -> io::Result<i32> {
let status = Command::new(real_rg).args(args).status()?;
Ok(status.code().unwrap_or(1))
}

Expand Down
12 changes: 12 additions & 0 deletions codex-rs/rg-shim/src/main_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::PACKAGE_METADATA_FILENAME;
use super::PATH_DIRNAME;
use super::RESOURCES_DIRNAME;
use super::is_default_files_request;
use super::real_rg_path;
use pretty_assertions::assert_eq;
use std::ffi::OsString;
use std::fs;
use tempfile::TempDir;

Expand Down Expand Up @@ -35,3 +37,13 @@ fn rejects_executable_outside_package_path() {

assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
}

#[test]
fn only_exact_default_files_request_uses_inventory() {
assert!(is_default_files_request(&[OsString::from("--files")]));
assert!(!is_default_files_request(&[
OsString::from("--files"),
OsString::from("-0")
]));
assert!(!is_default_files_request(&[OsString::from("--hidden")]));
}
Loading
Loading