Extend bundled package discovery and expose its version (#37886)

## What changed

- Recognize executables under `codex-resources/` as part of a package layout by resolving the sibling `bin/` directory, while requiring that directory to exist.
- Add `InstallContext::package_manifest()` to parse the semantic package version from `codex-package.json`.

## Testing

- Extend the package-layout test fixture with a complete manifest and verify that version `1.2.3` is returned.

GitOrigin-RevId: 034aad3e8b865c6c6ecdff47da7c12c969744404
This commit is contained in:
Adam Perry @ OpenAI
2026-08-10 22:06:38 +00:00
committed by copyberry
parent 9558d830f6
commit cc2f262033
3 changed files with 53 additions and 1 deletions

7
codex-rs/Cargo.lock generated
View File

@@ -3392,6 +3392,9 @@ dependencies = [
"codex-utils-absolute-path",
"codex-utils-home-dir",
"pretty_assertions",
"semver",
"serde",
"serde_json",
"tempfile",
]
@@ -12256,6 +12259,10 @@ name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "sentry"

View File

@@ -15,6 +15,9 @@ workspace = true
[dependencies]
codex-utils-absolute-path = { workspace = true }
codex-utils-home-dir = { workspace = true }
semver = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View File

@@ -4,6 +4,8 @@ use std::path::PathBuf;
use std::sync::OnceLock;
use codex_utils_absolute_path::AbsolutePathBuf;
use semver::Version;
use serde::Deserialize;
const BIN_DIRNAME: &str = "bin";
const CODE_MODE_HOST_EXECUTABLE_NAME: &str = if cfg!(windows) {
@@ -37,6 +39,12 @@ pub struct CodexPackageLayout {
pub path_dir: Option<AbsolutePathBuf>,
}
/// Version metadata recorded in a bundled Codex runtime package.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct CodexPackageManifest {
pub version: Version,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InstallContext {
pub method: InstallMethod,
@@ -128,6 +136,15 @@ impl InstallContext {
})
}
/// Read the manifest for the package that contains the current executable.
pub fn package_manifest(&self) -> Option<CodexPackageManifest> {
let package_layout = self.package_layout.as_ref()?;
let manifest =
std::fs::read_to_string(package_layout.package_dir.join(PACKAGE_METADATA_FILENAME))
.ok()?;
serde_json::from_str(&manifest).ok()
}
pub fn rg_command(&self) -> PathBuf {
if let Some(package_layout) = &self.package_layout
&& let Some(path_dir) = &package_layout.path_dir
@@ -227,11 +244,18 @@ impl CodexPackageLayout {
let exe_dir = canonical_exe.parent()?;
match exe_dir.file_name() {
Some(name) if name == OsStr::new(BIN_DIRNAME) => Self::from_package_bin_dir(exe_dir),
Some(name) if name == OsStr::new(RESOURCES_DIRNAME) => {
let package_dir = exe_dir.parent()?;
Self::from_package_bin_dir(package_dir.join(BIN_DIRNAME))
}
Some(_) | None => None,
}
}
fn from_package_bin_dir(bin_dir: AbsolutePathBuf) -> Option<Self> {
if !bin_dir.is_dir() {
return None;
}
let package_dir = bin_dir.parent()?;
if !package_dir.join(PACKAGE_METADATA_FILENAME).is_file() {
return None;
@@ -495,7 +519,19 @@ mod tests {
fs::create_dir_all(&bin_dir)?;
fs::create_dir_all(&resources_dir)?;
fs::create_dir_all(&path_dir)?;
fs::write(package_dir.path().join(PACKAGE_METADATA_FILENAME), "{}")?;
fs::write(
package_dir.path().join(PACKAGE_METADATA_FILENAME),
r#"{
"layoutVersion": 1,
"version": "1.2.3",
"target": "x86_64-unknown-linux-musl",
"variant": "codex",
"entrypoint": "bin/codex",
"resourcesDir": "codex-resources",
"pathDir": "codex-path"
}
"#,
)?;
let exe_path = bin_dir.join(if cfg!(windows) { "codex.exe" } else { "codex" });
fs::write(&exe_path, "")?;
fs::write(bin_dir.join(CODE_MODE_HOST_EXECUTABLE_NAME), "")?;
@@ -532,6 +568,12 @@ mod tests {
package_layout: Some(package_layout),
}
);
assert_eq!(
context.package_manifest(),
Some(CodexPackageManifest {
version: Version::new(1, 2, 3),
})
);
assert_eq!(
context.code_mode_host_program_from_exe(Some(&exe_path)),
canonical_bin_dir