Add shared runtime build information (#37929)

## What changed

- Add `codex-build-info` to resolve a packaged runtime's semantic version from `codex-package.json` while preserving the commit stamped into the executable.
- Represent source builds as version `0.0.0` and expose helpers for display, serialization, and source-build detection.
- Stamp `STABLE_GIT_COMMIT` into final Bazel Rust binaries so Git changes do not invalidate the shared library graph.

## Testing

- Cover packaged, source, legacy, and invalid-version resolution, plus serialization round trips.

GitOrigin-RevId: 669b02449644c738ba2946a1b7aafe4ec31a9edb
This commit is contained in:
Adam Perry @ OpenAI
2026-08-11 03:27:48 +00:00
committed by copyberry
parent 722784e936
commit 2cc9dbb984
7 changed files with 302 additions and 1 deletions

12
codex-rs/Cargo.lock generated
View File

@@ -2328,6 +2328,18 @@ dependencies = [
"serde_with", "serde_with",
] ]
[[package]]
name = "codex-build-info"
version = "0.0.0"
dependencies = [
"codex-install-context",
"pretty_assertions",
"semver",
"serde",
"serde_json",
"tempfile",
]
[[package]] [[package]]
name = "codex-bwrap" name = "codex-bwrap"
version = "0.0.0" version = "0.0.0"

View File

@@ -6,6 +6,7 @@ members = [
"agent-identity", "agent-identity",
"backend-client", "backend-client",
"bwrap", "bwrap",
"build-info",
"ansi-escape", "ansi-escape",
"async-utils", "async-utils",
"app-server", "app-server",

View File

@@ -0,0 +1,6 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "build-info",
crate_name = "codex_build_info",
)

View File

@@ -0,0 +1,23 @@
[package]
edition.workspace = true
license.workspace = true
name = "codex-build-info"
version.workspace = true
[lib]
doctest = false
name = "codex_build_info"
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
codex-install-context = { workspace = true }
semver = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
[dev-dependencies]
pretty_assertions = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true }

View File

@@ -0,0 +1,133 @@
use std::fs;
use codex_install_context::InstallContext;
use pretty_assertions::assert_eq;
use semver::Version;
use tempfile::tempdir;
use crate::BuildInfo;
const BUILD_COMMIT: &str = "0123456789abcdef0123456789abcdef01234567";
/// A packaged runtime takes its release identity from its package manifest.
#[test]
fn packaged_runtime_uses_manifest_version() {
let package = tempdir().expect("create runtime package");
let bin_dir = package.path().join("bin");
fs::create_dir(&bin_dir).expect("create runtime binary directory");
let executable = bin_dir.join("codex");
fs::write(&executable, b"").expect("create runtime binary");
fs::write(
package.path().join("codex-package.json"),
r#"{"version":"1.2.3-alpha.4"}"#,
)
.expect("create runtime package manifest");
let context = InstallContext::from_exe(
cfg!(target_os = "macos"),
Some(&executable),
/*method_override*/ None,
);
assert_eq!(
BuildInfo::resolve(&context, BUILD_COMMIT),
BuildInfo {
version: Version::parse("1.2.3-alpha.4").expect("valid release version"),
build_commit: BUILD_COMMIT.to_string(),
},
);
}
/// Unpackaged builds expose their stamped commit and structured source version.
#[test]
fn unpackaged_runtime_uses_build_commit() {
let context = InstallContext::from_exe(
cfg!(target_os = "macos"),
/*current_exe*/ None,
/*method_override*/ None,
);
assert_eq!(
BuildInfo::resolve(&context, BUILD_COMMIT),
BuildInfo {
version: Version::new(0, 0, 0),
build_commit: BUILD_COMMIT.to_string(),
},
);
}
/// Older package layouts without release metadata retain their build identity.
#[test]
fn legacy_package_without_version_uses_build_commit() {
let package = tempdir().expect("create runtime package");
let bin_dir = package.path().join("bin");
fs::create_dir(&bin_dir).expect("create runtime binary directory");
let executable = bin_dir.join("codex");
fs::write(&executable, b"").expect("create runtime binary");
fs::write(package.path().join("codex-package.json"), "{}")
.expect("create legacy runtime package manifest");
let context = InstallContext::from_exe(
cfg!(target_os = "macos"),
Some(&executable),
/*method_override*/ None,
);
assert_eq!(
BuildInfo::resolve(&context, BUILD_COMMIT),
BuildInfo {
version: Version::new(0, 0, 0),
build_commit: BUILD_COMMIT.to_string(),
},
);
}
/// Invalid package versions cannot override the executable's stamped commit.
#[test]
fn invalid_package_version_uses_build_commit() {
let package = tempdir().expect("create runtime package");
let bin_dir = package.path().join("bin");
fs::create_dir(&bin_dir).expect("create runtime binary directory");
let executable = bin_dir.join("codex");
fs::write(&executable, b"").expect("create runtime binary");
fs::write(
package.path().join("codex-package.json"),
r#"{"version":"not-a-release-version"}"#,
)
.expect("create runtime package manifest");
let context = InstallContext::from_exe(
cfg!(target_os = "macos"),
Some(&executable),
/*method_override*/ None,
);
assert_eq!(
BuildInfo::resolve(&context, BUILD_COMMIT),
BuildInfo {
version: Version::new(0, 0, 0),
build_commit: BUILD_COMMIT.to_string(),
},
);
}
/// Serializing build information preserves both its release version and commit.
#[test]
fn build_info_serialization_preserves_build_provenance() {
let build_info = BuildInfo {
version: Version::parse("1.2.3-alpha.4").expect("valid release version"),
build_commit: BUILD_COMMIT.to_string(),
};
assert_eq!(
serde_json::to_string(&build_info).expect("serialize build information"),
format!("{{\"version\":\"1.2.3-alpha.4\",\"build_commit\":\"{BUILD_COMMIT}\"}}"),
);
assert_eq!(
serde_json::from_str::<BuildInfo>(&format!(
"{{\"version\":\"1.2.3-alpha.4\",\"build_commit\":\"{BUILD_COMMIT}\"}}"
))
.expect("deserialize build information"),
build_info,
);
}

