runtime: detect Codex package layout (#23596)

## Why

The package-builder stack now creates a canonical Codex package
directory where the entrypoint lives under `bin/`, bundled helper
resources live under `codex-resources/`, and bundled PATH-style tools
live under `codex-path/`. That layout is not specific to the standalone
installer: npm, brew, install scripts, and manually unpacked artifacts
should all be able to use the same package shape.

The Rust runtime still only knew about the legacy standalone release
layout, where resources sit next to the executable. A packaged binary
therefore would not identify its package root or prefer the bundled `rg`
from `codex-path/`.

## What changed

- Adds `CodexPackageLayout` to `codex-install-context` and detects it
from an executable path shaped like `<package>/bin/<entrypoint>` when
`<package>/codex-package.json` is present.
- Splits `InstallContext` into an install `method` plus an optional
package layout so the layout is shared across npm, bun, brew,
standalone, and other launch contexts.
- Stores package-layout paths as `AbsolutePathBuf` values.
- Keeps `codex-resources/` and `codex-path/` optional so Codex can still
run with degraded behavior if sidecar directories are missing.
- Updates `InstallContext::rg_command()` to prefer bundled
`codex-path/rg` or `rg.exe`, then fall back to the legacy standalone
resources location, then system `rg`.
- Updates `codex doctor` reporting so package installs show package,
bin, resources, and path directories, and so bundled search detection
recognizes `codex-path/` for any install method.

## Test plan

- `cargo test -p codex-install-context`
- `cargo test -p codex-cli`
- `cargo test -p codex-tui
update_action::tests::maps_install_context_to_update_action`
- `just bazel-lock-check`
This commit is contained in:
Michael Bolin
2026-05-19 23:13:49 -07:00
committed by GitHub
Unverified
parent 57a68fb9e3
commit cfa16fcc2e
7 changed files with 467 additions and 125 deletions
+42 -20
View File
@@ -1,6 +1,8 @@
#[cfg(any(not(debug_assertions), test))]
use codex_install_context::InstallContext;
#[cfg(any(not(debug_assertions), test))]
use codex_install_context::InstallMethod;
#[cfg(any(not(debug_assertions), test))]
use codex_install_context::StandalonePlatform;
/// Update action the CLI should perform after the TUI exits.
@@ -21,15 +23,15 @@ pub enum UpdateAction {
impl UpdateAction {
#[cfg(any(not(debug_assertions), test))]
pub(crate) fn from_install_context(context: &InstallContext) -> Option<Self> {
match context {
InstallContext::Npm => Some(UpdateAction::NpmGlobalLatest),
InstallContext::Bun => Some(UpdateAction::BunGlobalLatest),
InstallContext::Brew => Some(UpdateAction::BrewUpgrade),
InstallContext::Standalone { platform, .. } => Some(match platform {
match &context.method {
InstallMethod::Npm => Some(UpdateAction::NpmGlobalLatest),
InstallMethod::Bun => Some(UpdateAction::BunGlobalLatest),
InstallMethod::Brew => Some(UpdateAction::BrewUpgrade),
InstallMethod::Standalone { platform, .. } => Some(match platform {
StandalonePlatform::Unix => UpdateAction::StandaloneUnix,
StandalonePlatform::Windows => UpdateAction::StandaloneWindows,
}),
InstallContext::Other => None,
InstallMethod::Other => None,
}
}
@@ -66,42 +68,62 @@ pub fn get_update_action() -> Option<UpdateAction> {
#[cfg(test)]
mod tests {
use super::*;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
#[test]
fn maps_install_context_to_update_action() {
let native_release_dir = PathBuf::from("/tmp/native-release");
let native_release_dir =
AbsolutePathBuf::from_absolute_path(std::env::temp_dir().join("native-release"))
.expect("temp dir path should be absolute");
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Other),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Other,
package_layout: None,
}),
None
);
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Npm),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Npm,
package_layout: None,
}),
Some(UpdateAction::NpmGlobalLatest)
);
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Bun),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Bun,
package_layout: None,
}),
Some(UpdateAction::BunGlobalLatest)
);
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Brew),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Brew,
package_layout: None,
}),
Some(UpdateAction::BrewUpgrade)
);
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Standalone {
platform: StandalonePlatform::Unix,
release_dir: native_release_dir.clone(),
resources_dir: Some(native_release_dir.join("codex-resources")),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Standalone {
platform: StandalonePlatform::Unix,
release_dir: native_release_dir.clone(),
resources_dir: Some(native_release_dir.join("codex-resources")),
},
package_layout: None,
}),
Some(UpdateAction::StandaloneUnix)
);
assert_eq!(
UpdateAction::from_install_context(&InstallContext::Standalone {
platform: StandalonePlatform::Windows,
release_dir: native_release_dir.clone(),
resources_dir: Some(native_release_dir.join("codex-resources")),
UpdateAction::from_install_context(&InstallContext {
method: InstallMethod::Standalone {
platform: StandalonePlatform::Windows,
release_dir: native_release_dir.clone(),
resources_dir: Some(native_release_dir.join("codex-resources")),
},
package_layout: None,
}),
Some(UpdateAction::StandaloneWindows)
);