From d29a2d09302a9230ef362bcbb946c08a7a431c86 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 19 May 2026 23:19:18 -0700 Subject: [PATCH] runtime: use install context for bundled bwrap ## Summary The Linux sandbox should find bundled `bwrap` through the same package-layout abstraction as the rest of the runtime, instead of maintaining a separate standalone-specific lookup path. This adds an `InstallContext` helper for bundled resources and updates `codex-linux-sandbox` to ask the current install context for `codex-resources/bwrap` before falling back to the old executable-relative probes. The tests cover npm-style, standalone, and canonical package layouts so `bwrap` lookup follows the package structure introduced earlier in the stack. ## Test plan - `cargo test -p codex-install-context` - `cargo test -p codex-linux-sandbox --lib` - `just fix -p codex-install-context -p codex-linux-sandbox` - `just bazel-lock-check` --- codex-rs/Cargo.lock | 1 + codex-rs/install-context/src/lib.rs | 45 +++++++++++++++-- codex-rs/linux-sandbox/Cargo.toml | 1 + codex-rs/linux-sandbox/src/bundled_bwrap.rs | 56 ++++++++++++++++++--- 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 09c5786a9c..56a9157202 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -3017,6 +3017,7 @@ version = "0.0.0" dependencies = [ "clap", "codex-core", + "codex-install-context", "codex-process-hardening", "codex-protocol", "codex-sandboxing", diff --git a/codex-rs/install-context/src/lib.rs b/codex-rs/install-context/src/lib.rs index d1f8d3839b..05f76b293b 100644 --- a/codex-rs/install-context/src/lib.rs +++ b/codex-rs/install-context/src/lib.rs @@ -142,6 +142,30 @@ impl InstallContext { default_rg_command() } + + pub fn bundled_resource(&self, file_name: impl AsRef) -> Option { + if let Some(package_layout) = &self.package_layout + && let Some(resources_dir) = &package_layout.resources_dir + { + let resource = resources_dir.join(file_name.as_ref()); + if resource.exists() { + return Some(resource); + } + } + + if let InstallMethod::Standalone { + resources_dir: Some(resources_dir), + .. + } = &self.method + { + let resource = resources_dir.join(file_name); + if resource.exists() { + return Some(resource); + } + } + + None + } } impl CodexPackageLayout { @@ -253,6 +277,7 @@ mod tests { let exe_path = release_dir.join(if cfg!(windows) { "codex.exe" } else { "codex" }); fs::write(&exe_path, "")?; fs::write(resources_dir.join(default_rg_command()), "")?; + fs::write(resources_dir.join("bwrap"), "")?; let canonical_release_dir = AbsolutePathBuf::from_absolute_path(release_dir.canonicalize()?)?; let canonical_resources_dir = @@ -270,12 +295,16 @@ mod tests { InstallContext { method: InstallMethod::Standalone { release_dir: canonical_release_dir, - resources_dir: Some(canonical_resources_dir), + resources_dir: Some(canonical_resources_dir.clone()), platform: standalone_platform(), }, package_layout: None, } ); + assert_eq!( + context.bundled_resource("bwrap"), + Some(canonical_resources_dir.join("bwrap")) + ); Ok(()) } @@ -312,6 +341,7 @@ mod tests { fs::write(package_dir.path().join(PACKAGE_METADATA_FILENAME), "{}")?; let exe_path = bin_dir.join(if cfg!(windows) { "codex.exe" } else { "codex" }); fs::write(&exe_path, "")?; + fs::write(resources_dir.join("bwrap"), "")?; fs::write(path_dir.join(default_rg_command()), "")?; let canonical_package_dir = AbsolutePathBuf::from_absolute_path(package_dir.path().canonicalize()?)?; @@ -322,7 +352,7 @@ mod tests { let package_layout = CodexPackageLayout { package_dir: canonical_package_dir, bin_dir: canonical_bin_dir, - resources_dir: Some(canonical_resources_dir), + resources_dir: Some(canonical_resources_dir.clone()), path_dir: Some(canonical_path_dir.clone()), }; @@ -346,6 +376,10 @@ mod tests { .join(default_rg_command()) .into_path_buf() ); + assert_eq!( + context.bundled_resource("bwrap"), + Some(canonical_resources_dir.join("bwrap")) + ); Ok(()) } @@ -364,6 +398,7 @@ mod tests { fs::write(package_dir.join(PACKAGE_METADATA_FILENAME), "{}")?; let exe_path = bin_dir.join(if cfg!(windows) { "codex.exe" } else { "codex" }); fs::write(&exe_path, "")?; + fs::write(resources_dir.join("bwrap"), "")?; fs::write(path_dir.join(default_rg_command()), "")?; let canonical_package_dir = AbsolutePathBuf::from_absolute_path(package_dir.canonicalize()?)?; @@ -390,7 +425,7 @@ mod tests { package_layout: Some(CodexPackageLayout { package_dir: canonical_package_dir, bin_dir: canonical_bin_dir, - resources_dir: Some(canonical_resources_dir), + resources_dir: Some(canonical_resources_dir.clone()), path_dir: Some(canonical_path_dir.clone()), }), } @@ -401,6 +436,10 @@ mod tests { .join(default_rg_command()) .into_path_buf() ); + assert_eq!( + context.bundled_resource("bwrap"), + Some(canonical_resources_dir.join("bwrap")) + ); Ok(()) } diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml index 1ae2e6b5f5..fc7937536c 100644 --- a/codex-rs/linux-sandbox/Cargo.toml +++ b/codex-rs/linux-sandbox/Cargo.toml @@ -18,6 +18,7 @@ workspace = true [target.'cfg(target_os = "linux")'.dependencies] clap = { workspace = true, features = ["derive"] } +codex-install-context = { workspace = true } codex-process-hardening = { workspace = true } codex-protocol = { workspace = true } codex-sandboxing = { workspace = true } diff --git a/codex-rs/linux-sandbox/src/bundled_bwrap.rs b/codex-rs/linux-sandbox/src/bundled_bwrap.rs index 505377907f..972be9dac3 100644 --- a/codex-rs/linux-sandbox/src/bundled_bwrap.rs +++ b/codex-rs/linux-sandbox/src/bundled_bwrap.rs @@ -12,6 +12,7 @@ use std::sync::OnceLock; use crate::bazel_bwrap; use crate::exec_util::argv_to_cstrings; use crate::exec_util::make_files_inheritable; +use codex_install_context::InstallContext; use codex_utils_absolute_path::AbsolutePathBuf; use sha2::Digest as _; use sha2::Sha256; @@ -26,7 +27,9 @@ pub(crate) struct BundledBwrapLauncher { pub(crate) fn launcher() -> Option { let current_exe = std::env::current_exe().ok()?; - find_for_exe(¤t_exe).map(|program| BundledBwrapLauncher { program }) + find_for_install_context(InstallContext::current()) + .or_else(|| find_legacy_for_exe(¤t_exe)) + .map(|program| BundledBwrapLauncher { program }) } impl BundledBwrapLauncher { @@ -66,8 +69,14 @@ impl BundledBwrapLauncher { } } -fn find_for_exe(exe: &Path) -> Option { - candidates_for_exe(exe) +fn find_for_install_context(context: &InstallContext) -> Option { + context + .bundled_resource("bwrap") + .filter(|path| is_executable_file(path)) +} + +fn find_legacy_for_exe(exe: &Path) -> Option { + legacy_candidates_for_exe(exe) .into_iter() .find(|candidate| is_executable_file(candidate)) .map(|path| { @@ -80,7 +89,7 @@ fn find_for_exe(exe: &Path) -> Option { }) } -fn candidates_for_exe(exe: &Path) -> Vec { +fn legacy_candidates_for_exe(exe: &Path) -> Vec { let Some(exe_dir) = exe.parent() else { return Vec::new(); }; @@ -180,13 +189,44 @@ fn bytes_to_hex(bytes: &[u8; 32]) -> String { #[cfg(test)] mod tests { use super::*; + use codex_install_context::CodexPackageLayout; + use codex_install_context::InstallContext; + use codex_install_context::InstallMethod; use pretty_assertions::assert_eq; use std::fs; use tempfile::NamedTempFile; use tempfile::tempdir; #[test] - fn finds_standalone_bundled_bwrap_next_to_exe_resources() { + fn finds_package_layout_bwrap_from_install_context() { + let temp_dir = tempdir().expect("temp dir"); + let package_dir = temp_dir.path(); + let bin_dir = package_dir.join("bin"); + let resources_dir = package_dir.join("codex-resources"); + let expected_bwrap = resources_dir.join("bwrap"); + fs::create_dir_all(&bin_dir).expect("create bin dir"); + write_executable(&expected_bwrap); + + let context = InstallContext { + method: InstallMethod::Other, + package_layout: Some(CodexPackageLayout { + package_dir: AbsolutePathBuf::from_absolute_path(package_dir).expect("absolute"), + bin_dir: AbsolutePathBuf::from_absolute_path(&bin_dir).expect("absolute"), + resources_dir: Some( + AbsolutePathBuf::from_absolute_path(&resources_dir).expect("absolute"), + ), + path_dir: None, + }), + }; + + assert_eq!( + find_for_install_context(&context), + Some(AbsolutePathBuf::from_absolute_path(&expected_bwrap).expect("absolute")) + ); + } + + #[test] + fn finds_legacy_standalone_bundled_bwrap_next_to_exe_resources() { let temp_dir = tempdir().expect("temp dir"); let exe = temp_dir.path().join("codex"); let expected_bwrap = temp_dir.path().join("codex-resources").join("bwrap"); @@ -194,7 +234,7 @@ mod tests { write_executable(&expected_bwrap); assert_eq!( - find_for_exe(&exe), + find_legacy_for_exe(&exe), Some(AbsolutePathBuf::from_absolute_path(&expected_bwrap).expect("absolute")) ); } @@ -209,7 +249,7 @@ mod tests { write_executable(&expected_bwrap); assert_eq!( - find_for_exe(&exe), + find_legacy_for_exe(&exe), Some(AbsolutePathBuf::from_absolute_path(&expected_bwrap).expect("absolute")) ); } @@ -223,7 +263,7 @@ mod tests { write_executable(&expected_bwrap); assert_eq!( - find_for_exe(&exe), + find_legacy_for_exe(&exe), Some(AbsolutePathBuf::from_absolute_path(&expected_bwrap).expect("absolute")) ); }