View File

@@ -0,0 +1,123 @@
//! Resolve the release identity of the current Codex runtime.
use std::fmt;
use std::sync::OnceLock;
use codex_install_context::InstallContext;
use semver::Version;
use serde::Deserialize;
use serde::Serialize;
static BUILD_INFO: OnceLock<BuildInfo> = OnceLock::new();
/// Initialize build information from the commit stamped into the calling executable.
///
/// The environment lookup intentionally expands at the macro call site so Git
/// changes invalidate only final binary actions, not this shared library.
#[macro_export]
macro_rules! initialize {
() => {
$crate::BuildInfo::initialize(option_env!("STABLE_GIT_COMMIT").unwrap_or("dev"));
};
}
/// The packaged release version and build provenance for the current runtime.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BuildInfo {
version: Version,
build_commit: String,
}
impl BuildInfo {
/// Return build information for the current Codex runtime.
pub fn get() -> Self {
BUILD_INFO
.get_or_init(|| Self::resolve(InstallContext::current(), "dev"))
.clone()
}
/// Initialize build information using the final executable's stamped commit.
///
/// Keeping the stamp in the executable prevents Git operations from
/// invalidating the shared Rust library graph.
#[doc(hidden)]
pub fn initialize(build_commit: &'static str) {
let _ = BUILD_INFO.get_or_init(|| Self::resolve(InstallContext::current(), build_commit));
}
/// Recover structured release information from a persisted version string.
pub fn from_version(version: impl Into<String>) -> Self {
let version = version.into();
match Version::parse(&version) {
Ok(parsed_version) => Self {
build_commit: if parsed_version.major == 0
&& parsed_version.minor == 0
&& parsed_version.patch == 0
{
version
} else {
"unknown".to_string()
},
version: parsed_version,
},
Err(_) => Self {
version: Version::new(0, 0, 0),
build_commit: version,
},
}
}
/// Return the parsed package version, or `0.0.0` for a source build.
pub fn version(&self) -> &Version {
&self.version
}
/// Format the version for a user-facing Codex header.
pub fn display_version(&self) -> String {
if self.build_commit == "dev" {
"dev".to_string()
} else if self.is_source_build() {
format!("v{}", self.build_commit)
} else {
format!("v{}", self.version)
}
}
/// Identify source builds without parsing their displayed Git commit.
pub fn is_source_build(&self) -> bool {
self.version.major == 0 && self.version.minor == 0 && self.version.patch == 0
}
/// Return the Git commit stamped into the final executable.
pub fn build_commit(&self) -> &str {
&self.build_commit
}
fn resolve(install_context: &InstallContext, build_commit: &'static str) -> Self {
if let Some(manifest) = install_context.package_manifest() {
return Self {
version: manifest.version,
build_commit: build_commit.to_owned(),
};
}
Self {
version: Version::new(0, 0, 0),
build_commit: build_commit.to_owned(),
}
}
}
impl fmt::Display for BuildInfo {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_source_build() {
formatter.write_str(&self.build_commit)
} else {
self.version.fmt(formatter)
}
}
}
#[cfg(test)]
#[path = "build_info_tests.rs"]
mod tests;

View File

@@ -378,7 +378,6 @@ def codex_rust_crate(
sanitized_binaries.append(binary) sanitized_binaries.append(binary)
cargo_env_runfiles[":" + binary] = "CARGO_BIN_EXE_" + binary cargo_env_runfiles[":" + binary] = "CARGO_BIN_EXE_" + binary
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath :%s)" % binary cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath :%s)" % binary
rust_binary( rust_binary(
name = binary, name = binary,
crate_name = binary.replace("-", "_"), crate_name = binary.replace("-", "_"),
@@ -386,7 +385,11 @@ def codex_rust_crate(
deps = all_crate_deps() + maybe_deps + deps_extra, deps = all_crate_deps() + maybe_deps + deps_extra,
edition = crate_edition, edition = crate_edition,
rustc_flags = rustc_flags_extra + WINDOWS_RUSTC_LINK_FLAGS, rustc_flags = rustc_flags_extra + WINDOWS_RUSTC_LINK_FLAGS,
# rules_rust substitutes workspace status values only for stamped
# actions, so pass the existing key through to final binaries.
rustc_env = {"STABLE_GIT_COMMIT": "{STABLE_GIT_COMMIT}"},
srcs = native.glob(["src/**/*.rs"]), srcs = native.glob(["src/**/*.rs"]),
stamp = 1,
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )