runtime: detect Codex package layout

This commit is contained in:
Michael Bolin
2026-05-19 22:54:56 -07:00
parent 57a68fb9e3
commit c688aa2a42
7 changed files with 467 additions and 125 deletions
+62 -15
View File
@@ -39,7 +39,9 @@ use codex_core::config::ConfigBuilder;
use codex_core::config::ConfigOverrides;
use codex_core::config::find_codex_home;
use codex_features::FEATURES;
use codex_install_context::CodexPackageLayout;
use codex_install_context::InstallContext;
use codex_install_context::InstallMethod;
use codex_install_context::StandalonePlatform;
use codex_login::AuthDotJson;
use codex_login::AuthManager;
@@ -812,7 +814,10 @@ fn installation_check(show_details: bool) -> DoctorCheck {
fn doctor_install_context(current_exe: Option<&Path>) -> InstallContext {
if inherited_managed_env_for_cargo_binary(current_exe) {
InstallContext::Other
InstallContext {
method: InstallMethod::Other,
package_layout: None,
}
} else {
InstallContext::current().clone()
}
@@ -843,8 +848,8 @@ fn inherited_managed_env_for_cargo_binary(current_exe: Option<&Path>) -> bool {
}
fn describe_install_context(context: &InstallContext) -> String {
match context {
InstallContext::Standalone {
match &context.method {
InstallMethod::Standalone {
release_dir,
resources_dir,
platform,
@@ -853,22 +858,63 @@ fn describe_install_context(context: &InstallContext) -> String {
StandalonePlatform::Unix => "unix",
StandalonePlatform::Windows => "windows",
};
let resources = resources_dir
.as_ref()
.map(|path| path.display().to_string())
.unwrap_or_else(|| "none".to_string());
match &context.package_layout {
Some(package_layout) => {
let resources = display_optional_path(package_layout.resources_dir.as_deref());
let path = display_optional_path(package_layout.path_dir.as_deref());
format!(
"standalone ({platform}, package {}, bin {}, resources {resources}, path {path})",
package_layout.package_dir.display(),
package_layout.bin_dir.display()
)
}
None => {
let resources = display_optional_path(resources_dir.as_deref());
format!(
"standalone ({platform}, release {}, resources {resources})",
release_dir.display()
)
}
}
}
InstallMethod::Npm => {
describe_method_with_package_layout("npm", context.package_layout.as_ref())
}
InstallMethod::Bun => {
describe_method_with_package_layout("bun", context.package_layout.as_ref())
}
InstallMethod::Brew => {
describe_method_with_package_layout("brew", context.package_layout.as_ref())
}
InstallMethod::Other => {
describe_method_with_package_layout("other", context.package_layout.as_ref())
}
}
}
fn describe_method_with_package_layout(
method: &str,
package_layout: Option<&CodexPackageLayout>,
) -> String {
match package_layout {
Some(package_layout) => {
let resources = display_optional_path(package_layout.resources_dir.as_deref());
let path = display_optional_path(package_layout.path_dir.as_deref());
format!(
"standalone ({platform}, release {}, resources {resources})",
release_dir.display()
"{method} (package {}, bin {}, resources {resources}, path {path})",
package_layout.package_dir.display(),
package_layout.bin_dir.display()
)
}
InstallContext::Npm => "npm".to_string(),
InstallContext::Bun => "bun".to_string(),
InstallContext::Brew => "brew".to_string(),
InstallContext::Other => "other".to_string(),
None => method.to_string(),
}
}
fn display_optional_path(path: Option<&Path>) -> String {
path.map(|path| path.display().to_string())
.unwrap_or_else(|| "none".to_string())
}
#[derive(Debug, PartialEq, Eq)]
enum NpmRootCheck {
Match {
@@ -2791,13 +2837,14 @@ fn path_readiness(details: &mut Vec<String>, label: &str, path: &Path) {
}
fn standalone_release_cache_details(details: &mut Vec<String>) {
let InstallContext::Standalone { release_dir, .. } = InstallContext::current() else {
let context = InstallContext::current();
let InstallMethod::Standalone { release_dir, .. } = &context.method else {
return;
};
let Some(releases_dir) = release_dir.parent() else {
return;
};
let Ok(entries) = std::fs::read_dir(releases_dir) else {
let Ok(entries) = std::fs::read_dir(&releases_dir) else {
return;
};
let release_count = entries.filter_map(Result::ok).count();
+29 -15
View File
@@ -3,12 +3,13 @@
//! Runtime diagnostics answer provenance questions that are hard to infer from
//! user reports: which binary is running, which install channel it resembles,
//! which platform it targets, and whether the search command comes from bundled
//! standalone resources or from PATH.
//! package files or from PATH.
use std::env;
use std::process::Command;
use codex_install_context::InstallContext;
use codex_install_context::InstallMethod;
use super::CheckStatus;
use super::DoctorCheck;
@@ -49,9 +50,10 @@ pub(super) fn runtime_check() -> DoctorCheck {
/// Verifies that the search command selected by the install context is usable.
///
/// Standalone installs should point at a bundled ripgrep binary, while local or
/// package-managed installs usually resolve rg from PATH. A warning here means
/// features that depend on file search may degrade even when the CLI launches.
/// Package-layout installs should point at a bundled ripgrep binary, while local
/// installs without that layout usually resolve rg from PATH. A warning here
/// means features that depend on file search may degrade even when the CLI
/// launches.
pub(super) fn search_check() -> DoctorCheck {
let current_exe = env::current_exe().ok();
let install_context = doctor_install_context(current_exe.as_deref());
@@ -109,28 +111,40 @@ pub(super) fn search_check() -> DoctorCheck {
};
let mut check = DoctorCheck::new("runtime.search", "search", status, summary).details(details);
if status != CheckStatus::Ok {
check = check.remediation("Install ripgrep or repair the bundled standalone resources.");
check = check.remediation("Install ripgrep or repair the bundled Codex package.");
}
check
}
fn install_method_name(context: &InstallContext) -> &'static str {
match context {
InstallContext::Standalone { .. } => "standalone",
InstallContext::Npm => "npm",
InstallContext::Bun => "bun",
InstallContext::Brew => "brew",
InstallContext::Other => "local build",
match &context.method {
InstallMethod::Standalone { .. } => "standalone",
InstallMethod::Npm => "npm",
InstallMethod::Bun => "bun",
InstallMethod::Brew => "brew",
InstallMethod::Other => "local build",
}
}
fn search_provider(context: &InstallContext) -> &'static str {
match context {
InstallContext::Standalone {
let rg_command = context.rg_command();
let from_package_layout = context
.package_layout
.as_ref()
.and_then(|package_layout| package_layout.path_dir.as_ref())
.is_some_and(|path_dir| rg_command.starts_with(path_dir));
let from_legacy_standalone = matches!(
&context.method,
InstallMethod::Standalone {
resources_dir: Some(resources_dir),
..
} if context.rg_command().starts_with(resources_dir) => "bundled",
_ => "system",
} if rg_command.starts_with(resources_dir)
);
if from_package_layout || from_legacy_standalone {
"bundled"
} else {
"system"
}
}
+21 -14
View File
@@ -10,6 +10,7 @@ use std::path::Path;
use codex_core::config::Config;
use codex_install_context::InstallContext;
use codex_install_context::InstallMethod;
use serde::Deserialize;
use super::CheckStatus;
@@ -129,22 +130,22 @@ fn push_cached_version_details(details: &mut Vec<String>, version_file: &Path) {
}
fn update_action_label(context: &InstallContext) -> &'static str {
match context {
InstallContext::Npm => "npm install -g @openai/codex",
InstallContext::Bun => "bun install -g @openai/codex",
InstallContext::Brew => "brew upgrade --cask codex",
InstallContext::Standalone { .. } => "standalone installer",
InstallContext::Other => "manual or unknown",
match &context.method {
InstallMethod::Npm => "npm install -g @openai/codex",
InstallMethod::Bun => "bun install -g @openai/codex",
InstallMethod::Brew => "brew upgrade --cask codex",
InstallMethod::Standalone { .. } => "standalone installer",
InstallMethod::Other => "manual or unknown",
}
}
fn fetch_latest_version(context: &InstallContext) -> Result<String, String> {
match context {
InstallContext::Brew => fetch_homebrew_cask_version(),
InstallContext::Npm
| InstallContext::Bun
| InstallContext::Standalone { .. }
| InstallContext::Other => fetch_latest_github_release_version(),
match &context.method {
InstallMethod::Brew => fetch_homebrew_cask_version(),
InstallMethod::Npm
| InstallMethod::Bun
| InstallMethod::Standalone { .. }
| InstallMethod::Other => fetch_latest_github_release_version(),
}
}
@@ -216,11 +217,17 @@ mod tests {
#[test]
fn update_action_labels_install_contexts() {
assert_eq!(
update_action_label(&InstallContext::Npm),
update_action_label(&InstallContext {
method: InstallMethod::Npm,
package_layout: None,
}),
"npm install -g @openai/codex"
);
assert_eq!(
update_action_label(&InstallContext::Other),
update_action_label(&InstallContext {
method: InstallMethod::Other,
package_layout: None,
}),
"manual or unknown"
);
}