fix code mode host resource installation

This commit is contained in:
Channing Conger
2026-07-09 23:28:31 +00:00
parent cbdee7976b
commit 955fa324da
13 changed files with 189 additions and 129 deletions
+4 -4
View File
@@ -352,13 +352,13 @@ jobs:
rm -rf "$bundle_root"
mkdir -p "$bundle_root/codex-resources"
cp "$dest/codex-${{ matrix.target }}" "$bundle_root/codex"
cp "$dest/codex-code-mode-host-${{ matrix.target }}" "$bundle_root/codex-code-mode-host"
cp "$dest/codex-code-mode-host-${{ matrix.target }}" "$bundle_root/codex-resources/codex-code-mode-host"
cp "$dest/bwrap-${{ matrix.target }}" "$bundle_root/codex-resources/bwrap"
chmod 0755 \
"$bundle_root/codex" \
"$bundle_root/codex-code-mode-host" \
"$bundle_root/codex-resources/codex-code-mode-host" \
"$bundle_root/codex-resources/bwrap"
tar -C "$bundle_root" -cf - codex codex-code-mode-host codex-resources/bwrap |
tar -C "$bundle_root" -cf - codex codex-resources/codex-code-mode-host codex-resources/bwrap |
zstd -T0 -19 -o "$dest/codex-${{ matrix.target }}-bundle.tar.zst"
fi
@@ -1054,7 +1054,7 @@ jobs:
mkdir -p "$package_dir"
tar -xzf "${packaged_dir}/${package_stem}-${target}.tar.gz" -C "$package_dir"
verify_signed_binary "${package_dir}/bin/${package_entrypoint}" "$package_entrypoint"
verify_signed_binary "${package_dir}/bin/codex-code-mode-host" "codex-code-mode-host"
verify_signed_binary "${package_dir}/codex-resources/codex-code-mode-host" "codex-code-mode-host"
if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then
exit 0
+1
View File
@@ -2491,6 +2491,7 @@ name = "codex-code-mode"
version = "0.0.0"
dependencies = [
"codex-code-mode-protocol",
"codex-install-context",
"codex-protocol",
"deno_core_icudata",
"futures",
+1
View File
@@ -17,6 +17,7 @@ workspace = true
[dependencies]
codex-code-mode-protocol = { workspace = true }
codex-install-context = { workspace = true }
codex-protocol = { workspace = true }
deno_core_icudata = { workspace = true }
futures = { workspace = true }
+5 -33
View File
@@ -1,5 +1,3 @@
use std::ffi::OsString;
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex as StdMutex;
@@ -18,6 +16,7 @@ use codex_code_mode_protocol::StartedCell;
use codex_code_mode_protocol::WaitOutcome;
use codex_code_mode_protocol::WaitRequest;
use codex_code_mode_protocol::host::SessionId;
use codex_install_context::InstallContext;
use tokio::sync::Semaphore;
use tokio::sync::watch;
@@ -29,8 +28,6 @@ use crate::NoopCodeModeSessionDelegate;
mod connection;
const CODE_MODE_HOST_PATH_ENV: &str = "CODEX_CODE_MODE_HOST_PATH";
type ShutdownResultReceiver = watch::Receiver<Option<Result<(), String>>>;
/// Creates code-mode sessions backed by one lazily spawned process host.
@@ -66,7 +63,7 @@ impl ProcessOwnedCodeModeSessionProvider {
impl Default for ProcessOwnedCodeModeSessionProvider {
fn default() -> Self {
Self::with_host_program(default_host_program())
Self::with_host_program(InstallContext::current().code_mode_host_program())
}
}
@@ -195,7 +192,9 @@ impl ProcessOwnedCodeModeSession {
pub fn new() -> Self {
Self::with_process_host(
Arc::new(NoopCodeModeSessionDelegate),
Arc::new(OwnedProcessHost::new(default_host_program())),
Arc::new(OwnedProcessHost::new(
InstallContext::current().code_mode_host_program(),
)),
)
}
@@ -494,33 +493,6 @@ impl CodeModeSession for ProcessOwnedCodeModeSession {
}
}
fn default_host_program() -> PathBuf {
resolve_host_program(
std::env::var_os(CODE_MODE_HOST_PATH_ENV),
std::env::current_exe(),
)
}
fn resolve_host_program(
override_path: Option<OsString>,
current_exe: io::Result<PathBuf>,
) -> PathBuf {
if let Some(path) = override_path {
return PathBuf::from(path);
}
let executable_name = if cfg!(windows) {
"codex-code-mode-host.exe"
} else {
"codex-code-mode-host"
};
if let Ok(current_exe) = current_exe
&& let Some(parent) = current_exe.parent()
{
return parent.join(executable_name);
}
PathBuf::from(executable_name)
}
#[cfg(test)]
#[path = "remote_session_tests.rs"]
mod tests;
@@ -1,5 +1,3 @@
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
use codex_code_mode_protocol::CodeModeSessionProvider;
@@ -10,7 +8,6 @@ use pretty_assertions::assert_eq;
use super::ProcessOwnedCodeModeSession;
use super::ProcessOwnedCodeModeSessionProvider;
use super::resolve_host_program;
use crate::NoopCodeModeSessionDelegate;
#[test]
@@ -23,54 +20,6 @@ fn provider_reuses_its_live_process_host() {
assert!(Arc::ptr_eq(&first, &second));
}
#[test]
fn host_program_override_takes_precedence() {
assert_eq!(
resolve_host_program(
Some("custom-code-mode-host".into()),
Ok(PathBuf::from("/opt/codex/bin/codex")),
),
PathBuf::from("custom-code-mode-host")
);
}
#[test]
fn host_program_is_next_to_the_main_executable_even_when_missing() {
let executable_name = if cfg!(windows) {
"codex-code-mode-host.exe"
} else {
"codex-code-mode-host"
};
assert_eq!(
resolve_host_program(
/*override_path*/ None,
Ok(PathBuf::from("/opt/codex/bin/codex")),
),
PathBuf::from("/opt/codex/bin").join(executable_name)
);
}
#[test]
fn host_program_falls_back_to_its_name_when_main_executable_is_unknown() {
let executable_name = if cfg!(windows) {
"codex-code-mode-host.exe"
} else {
"codex-code-mode-host"
};
assert_eq!(
resolve_host_program(
/*override_path*/ None,
Err(io::Error::new(
io::ErrorKind::NotFound,
"missing executable"
)),
),
PathBuf::from(executable_name)
);
}
#[tokio::test]
async fn provider_falls_back_to_in_process_session_when_host_is_missing() {
let provider = ProcessOwnedCodeModeSessionProvider::with_host_program(
+110
View File
@@ -1,4 +1,6 @@
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::sync::OnceLock;
@@ -6,6 +8,7 @@ use std::sync::OnceLock;
use codex_utils_absolute_path::AbsolutePathBuf;
const BIN_DIRNAME: &str = "bin";
const CODE_MODE_HOST_PATH_ENV: &str = "CODEX_CODE_MODE_HOST_PATH";
const PACKAGE_METADATA_FILENAME: &str = "codex-package.json";
const PATH_DIRNAME: &str = "codex-path";
const RELEASES_DIRNAME: &str = "releases";
@@ -147,6 +150,37 @@ impl InstallContext {
default_rg_command()
}
/// Resolves the code-mode host executable for the current installation.
pub fn code_mode_host_program(&self) -> PathBuf {
self.resolve_code_mode_host_program(
std::env::var_os(CODE_MODE_HOST_PATH_ENV),
std::env::current_exe(),
)
}
fn resolve_code_mode_host_program(
&self,
override_path: Option<OsString>,
current_exe: io::Result<PathBuf>,
) -> PathBuf {
if let Some(path) = override_path {
return PathBuf::from(path);
}
let executable_name = code_mode_host_executable_name();
if let Some(path) = self.bundled_resource(executable_name) {
return path.into_path_buf();
}
if let Ok(current_exe) = current_exe
&& let Some(parent) = current_exe.parent()
{
return parent.join(executable_name);
}
PathBuf::from(executable_name)
}
pub fn bundled_resource(&self, file_name: impl AsRef<Path>) -> Option<AbsolutePathBuf> {
if let Some(package_layout) = &self.package_layout
&& let Some(resources_dir) = &package_layout.resources_dir
@@ -279,6 +313,14 @@ fn default_rg_command() -> PathBuf {
}
}
fn code_mode_host_executable_name() -> &'static str {
if cfg!(windows) {
"codex-code-mode-host.exe"
} else {
"codex-code-mode-host"
}
}
fn zsh_resource_path() -> PathBuf {
PathBuf::from(ZSH_DIRNAME).join(BIN_DIRNAME).join("zsh")
}
@@ -291,6 +333,74 @@ mod tests {
const TEST_RESOURCE_NAME: &str = "codex-test-helper";
#[test]
fn code_mode_host_program_prefers_override_then_bundled_resource() -> std::io::Result<()> {
let package_dir = tempfile::tempdir()?;
let bin_dir = package_dir.path().join(BIN_DIRNAME);
let resources_dir = package_dir.path().join(RESOURCES_DIRNAME);
fs::create_dir_all(&bin_dir)?;
fs::create_dir_all(&resources_dir)?;
fs::write(package_dir.path().join(PACKAGE_METADATA_FILENAME), "{}")?;
let exe_path = bin_dir.join(if cfg!(windows) { "codex.exe" } else { "codex" });
fs::write(&exe_path, "")?;
let host_path = resources_dir.join(code_mode_host_executable_name());
fs::write(&host_path, "")?;
let canonical_host_path = host_path.canonicalize()?;
let context = InstallContext::from_exe(
/*is_macos*/ false,
/*current_exe*/ Some(&exe_path),
/*method_override*/ None,
);
assert_eq!(
context.resolve_code_mode_host_program(
Some("custom-code-mode-host".into()),
Ok(exe_path.clone()),
),
PathBuf::from("custom-code-mode-host")
);
assert_eq!(
context.resolve_code_mode_host_program(/*override_path*/ None, Ok(exe_path),),
canonical_host_path
);
Ok(())
}
#[test]
fn code_mode_host_program_uses_adjacent_binary_for_legacy_layout() {
let context = InstallContext {
method: InstallMethod::Other,
package_layout: None,
};
assert_eq!(
context.resolve_code_mode_host_program(
/*override_path*/ None,
Ok(PathBuf::from("/opt/codex/bin/codex")),
),
PathBuf::from("/opt/codex/bin").join(code_mode_host_executable_name())
);
}
#[test]
fn code_mode_host_program_falls_back_to_executable_name() {
let context = InstallContext {
method: InstallMethod::Other,
package_layout: None,
};
assert_eq!(
context.resolve_code_mode_host_program(
/*override_path*/ None,
Err(io::Error::new(
io::ErrorKind::NotFound,
"missing executable"
)),
),
PathBuf::from(code_mode_host_executable_name())
);
}
#[test]
fn detects_standalone_install_from_release_layout() -> std::io::Result<()> {
let codex_home = tempfile::tempdir()?;
+3 -3
View File
@@ -10,10 +10,10 @@ The builder creates a canonical Codex package directory:
.
├── codex-package.json
├── bin
│ ├── <entrypoint>[.exe]
│ └── codex-code-mode-host[.exe]
│ └── <entrypoint>[.exe]
├── codex-resources
│ ├── bwrap # Linux only
│ ├── codex-code-mode-host[.exe]
│ ├── zsh/bin/zsh # supported Unix targets only
│ ├── codex-command-runner.exe # Windows only
│ └── codex-windows-sandbox-setup.exe # Windows only
@@ -53,7 +53,7 @@ entrypoint should pass `--entrypoint-bin` so the package contains that exact
binary instead of rebuilding it.
Release jobs should likewise pass `--code-mode-host-bin` so the package contains
the signed host executable beside the signed entrypoint.
the signed host executable in `codex-resources`.
Release jobs that already built package resource binaries should also pass the
corresponding resource flags: `--bwrap-bin` for Linux packages, and
+2 -2
View File
@@ -53,7 +53,7 @@ def build_package_dir(
)
copy_executable(
inputs.code_mode_host_bin,
bin_dir / f"codex-code-mode-host{spec.exe_suffix}",
resources_dir / f"codex-code-mode-host{spec.exe_suffix}",
is_windows=spec.is_windows,
)
copy_executable(inputs.rg_bin, path_dir / spec.rg_name, is_windows=spec.is_windows)
@@ -135,7 +135,7 @@ def validate_package_dir(
required_files = [
Path("bin") / variant.entrypoint_name(spec),
Path("bin") / f"codex-code-mode-host{spec.exe_suffix}",
Path("codex-resources") / f"codex-code-mode-host{spec.exe_suffix}",
Path("codex-path") / spec.rg_name,
]
executable_files = list(required_files)
+52 -27
View File
@@ -15,36 +15,61 @@ from codex_package.targets import TARGET_SPECS
class PackageLayoutTest(unittest.TestCase):
def test_app_server_package_places_code_mode_host_beside_entrypoint(self) -> None:
def test_app_server_package_places_code_mode_host_in_resources(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
package_dir = root / "package"
package_dir.mkdir()
inputs = PackageInputs(
entrypoint_bin=touch_executable(root / "codex-app-server"),
code_mode_host_bin=touch_executable(root / "codex-code-mode-host"),
rg_bin=touch_executable(root / "rg"),
zsh_bin=None,
bwrap_bin=touch_executable(root / "bwrap"),
codex_command_runner_bin=None,
codex_windows_sandbox_setup_bin=None,
)
for target in (
"x86_64-unknown-linux-musl",
"x86_64-pc-windows-msvc",
):
with self.subTest(target=target):
root = Path(temp_dir) / target
root.mkdir()
package_dir = root / "package"
package_dir.mkdir()
spec = TARGET_SPECS[target]
inputs = PackageInputs(
entrypoint_bin=touch_executable(root / "codex-app-server"),
code_mode_host_bin=touch_executable(
root / "codex-code-mode-host"
),
rg_bin=touch_executable(root / "rg"),
zsh_bin=None,
bwrap_bin=(
touch_executable(root / "bwrap") if spec.is_linux else None
),
codex_command_runner_bin=(
touch_executable(root / "codex-command-runner.exe")
if spec.is_windows
else None
),
codex_windows_sandbox_setup_bin=(
touch_executable(root / "codex-windows-sandbox-setup.exe")
if spec.is_windows
else None
),
)
build_package_dir(
package_dir,
"1.2.3",
PACKAGE_VARIANTS["codex-app-server"],
TARGET_SPECS["x86_64-unknown-linux-musl"],
inputs,
)
validate_package_dir(
package_dir,
PACKAGE_VARIANTS["codex-app-server"],
TARGET_SPECS["x86_64-unknown-linux-musl"],
include_zsh=False,
)
build_package_dir(
package_dir,
"1.2.3",
PACKAGE_VARIANTS["codex-app-server"],
spec,
inputs,
)
validate_package_dir(
package_dir,
PACKAGE_VARIANTS["codex-app-server"],
spec,
include_zsh=False,
)
self.assertTrue((package_dir / "bin" / "codex-code-mode-host").is_file())
self.assertTrue(
(
package_dir
/ "codex-resources"
/ f"codex-code-mode-host{spec.exe_suffix}"
).is_file()
)
def touch_executable(path: Path) -> Path:
+1 -1
View File
@@ -538,8 +538,8 @@ function Test-PackageContentsAreComplete {
$expectedFiles = @(
"codex-package.json",
"bin\codex.exe",
"bin\codex-code-mode-host.exe",
"codex-path\rg.exe",
"codex-resources\codex-code-mode-host.exe",
"codex-resources\codex-command-runner.exe",
"codex-resources\codex-windows-sandbox-setup.exe"
)
+2 -4
View File
@@ -771,12 +771,11 @@ install_package_release() {
tar -xzf "$archive_path" -C "$stage_release"
chmod 0755 \
"$stage_release/bin/codex" \
"$stage_release/bin/codex-code-mode-host" \
"$stage_release/codex-resources/codex-code-mode-host" \
"$stage_release/codex-path/rg"
if [ -f "$stage_release/codex-resources/bwrap" ]; then
chmod 0755 "$stage_release/codex-resources/bwrap"
fi
ln -sf "bin/codex" "$stage_release/codex"
if [ -e "$release_dir" ] || [ -L "$release_dir" ]; then
rm -rf "$release_dir"
@@ -825,8 +824,7 @@ release_dir_is_complete() {
package)
[ -f "$release_dir/codex-package.json" ] &&
[ -x "$release_dir/bin/codex" ] &&
[ -x "$release_dir/bin/codex-code-mode-host" ] &&
[ -x "$release_dir/codex" ] &&
[ -x "$release_dir/codex-resources/codex-code-mode-host" ] &&
[ -x "$release_dir/codex-path/rg" ] ||
return 1
;;
+2 -2
View File
@@ -280,9 +280,9 @@ def _validate_codex_package_layout(package_dir: Path, package_archive: Path) ->
package_binary = package_dir / "bin" / runtime_binary_name()
if not package_binary.is_file():
missing_entries.append(str(Path("bin") / runtime_binary_name()))
code_mode_host = package_dir / "bin" / runtime_code_mode_host_name()
code_mode_host = package_dir / "codex-resources" / runtime_code_mode_host_name()
if not code_mode_host.is_file():
missing_entries.append(str(Path("bin") / runtime_code_mode_host_name()))
missing_entries.append(str(Path("codex-resources") / runtime_code_mode_host_name()))
if missing_entries:
missing = ", ".join(missing_entries)
raise RuntimeError(f"Missing Codex package layout entries in {package_archive}: {missing}")
@@ -57,7 +57,9 @@ def _write_fake_codex_package(package_dir: Path, script) -> Path:
(package_dir / "codex-path").mkdir()
(package_dir / "codex-package.json").write_text('{"variant":"codex"}\n')
(package_dir / "bin" / script.runtime_binary_name()).write_text("fake codex\n")
(package_dir / "bin" / script.runtime_code_mode_host_name()).write_text("fake code mode host\n")
(package_dir / "codex-resources" / script.runtime_code_mode_host_name()).write_text(
"fake code mode host\n"
)
(package_dir / "codex-resources" / "bwrap").write_text("fake bwrap\n")
(package_dir / "codex-path" / "rg").write_text("fake rg\n")
return package_dir
@@ -683,7 +685,9 @@ def test_stage_runtime_release_copies_package_layout_and_sets_version(
assert {
"metadata": (package_root / "codex-package.json").read_text(),
"codex": (package_root / "bin" / script.runtime_binary_name()).read_text(),
"code_mode_host": (package_root / "bin" / script.runtime_code_mode_host_name()).read_text(),
"code_mode_host": (
package_root / "codex-resources" / script.runtime_code_mode_host_name()
).read_text(),
"bwrap": (package_root / "codex-resources" / "bwrap").read_text(),
"rg": (package_root / "codex-path" / "rg").read_text(),
} == {