build: package and validate code-mode host

This commit is contained in:
Channing Conger
2026-06-21 07:52:00 +00:00
parent a12f41d4e8
commit 60dad3fc7e
26 changed files with 457 additions and 78 deletions
+15 -6
View File
@@ -4,6 +4,10 @@ inputs:
target:
description: Rust target triple with Codex-built V8 release artifacts.
required: true
sandbox:
description: Use the V8 pointer-compression sandbox artifact profile.
required: false
default: "false"
runs:
using: composite
@@ -11,6 +15,7 @@ runs:
- name: Configure rusty_v8 artifact overrides and verify checksums
shell: bash
env:
SANDBOX: ${{ inputs.sandbox }}
TARGET: ${{ inputs.target }}
run: |
set -euo pipefail
@@ -19,14 +24,18 @@ runs:
release_tag="rusty-v8-v${version}"
base_url="https://github.com/openai/codex/releases/download/${release_tag}"
binding_dir="${RUNNER_TEMP}/rusty_v8"
archive_path="${binding_dir}/librusty_v8_release_${TARGET}.a.gz"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
checksums_path="${binding_dir}/rusty_v8_release_${TARGET}.sha256"
artifact_profile="release"
if [[ "${SANDBOX}" == "true" ]]; then
artifact_profile="ptrcomp_sandbox_release"
fi
archive_path="${binding_dir}/librusty_v8_${artifact_profile}_${TARGET}.a.gz"
binding_path="${binding_dir}/src_binding_${artifact_profile}_${TARGET}.rs"
checksums_path="${binding_dir}/rusty_v8_${artifact_profile}_${TARGET}.sha256"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/librusty_v8_release_${TARGET}.a.gz" -o "${archive_path}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
curl -fsSL "${base_url}/rusty_v8_release_${TARGET}.sha256" -o "${checksums_path}"
curl -fsSL "${base_url}/$(basename "${archive_path}")" -o "${archive_path}"
curl -fsSL "${base_url}/$(basename "${binding_path}")" -o "${binding_path}"
curl -fsSL "${base_url}/$(basename "${checksums_path}")" -o "${checksums_path}"
if [[ "$(wc -l < "${checksums_path}")" -ne 2 ]]; then
echo "Expected exactly two checksums for ${TARGET} in ${checksums_path}" >&2
@@ -8,6 +8,7 @@ Usage: build-codex-package-archive.sh \
--bundle <primary|app-server> \
--entrypoint-dir <dir> \
--archive-dir <dir> \
[--code-mode-host-bin <path>] \
[--bwrap-bin <path>] \
[--codex-command-runner-bin <path>] \
[--codex-windows-sandbox-setup-bin <path>] \
@@ -19,6 +20,7 @@ target=""
bundle=""
entrypoint_dir=""
archive_dir=""
code_mode_host_bin=""
target_suffixed_entrypoint="false"
resource_args=()
bwrap_bin_provided="false"
@@ -43,6 +45,10 @@ while [[ $# -gt 0 ]]; do
archive_dir="${2:?--archive-dir requires a value}"
shift 2
;;
--code-mode-host-bin)
code_mode_host_bin="${2:?--code-mode-host-bin requires a value}"
shift 2
;;
--bwrap-bin)
resource_args+=(--bwrap-bin "${2:?--bwrap-bin requires a value}")
bwrap_bin_provided="true"
@@ -110,8 +116,18 @@ case "$target" in
esac
entrypoint_name="$entrypoint"
code_mode_host_name="codex-code-mode-host"
if [[ "$target_suffixed_entrypoint" == "true" ]]; then
entrypoint_name="${entrypoint_name}-${target}"
code_mode_host_name="${code_mode_host_name}-${target}"
fi
if [[ -z "$code_mode_host_bin" ]]; then
code_mode_host_bin="${entrypoint_dir%/}/${code_mode_host_name}${exe_suffix}"
fi
if [[ ! -f "$code_mode_host_bin" ]]; then
echo "Code-mode host binary ${code_mode_host_bin} not found" >&2
exit 1
fi
case "$target" in
@@ -159,6 +175,7 @@ python_args=(
--target "$target"
--variant "$variant"
--entrypoint-bin "${entrypoint_dir%/}/${entrypoint_name}${exe_suffix}"
--code-mode-host-bin "$code_mode_host_bin"
--cargo-profile release
--package-dir "$package_dir"
--archive-output "$gzip_archive_path"
@@ -104,6 +104,13 @@ jobs:
with:
target: ${{ inputs.target }}
- name: Configure rusty_v8 artifact overrides and verify checksums
if: ${{ runner.os != 'Windows' }}
uses: ./.github/actions/setup-rusty-v8
with:
sandbox: "true"
target: ${{ inputs.target }}
- name: Compute lockfile hash
id: lockhash
shell: bash
@@ -199,13 +206,23 @@ jobs:
--archive-file "${archive_dir}/${NEXTEST_ARCHIVE_FILE}"
- name: Build runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
shell: bash
run: |
set -euo pipefail
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
mkdir -p "${helper_dir}"
host_name="codex-code-mode-host"
if [[ "${RUNNER_OS}" == "Windows" ]]; then
host_name="${host_name}.exe"
fi
cargo build \
--target ${{ inputs.target }} \
--profile ${{ inputs.profile }} \
-p codex-code-mode-host \
--bin codex-code-mode-host
cp "target/${{ inputs.target }}/${{ inputs.profile }}/${host_name}" "${helper_dir}/"
if [[ "${RUNNER_OS}" == "Linux" ]]; then
cargo build \
--target ${{ inputs.target }} \
@@ -213,7 +230,7 @@ jobs:
-p codex-linux-sandbox \
--bin codex-linux-sandbox
cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-linux-sandbox" "${helper_dir}/"
else
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
cargo build \
--target ${{ inputs.target }} \
--profile ${{ inputs.profile }} \
@@ -241,7 +258,6 @@ jobs:
retention-days: 1
- name: Upload runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.TEST_HELPERS_ARTIFACT }}
@@ -355,7 +371,6 @@ jobs:
path: ${{ runner.temp }}/nextest-archive
- name: Download runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ env.TEST_HELPERS_ARTIFACT }}
@@ -374,18 +389,43 @@ jobs:
workspace_root="$(cygpath -w "${workspace_root}")"
fi
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}"
mkdir -p "${helper_target_dir}"
host_name="codex-code-mode-host"
if [[ "${RUNNER_OS}" == "Windows" ]]; then
host_name="${host_name}.exe"
fi
cp "${helper_dir}/${host_name}" "${helper_target_dir}/"
host_path="${helper_target_dir}/${host_name}"
if [[ "${RUNNER_OS}" == "Windows" ]]; then
host_path="$(cygpath -w "${host_path}")"
else
chmod +x "${host_path}"
fi
test_env=(
"CARGO_BIN_EXE_codex-code-mode-host=${host_path}"
"CARGO_BIN_EXE_codex_code_mode_host=${host_path}"
)
if [[ "${RUNNER_OS}" == "Linux" ]]; then
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}"
mkdir -p "${helper_target_dir}"
cp "${helper_dir}/codex-linux-sandbox" "${helper_target_dir}/"
chmod +x "${helper_target_dir}/codex-linux-sandbox"
sandbox_helper="${helper_target_dir}/codex-linux-sandbox"
test_env+=(
"CARGO_BIN_EXE_codex-linux-sandbox=${sandbox_helper}"
"CARGO_BIN_EXE_codex_linux_sandbox=${sandbox_helper}"
)
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}"
mkdir -p "${helper_target_dir}"
cp "${helper_dir}/codex-windows-sandbox-setup.exe" "${helper_target_dir}/"
cp "${helper_dir}/codex-command-runner.exe" "${helper_target_dir}/"
setup_helper="$(cygpath -w "${helper_target_dir}/codex-windows-sandbox-setup.exe")"
command_runner="$(cygpath -w "${helper_target_dir}/codex-command-runner.exe")"
test_env+=(
"CARGO_BIN_EXE_codex_windows_sandbox_setup=${setup_helper}"
"CARGO_BIN_EXE_codex_command_runner=${command_runner}"
)
fi
nextest_args=(
@@ -399,27 +439,7 @@ jobs:
nextest_args+=(--test-threads "${{ inputs.test_threads }}")
fi
test_command=(cargo nextest "${nextest_args[@]}")
if [[ "${RUNNER_OS}" == "Linux" ]]; then
sandbox_helper="${helper_target_dir}/codex-linux-sandbox"
test_command=(
env
"CARGO_BIN_EXE_codex-linux-sandbox=${sandbox_helper}"
"CARGO_BIN_EXE_codex_linux_sandbox=${sandbox_helper}"
cargo nextest "${nextest_args[@]}"
)
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
setup_helper="$(cygpath -w "${helper_target_dir}/codex-windows-sandbox-setup.exe")"
command_runner="$(cygpath -w "${helper_target_dir}/codex-command-runner.exe")"
test_command=(
env
"CARGO_BIN_EXE_codex_windows_sandbox_setup=${setup_helper}"
"CARGO_BIN_EXE_codex_command_runner=${command_runner}"
cargo nextest "${nextest_args[@]}"
)
fi
"${test_command[@]}"
env "${test_env[@]}" cargo nextest "${nextest_args[@]}"
env:
RUST_BACKTRACE: 1
RUST_MIN_STACK: "8388608" # 8 MiB
+1
View File
@@ -382,6 +382,7 @@ jobs:
name: Configure rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8
with:
sandbox: "true"
target: ${{ matrix.target }}
- name: Install cargo-chef
+10 -5
View File
@@ -42,14 +42,14 @@ jobs:
- runner: windows-x64
target: x86_64-pc-windows-msvc
bundle: helpers
binaries: "codex-windows-sandbox-setup codex-command-runner"
binaries: "codex-windows-sandbox-setup codex-command-runner codex-code-mode-host"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- runner: windows-arm64
target: aarch64-pc-windows-msvc
bundle: helpers
binaries: "codex-windows-sandbox-setup codex-command-runner"
binaries: "codex-windows-sandbox-setup codex-command-runner codex-code-mode-host"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-arm64
@@ -201,6 +201,9 @@ jobs:
defaults:
run:
working-directory: codex-rs
env:
WINDOWS_BINARIES: "codex codex-responses-api-proxy codex-windows-sandbox-setup codex-command-runner codex-code-mode-host codex-app-server"
strategy:
fail-fast: false
matrix:
@@ -382,16 +385,18 @@ jobs:
bundle_dir="$(mktemp -d)"
runner_src="$dest/codex-command-runner-${target}.exe"
setup_src="$dest/codex-windows-sandbox-setup-${target}.exe"
if [[ -f "$runner_src" && -f "$setup_src" ]]; then
code_mode_host_src="$dest/codex-code-mode-host-${target}.exe"
if [[ -f "$runner_src" && -f "$setup_src" && -f "$code_mode_host_src" ]]; then
cp "$dest/$base" "$bundle_dir/$base"
cp "$runner_src" "$bundle_dir/codex-command-runner.exe"
cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe"
cp "$code_mode_host_src" "$bundle_dir/codex-code-mode-host.exe"
# Use an absolute path so bundle zips land in the real dist
# dir even when 7z runs from a temp directory.
(cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .)
else
echo "warning: missing sandbox binaries; falling back to single-binary zip"
echo "warning: expected $runner_src and $setup_src"
echo "warning: missing bundled binaries; falling back to single-binary zip"
echo "warning: expected $runner_src, $setup_src, and $code_mode_host_src"
(cd "$dest" && 7z a "${base}.zip" "$base")
fi
rm -rf "$bundle_dir"
+30 -22
View File
@@ -80,50 +80,50 @@ jobs:
target: aarch64-apple-darwin
bundle: primary
artifact_name: aarch64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
build_dmg: "true"
- runner: macos-15-xlarge
target: aarch64-apple-darwin
bundle: app-server
artifact_name: aarch64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
- runner: macos-15-xlarge
target: x86_64-apple-darwin
bundle: primary
artifact_name: x86_64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
build_dmg: "true"
- runner: macos-15-xlarge
target: x86_64-apple-darwin
bundle: app-server
artifact_name: x86_64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
# Release artifacts intentionally ship MUSL-linked Linux binaries.
- runner: ${{ github.event.repository.name }}-linux-x64-xl
target: x86_64-unknown-linux-musl
bundle: primary
artifact_name: x86_64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy bwrap"
binaries: "codex codex-responses-api-proxy codex-code-mode-host bwrap"
build_dmg: "false"
- runner: ${{ github.event.repository.name }}-linux-x64-xl
target: x86_64-unknown-linux-musl
bundle: app-server
artifact_name: x86_64-unknown-linux-musl-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
- runner: ${{ github.event.repository.name }}-linux-arm64
target: aarch64-unknown-linux-musl
bundle: primary
artifact_name: aarch64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy bwrap"
binaries: "codex codex-responses-api-proxy codex-code-mode-host bwrap"
build_dmg: "false"
- runner: ${{ github.event.repository.name }}-linux-arm64
target: aarch64-unknown-linux-musl
bundle: app-server
artifact_name: aarch64-unknown-linux-musl-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
steps:
@@ -208,6 +208,7 @@ jobs:
- name: Configure rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8
with:
sandbox: "true"
target: ${{ matrix.target }}
- if: ${{ contains(matrix.target, 'linux') }}
@@ -344,9 +345,15 @@ 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/bwrap-${{ matrix.target }}" "$bundle_root/codex-resources/bwrap"
chmod 0755 "$bundle_root/codex" "$bundle_root/codex-resources/bwrap"
tar -C "$bundle_root" -cf - codex codex-resources/bwrap |
chmod 0755 \
"$bundle_root/codex" \
"$bundle_root/codex-code-mode-host" \
"$bundle_root/codex-resources/bwrap"
tar -C "$bundle_root" -cf - \
codex codex-code-mode-host codex-resources/bwrap |
zstd -T0 -19 -o "$dest/codex-${{ matrix.target }}-bundle.tar.zst"
fi
@@ -484,19 +491,19 @@ jobs:
- target: aarch64-apple-darwin
bundle: primary
artifact_name: aarch64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
- target: aarch64-apple-darwin
bundle: app-server
artifact_name: aarch64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
- target: x86_64-apple-darwin
bundle: primary
artifact_name: x86_64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
- target: x86_64-apple-darwin
bundle: app-server
artifact_name: x86_64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -602,22 +609,22 @@ jobs:
- target: aarch64-apple-darwin
bundle: primary
artifact_name: aarch64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
build_dmg: "true"
- target: aarch64-apple-darwin
bundle: app-server
artifact_name: aarch64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
- target: x86_64-apple-darwin
bundle: primary
artifact_name: x86_64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
build_dmg: "true"
- target: x86_64-apple-darwin
bundle: app-server
artifact_name: x86_64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
build_dmg: "false"
steps:
@@ -889,22 +896,22 @@ jobs:
- target: aarch64-apple-darwin
bundle: primary
artifact_name: aarch64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
verify_dmg: "true"
- target: aarch64-apple-darwin
bundle: app-server
artifact_name: aarch64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
verify_dmg: "false"
- target: x86_64-apple-darwin
bundle: primary
artifact_name: x86_64-apple-darwin
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy codex-code-mode-host"
verify_dmg: "true"
- target: x86_64-apple-darwin
bundle: app-server
artifact_name: x86_64-apple-darwin-app-server
binaries: "codex-app-server"
binaries: "codex-app-server codex-code-mode-host"
verify_dmg: "false"
steps:
@@ -992,6 +999,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}"
verify_signed_binary "${package_dir}/bin/codex-code-mode-host"
if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then
exit 0
+2
View File
@@ -71,6 +71,8 @@ alias(
exports_files([
"AGENTS.md",
"runfile_env_binary_launcher.bat.tpl",
"runfile_env_binary_launcher.sh.tpl",
"workspace_root_test_launcher.bat.tpl",
"workspace_root_test_launcher.sh.tpl",
])
+1
View File
@@ -2531,6 +2531,7 @@ dependencies = [
"serde_json",
"tokio",
"tokio-util",
"v8",
]
[[package]]
+9 -1
View File
@@ -1,4 +1,4 @@
load("//:defs.bzl", "MACOS_WEBRTC_RUSTC_LINK_FLAGS", "codex_rust_crate")
load("//:defs.bzl", "MACOS_WEBRTC_RUSTC_LINK_FLAGS", "codex_rust_crate", "runfile_env_binary")
load("//bazel/platforms:release_binaries.bzl", "multiplatform_binaries")
codex_rust_crate(
@@ -13,3 +13,11 @@ codex_rust_crate(
multiplatform_binaries(
name = "codex",
)
runfile_env_binary(
name = "codex-with-code-mode",
binary = ":codex",
runfile_env = {
"//codex-rs/code-mode-host:codex-code-mode-host": "CODEX_CODE_MODE_HOST_PATH",
},
)
+1
View File
@@ -3,4 +3,5 @@ load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "code-mode-host",
crate_name = "codex_code_mode_host",
crate_srcs = [],
)
+8
View File
@@ -17,9 +17,17 @@ codex-code-mode-protocol = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["io-std", "io-util", "macros", "process", "rt-multi-thread", "sync", "time"] }
tokio-util = { workspace = true, features = ["rt"] }
# The host owns V8 process configuration, so select the sandboxed V8 build at
# the executable boundary without adding a workspace-crate feature gate.
v8 = { workspace = true, features = ["v8_enable_sandbox"] }
[dev-dependencies]
codex-code-mode-client = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
pretty_assertions = { workspace = true }
[package.metadata.cargo-shear]
# This direct dependency intentionally selects the V8 sandbox feature for the
# host without activating an internal workspace-crate feature.
ignored = ["v8"]
+3 -2
View File
@@ -64,9 +64,10 @@ mod tests {
assert!(!super::embedded_v8_version().is_empty());
}
#[cfg(feature = "sandbox")]
#[test]
fn sandbox_feature_matches_linked_v8() {
assert_eq!(super::linked_v8_has_sandbox(), cfg!(feature = "sandbox"));
fn sandbox_feature_enables_linked_v8_sandbox() {
assert!(super::linked_v8_has_sandbox());
}
#[test]
+61 -1
View File
@@ -138,6 +138,66 @@ def _windows_workspace_root_setup(ctx):
return """set "INSTA_WORKSPACE_ROOT=%workspace_root%"
cd /d "%workspace_root%" || exit /b 1"""
def _runfile_env_binary_impl(ctx):
is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo])
launcher = ctx.actions.declare_file(ctx.label.name + ".bat" if is_windows else ctx.label.name)
binary = ctx.executable.binary
launcher_template = ctx.file._windows_launcher_template if is_windows else ctx.file._bash_launcher_template
runfile_env_exports = _windows_runfile_env_exports(ctx) if is_windows else _bash_runfile_env_exports(ctx)
ctx.actions.expand_template(
template = launcher_template,
output = launcher,
is_executable = True,
substitutions = {
"__BINARY__": binary.short_path,
"__RUNFILE_ENV_EXPORTS__": runfile_env_exports,
"__WORKSPACE_NAME__": ctx.workspace_name,
},
)
runfiles = ctx.runfiles(files = [binary]).merge(ctx.attr.binary[DefaultInfo].default_runfiles)
for runfile_dep in ctx.attr.runfile_env:
executable = runfile_dep[DefaultInfo].files_to_run.executable
if executable == None:
fail("{} does not provide an executable for runfile_env".format(runfile_dep.label))
runfiles = runfiles.merge(ctx.runfiles(files = [executable]))
runfiles = runfiles.merge(runfile_dep[DefaultInfo].default_runfiles)
return [DefaultInfo(
executable = launcher,
files = depset([launcher]),
runfiles = runfiles,
)]
runfile_env_binary = rule(
implementation = _runfile_env_binary_impl,
executable = True,
toolchains = ["@bazel_tools//tools/test:default_test_toolchain_type"],
attrs = {
"binary": attr.label(
cfg = "target",
executable = True,
mandatory = True,
),
"runfile_env": attr.label_keyed_string_dict(
cfg = "target",
),
"_windows_constraint": attr.label(
default = "@platforms//os:windows",
providers = [platform_common.ConstraintValueInfo],
),
"_bash_launcher_template": attr.label(
allow_single_file = True,
default = "//:runfile_env_binary_launcher.sh.tpl",
),
"_windows_launcher_template": attr.label(
allow_single_file = True,
default = "//:runfile_env_binary_launcher.bat.tpl",
),
},
doc = "Runs a binary with environment variables resolved from executable runfiles.",
)
workspace_root_test = rule(
implementation = _workspace_root_test_impl,
test = True,
@@ -285,7 +345,7 @@ def codex_rust_crate(
binaries = DEP_DATA.get(native.package_name())["binaries"]
lib_srcs = crate_srcs or native.glob(["src/**/*.rs"], exclude = binaries.values(), allow_empty = True)
lib_srcs = native.glob(["src/**/*.rs"], exclude = binaries.values(), allow_empty = True) if crate_srcs == None else crate_srcs
maybe_deps = []
+6 -4
View File
@@ -75,11 +75,11 @@ install:
# there should be no need to add `--all-features`.
[unix]
test *args:
RUST_MIN_STACK={{ rust_min_stack }} NEXTEST_PROFILE=local cargo nextest run --no-fail-fast "$@"
RUST_MIN_STACK={{ rust_min_stack }} NEXTEST_PROFILE=local {{ python }} ../scripts/run-nextest.py "$@"
[windows]
test *args:
$env:RUST_MIN_STACK = "{{ rust_min_stack }}"; $env:NEXTEST_PROFILE = "local"; cargo nextest run --no-fail-fast @($args | Select-Object -Skip 1)
$env:RUST_MIN_STACK = "{{ rust_min_stack }}"; $env:NEXTEST_PROFILE = "local"; {{ python }} ../scripts/run-nextest.py @($args | Select-Object -Skip 1)
# Run from the repository root so scripts that resolve paths from `cwd` see
# the same layout they use in GitHub Actions.
@@ -96,16 +96,18 @@ bench-smoke:
just bench -- --test
# Build and run Codex from source using Bazel.
# The launcher target also builds the code-mode host and injects its runfile
# path, so Bazel can rebuild either binary incrementally.
# On Unix, use `[no-cd]` and `--run_under="cd $PWD &&"` to ensure Bazel runs
# the command in the current working directory.
[no-cd]
[unix]
bazel-codex *args:
bazel run //codex-rs/cli:codex --run_under="cd $PWD &&" -- "$@"
bazel run //codex-rs/cli:codex-with-code-mode --run_under="cd $PWD &&" -- "$@"
[windows]
bazel-codex *args:
bazel run //codex-rs/cli:codex --run_under='cd /d "{{ invocation_directory_native() }}" &&' -- @($args | Select-Object -Skip 1)
bazel run //codex-rs/cli:codex-with-code-mode --run_under='cd /d "{{ invocation_directory_native() }}" &&' -- @($args | Select-Object -Skip 1)
[no-cd]
bazel-lock-update:
+51
View File
@@ -0,0 +1,51 @@
@echo off
setlocal EnableExtensions EnableDelayedExpansion
call :resolve_runfile binary "__BINARY__"
if errorlevel 1 exit /b 1
__RUNFILE_ENV_EXPORTS__
"%binary%" %*
exit /b %ERRORLEVEL%
:resolve_runfile
setlocal EnableExtensions EnableDelayedExpansion
set "logical_path=%~2"
set "native_logical_path=%logical_path:/=\%"
set "workspace_name=__WORKSPACE_NAME__"
for %%R in ("%RUNFILES_DIR%" "%TEST_SRCDIR%" "%~f0.runfiles") do (
set "runfiles_root=%%~R"
if defined runfiles_root (
for %%P in ("!native_logical_path!" "%TEST_WORKSPACE%\!native_logical_path!" "!workspace_name!\!native_logical_path!" "_main\!native_logical_path!") do (
if exist "!runfiles_root!\%%~P" (
endlocal & set "%~1=!runfiles_root!\%%~P" & exit /b 0
)
)
)
)
set "manifest=%RUNFILES_MANIFEST_FILE%"
if not defined manifest if exist "%~f0.runfiles_manifest" set "manifest=%~f0.runfiles_manifest"
if not defined manifest if exist "%~f0.exe.runfiles_manifest" set "manifest=%~f0.exe.runfiles_manifest"
if defined manifest if exist "%manifest%" (
for /f "usebackq tokens=1,* delims= " %%A in ("%manifest%") do (
if "%%A"=="%logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
if "%%A"=="%TEST_WORKSPACE%/%logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
if "%%A"=="%workspace_name%/%logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
if "%%A"=="_main/%logical_path%" (
endlocal & set "%~1=%%B" & exit /b 0
)
)
)
>&2 echo failed to resolve runfile: %logical_path%
endlocal & exit /b 1
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -euo pipefail
resolve_runfile() {
local logical_path="$1"
local launcher_runfiles="$0.runfiles"
local workspace_name="__WORKSPACE_NAME__"
local runfiles_root
for runfiles_root in "${RUNFILES_DIR:-}" "${TEST_SRCDIR:-}" "${launcher_runfiles}"; do
if [[ -z "${runfiles_root}" ]]; then
continue
fi
for candidate in \
"${runfiles_root}/${logical_path}" \
"${runfiles_root}/${TEST_WORKSPACE:-}/${logical_path}" \
"${runfiles_root}/${workspace_name}/${logical_path}" \
"${runfiles_root}/_main/${logical_path}"; do
if [[ -e "${candidate}" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
done
done
local manifest="${RUNFILES_MANIFEST_FILE:-}"
if [[ -z "${manifest}" ]]; then
if [[ -f "$0.runfiles_manifest" ]]; then
manifest="$0.runfiles_manifest"
elif [[ -f "$0.exe.runfiles_manifest" ]]; then
manifest="$0.exe.runfiles_manifest"
fi
fi
if [[ -n "${manifest}" && -f "${manifest}" ]]; then
local resolved
resolved="$(awk -v path="${logical_path}" '$1 == path || substr($1, length($1) - length(path), length(path) + 1) == "/" path { $1 = ""; sub(/^ /, ""); print; exit }' "${manifest}")"
if [[ -n "${resolved}" ]]; then
printf '%s\n' "${resolved}"
return 0
fi
fi
echo "failed to resolve runfile: ${logical_path}" >&2
return 1
}
binary="$(resolve_runfile "__BINARY__")"
RUNFILE_ENV_ARGS=()
__RUNFILE_ENV_EXPORTS__
if (( ${#RUNFILE_ENV_ARGS[@]} > 0 )); then
exec env "${RUNFILE_ENV_ARGS[@]}" "${binary}" "$@"
else
exec "${binary}" "$@"
fi
+3 -1
View File
@@ -10,7 +10,8 @@ The builder creates a canonical Codex package directory:
.
├── codex-package.json
├── bin
│ └── <entrypoint>[.exe]
│ ├── <entrypoint>[.exe]
│ └── codex-code-mode-host[.exe]
├── codex-resources
│ ├── bwrap # Linux only
│ ├── zsh/bin/zsh # supported Unix targets only
@@ -40,6 +41,7 @@ grouped `cargo build` command per package when they are needed and no prebuilt
override was provided:
- all targets: the selected entrypoint, unless `--entrypoint-bin` is provided
- all targets: `codex-code-mode-host`, unless `--code-mode-host-bin` is provided
- Linux targets: `bwrap`, unless `--bwrap-bin` is provided
- Windows targets: `codex-command-runner` and `codex-windows-sandbox-setup`,
unless the corresponding prebuilt helper flags are provided
+12 -1
View File
@@ -17,6 +17,7 @@ CODEX_RS_ROOT = REPO_ROOT / "codex-rs"
@dataclass(frozen=True)
class SourceBuildOutputs:
entrypoint_bin: Path
code_mode_host_bin: Path
bwrap_bin: Path | None
codex_command_runner_bin: Path | None
codex_windows_sandbox_setup_bin: Path | None
@@ -29,6 +30,7 @@ def build_source_binaries(
cargo: str,
profile: str,
entrypoint_bin: Path | None,
code_mode_host_bin: Path | None,
bwrap_bin: Path | None,
codex_command_runner_bin: Path | None,
codex_windows_sandbox_setup_bin: Path | None,
@@ -43,6 +45,7 @@ def build_source_binaries(
spec,
variant,
build_entrypoint=entrypoint_bin is None,
build_code_mode_host=code_mode_host_bin is None,
build_bwrap=spec.is_linux and bwrap_bin is None,
build_codex_command_runner=spec.is_windows and codex_command_runner_bin is None,
build_codex_windows_sandbox_setup=spec.is_windows
@@ -61,7 +64,7 @@ def build_source_binaries(
cmd.extend(["--bin", binary])
cargo_env = None
if entrypoint_bin is None:
if entrypoint_bin is None or code_mode_host_bin is None:
codex_v8_env = resolve_codex_v8_cargo_env(spec)
if codex_v8_env:
cargo_env = {**os.environ, **codex_v8_env}
@@ -80,6 +83,10 @@ def build_source_binaries(
entrypoint_bin,
output_dir / variant.entrypoint_name(spec),
),
code_mode_host_bin=resolve_output_path(
code_mode_host_bin,
output_dir / f"codex-code-mode-host{spec.exe_suffix}",
),
bwrap_bin=resolve_output_path(
bwrap_bin,
output_dir / "bwrap" if spec.is_linux else None,
@@ -102,6 +109,7 @@ def source_binaries_for_target(
variant: PackageVariant,
*,
build_entrypoint: bool,
build_code_mode_host: bool,
build_bwrap: bool,
build_codex_command_runner: bool,
build_codex_windows_sandbox_setup: bool,
@@ -109,6 +117,8 @@ def source_binaries_for_target(
binaries = []
if build_entrypoint:
binaries.append(variant.cargo_bin)
if build_code_mode_host:
binaries.append("codex-code-mode-host")
if build_bwrap:
binaries.append("bwrap")
if build_codex_command_runner:
@@ -174,6 +184,7 @@ def cargo_profile_dirname(profile: str) -> str:
def validate_source_outputs(outputs: SourceBuildOutputs) -> None:
for path in [
outputs.entrypoint_bin,
outputs.code_mode_host_bin,
outputs.bwrap_bin,
outputs.codex_command_runner_bin,
outputs.codex_windows_sandbox_setup_bin,
+14
View File
@@ -82,6 +82,14 @@ def parse_args() -> argparse.Namespace:
"variant. If omitted, the entrypoint is built with Cargo."
),
)
parser.add_argument(
"--code-mode-host-bin",
type=Path,
help=(
"Optional prebuilt codex-code-mode-host executable. If omitted, "
"codex-code-mode-host is built with Cargo."
),
)
parser.add_argument(
"--bwrap-bin",
type=Path,
@@ -140,6 +148,11 @@ def main() -> int:
"prebuilt entrypoint executable",
"--entrypoint-bin",
),
code_mode_host_bin=resolve_optional_input_path(
args.code_mode_host_bin,
"prebuilt code-mode host executable",
"--code-mode-host-bin",
),
bwrap_bin=resolve_optional_input_path(
args.bwrap_bin,
"prebuilt Linux bwrap executable",
@@ -159,6 +172,7 @@ def main() -> int:
version = read_workspace_version()
inputs = PackageInputs(
entrypoint_bin=source_outputs.entrypoint_bin,
code_mode_host_bin=source_outputs.code_mode_host_bin,
rg_bin=resolve_rg_bin(spec, args.rg_bin),
zsh_bin=resolve_zsh_bin(spec),
bwrap_bin=source_outputs.bwrap_bin,
+8
View File
@@ -51,6 +51,11 @@ def build_package_dir(
bin_dir / entrypoint_name,
is_windows=spec.is_windows,
)
copy_executable(
inputs.code_mode_host_bin,
bin_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)
if inputs.zsh_bin is not None:
@@ -83,6 +88,7 @@ def build_package_dir(
"target": spec.target,
"variant": variant.name,
"entrypoint": f"bin/{entrypoint_name}",
"codeModeHost": f"bin/codex-code-mode-host{spec.exe_suffix}",
"resourcesDir": "codex-resources",
"pathDir": "codex-path",
}
@@ -118,6 +124,7 @@ def validate_package_dir(
"target": spec.target,
"variant": variant.name,
"entrypoint": f"bin/{variant.entrypoint_name(spec)}",
"codeModeHost": f"bin/codex-code-mode-host{spec.exe_suffix}",
"resourcesDir": "codex-resources",
"pathDir": "codex-path",
}
@@ -130,6 +137,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-path") / spec.rg_name,
]
executable_files = list(required_files)
+1
View File
@@ -39,6 +39,7 @@ class PackageVariant:
@dataclass(frozen=True)
class PackageInputs:
entrypoint_bin: Path
code_mode_host_bin: Path
rg_bin: Path
zsh_bin: Path | None
bwrap_bin: Path | None
+21
View File
@@ -20,6 +20,7 @@ class SourceBinariesForTargetTest(unittest.TestCase):
TARGET_SPECS["aarch64-apple-darwin"],
PACKAGE_VARIANTS["codex"],
build_entrypoint=False,
build_code_mode_host=False,
build_bwrap=False,
build_codex_command_runner=False,
build_codex_windows_sandbox_setup=False,
@@ -35,6 +36,7 @@ class SourceBinariesForTargetTest(unittest.TestCase):
TARGET_SPECS["x86_64-unknown-linux-musl"],
PACKAGE_VARIANTS["codex"],
build_entrypoint=False,
build_code_mode_host=False,
build_bwrap=False,
build_codex_command_runner=False,
build_codex_windows_sandbox_setup=False,
@@ -50,6 +52,7 @@ class SourceBinariesForTargetTest(unittest.TestCase):
TARGET_SPECS["x86_64-pc-windows-msvc"],
PACKAGE_VARIANTS["codex"],
build_entrypoint=False,
build_code_mode_host=False,
build_bwrap=False,
build_codex_command_runner=False,
build_codex_windows_sandbox_setup=False,
@@ -63,6 +66,7 @@ class SourceBinariesForTargetTest(unittest.TestCase):
TARGET_SPECS["x86_64-pc-windows-msvc"],
PACKAGE_VARIANTS["codex"],
build_entrypoint=False,
build_code_mode_host=False,
build_bwrap=False,
build_codex_command_runner=True,
build_codex_windows_sandbox_setup=True,
@@ -70,10 +74,25 @@ class SourceBinariesForTargetTest(unittest.TestCase):
["codex-command-runner", "codex-windows-sandbox-setup"],
)
def test_missing_code_mode_host_is_built(self) -> None:
self.assertEqual(
source_binaries_for_target(
TARGET_SPECS["x86_64-unknown-linux-musl"],
PACKAGE_VARIANTS["codex"],
build_entrypoint=False,
build_code_mode_host=True,
build_bwrap=False,
build_codex_command_runner=False,
build_codex_windows_sandbox_setup=False,
),
["codex-code-mode-host"],
)
def test_build_uses_prebuilt_windows_helpers_without_running_cargo(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
entrypoint = touch_file(root / "codex.exe")
code_mode_host = touch_file(root / "codex-code-mode-host.exe")
command_runner = touch_file(root / "codex-command-runner.exe")
sandbox_setup = touch_file(root / "codex-windows-sandbox-setup.exe")
@@ -83,12 +102,14 @@ class SourceBinariesForTargetTest(unittest.TestCase):
cargo=str(root / "cargo-that-should-not-run"),
profile="release",
entrypoint_bin=entrypoint,
code_mode_host_bin=code_mode_host,
bwrap_bin=None,
codex_command_runner_bin=command_runner,
codex_windows_sandbox_setup_bin=sandbox_setup,
)
self.assertEqual(outputs.entrypoint_bin, entrypoint)
self.assertEqual(outputs.code_mode_host_bin, code_mode_host)
self.assertEqual(outputs.codex_command_runner_bin, command_runner)
self.assertEqual(outputs.codex_windows_sandbox_setup_bin, sandbox_setup)
+4 -3
View File
@@ -16,6 +16,7 @@ from .targets import TargetSpec
DOWNLOAD_TIMEOUT_SECS = 120
ARTIFACT_PROFILE = "ptrcomp_sandbox_release"
@dataclass(frozen=True)
@@ -70,9 +71,9 @@ def fetch_codex_v8_artifacts(
)
target = spec.target
cache_dir = (cache_root or default_cache_root()) / f"rusty-v8-{version}-{target}"
archive = cache_dir / f"librusty_v8_release_{target}.a.gz"
binding = cache_dir / f"src_binding_release_{target}.rs"
checksums = cache_dir / f"rusty_v8_release_{target}.sha256"
archive = cache_dir / f"librusty_v8_{ARTIFACT_PROFILE}_{target}.a.gz"
binding = cache_dir / f"src_binding_{ARTIFACT_PROFILE}_{target}.rs"
checksums = cache_dir / f"rusty_v8_{ARTIFACT_PROFILE}_{target}.sha256"
download_file(f"{release_url}/{checksums.name}", checksums)
expected_checksums = load_checksums(checksums, {archive.name, binding.name})
+1
View File
@@ -537,6 +537,7 @@ function Test-PackageContentsAreComplete {
$expectedFiles = @(
"codex-package.json",
"bin\codex.exe",
"bin\codex-code-mode-host.exe",
"codex-path\rg.exe",
"codex-resources\codex-command-runner.exe",
"codex-resources\codex-windows-sandbox-setup.exe"
+5 -1
View File
@@ -677,7 +677,10 @@ install_package_release() {
rm -rf "$stage_release"
mkdir -p "$stage_release"
tar -xzf "$archive_path" -C "$stage_release"
chmod 0755 "$stage_release/bin/codex" "$stage_release/codex-path/rg"
chmod 0755 \
"$stage_release/bin/codex" \
"$stage_release/bin/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
@@ -730,6 +733,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-path/rg" ] ||
return 1
+65
View File
@@ -0,0 +1,65 @@
"""Build runtime helpers and run the Codex nextest suite."""
import os
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from codex_package.targets import REPO_ROOT
from codex_package.targets import TARGET_SPECS
from codex_package.v8 import resolve_codex_v8_cargo_env
CODEX_RS_ROOT = REPO_ROOT / "codex-rs"
def rustc_host_target() -> str:
rustc = os.environ.get("RUSTC", "rustc")
version = subprocess.run(
[rustc, "-vV"],
check=True,
capture_output=True,
text=True,
).stdout
for line in version.splitlines():
if line.startswith("host: "):
return line.removeprefix("host: ")
raise RuntimeError("rustc -vV did not report a host target")
def main() -> int:
target = rustc_host_target()
try:
spec = TARGET_SPECS[target]
except KeyError as exc:
raise RuntimeError(
f"unsupported host target for code-mode tests: {target}"
) from exc
env = {**os.environ, **resolve_codex_v8_cargo_env(spec)}
cargo = os.environ.get("CARGO", "cargo")
subprocess.run(
[
cargo,
"build",
"-p",
"codex-code-mode-host",
"--bin",
"codex-code-mode-host",
],
cwd=CODEX_RS_ROOT,
check=True,
env=env,
)
return subprocess.run(
[cargo, "nextest", "run", "--no-fail-fast", *sys.argv[1:]],
cwd=CODEX_RS_ROOT,
env=env,
).returncode
if __name__ == "__main__":
raise SystemExit(main())