From a0a10458f6a14549b61436361e79d609853efaa7 Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Thu, 23 Apr 2026 11:44:36 -0700 Subject: [PATCH] use a version-specific suffix for command runner binary in .sandbox-bin --- .../src/helper_materialization.rs | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/helper_materialization.rs b/codex-rs/windows-sandbox-rs/src/helper_materialization.rs index ad5cc4e2b2f5..b7d00e1dd926 100644 --- a/codex-rs/windows-sandbox-rs/src/helper_materialization.rs +++ b/codex-rs/windows-sandbox-rs/src/helper_materialization.rs @@ -8,11 +8,13 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Mutex; use std::sync::OnceLock; +use std::time::UNIX_EPOCH; use tempfile::NamedTempFile; use crate::logging::log_note; use crate::sandbox_bin_dir; +const DEV_BUILD_VERSION_SENTINEL: &str = "0.0.0"; const RESOURCES_DIRNAME: &str = "codex-resources"; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -134,7 +136,7 @@ pub(crate) fn copy_helper_if_needed( } let source = sibling_source_path(kind)?; - let destination = helper_bin_dir(codex_home).join(kind.file_name()); + let destination = helper_destination_for_source(kind, codex_home, &source)?; log_note( &format!( "helper copy: validating {} source={} destination={}", @@ -197,6 +199,56 @@ fn source_path_for_exe(exe: &Path, file_name: &str) -> Option { resource_candidate.exists().then_some(resource_candidate) } +fn helper_destination_for_source( + kind: HelperExecutable, + codex_home: &Path, + source: &Path, +) -> Result { + let suffix = helper_version_suffix(source)?; + let file_name = materialized_file_name(kind, &suffix); + Ok(helper_bin_dir(codex_home).join(file_name)) +} + +fn materialized_file_name(kind: HelperExecutable, suffix: &str) -> String { + let source_name = kind.file_name(); + let path = Path::new(source_name); + let stem = path + .file_stem() + .and_then(|stem| stem.to_str()) + .unwrap_or(source_name); + let extension = path + .extension() + .and_then(|ext| ext.to_str()) + .map(|ext| format!(".{ext}")) + .unwrap_or_default(); + format!("{stem}-{suffix}{extension}") +} + +fn helper_version_suffix(source: &Path) -> Result { + let version = env!("CARGO_PKG_VERSION"); + if version == DEV_BUILD_VERSION_SENTINEL { + dev_build_suffix(source) + } else { + Ok(version.to_string()) + } +} + +fn dev_build_suffix(source: &Path) -> Result { + let metadata = fs::metadata(source) + .with_context(|| format!("read helper source metadata {}", source.display()))?; + let modified = metadata + .modified() + .with_context(|| format!("read helper source mtime {}", source.display()))?; + let duration = modified + .duration_since(UNIX_EPOCH) + .with_context(|| format!("convert helper source mtime {}", source.display()))?; + Ok(format!( + "{}-{:x}", + metadata.len(), + duration.as_secs(), + )) +} + fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result { if destination_is_fresh(source, destination)? { return Ok(CopyOutcome::Reused); @@ -298,8 +350,13 @@ fn destination_is_fresh(source: &Path, destination: &Path) -> Result { mod tests { use super::copy_from_source_if_needed; use super::CopyOutcome; + use super::dev_build_suffix; use super::destination_is_fresh; use super::helper_bin_dir; + use super::helper_version_suffix; + use super::materialized_file_name; + use super::HelperExecutable; + use super::DEV_BUILD_VERSION_SENTINEL; use super::RESOURCES_DIRNAME; use super::source_path_for_exe; use pretty_assertions::assert_eq; @@ -370,8 +427,10 @@ mod tests { let source_dir = tmp.path().join("sibling-source"); fs::create_dir_all(&source_dir).expect("create source dir"); let runner_source = source_dir.join("codex-command-runner.exe"); - let runner_destination = helper_bin_dir(&codex_home).join("codex-command-runner.exe"); fs::write(&runner_source, b"runner").expect("runner"); + let runner_suffix = helper_version_suffix(&runner_source).expect("runner suffix"); + let runner_destination = helper_bin_dir(&codex_home) + .join(materialized_file_name(HelperExecutable::CommandRunner, &runner_suffix)); let runner_outcome = copy_from_source_if_needed(&runner_source, &runner_destination).expect("runner copy"); @@ -418,4 +477,25 @@ mod tests { assert_eq!(resolved, sibling_helper); } + + #[test] + fn helper_version_suffix_uses_cli_version_or_dev_build_metadata() { + let tmp = TempDir::new().expect("tempdir"); + let source = tmp.path().join("source.exe"); + fs::write(&source, b"runner-v1").expect("write source"); + let suffix = helper_version_suffix(&source).expect("suffix"); + + if env!("CARGO_PKG_VERSION") == DEV_BUILD_VERSION_SENTINEL { + assert_eq!(suffix, dev_build_suffix(&source).expect("dev build suffix")); + } else { + assert_eq!(suffix, env!("CARGO_PKG_VERSION")); + } + } + + #[test] + fn materialized_file_name_adds_suffix_before_extension() { + let file_name = materialized_file_name(HelperExecutable::CommandRunner, "test-suffix"); + + assert_eq!(file_name, "codex-command-runner-test-suffix.exe"); + } }