Test Python SDK against the built CLI and installed runtime (#44053)

## Why

Python SDK CI needs to exercise the checkout's CLI while retaining coverage of the wheel installation and its default runtime.

## What changed

- Run the Python SDK test suite against the same Bazel-built CLI as the TypeScript SDK tests. Build and stage `codex-code-mode-host` alongside the CLI so it can be discovered.
- Make the Python test harness prefer `CODEX_EXEC_PATH`, then a local debug build, then the installed runtime. Exclude optional turn-cost probes from recorded model requests.
- Add a separate installation job that builds the Python SDK wheel and installs it in a fresh environment.

## Testing

The installation smoke test verifies that imports resolve to the installed SDK and that its default runtime completes a mocked turn with the expected user input and final response.

GitOrigin-RevId: 8e3126742a014e2cf042afe799bc657e1ea2a282
This commit is contained in:
Ahmed Ibrahim
2026-09-09 04:40:22 +00:00
committed by copyberry
parent 9ba1d9eb5b
commit c55db1b8d9
3 changed files with 82 additions and 44 deletions
+35 -42
View File
@@ -4,7 +4,7 @@ on:
workflow_call:
jobs:
python-sdk:
python-sdk-installation:
runs-on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-linux-x64
@@ -15,13 +15,10 @@ jobs:
with:
persist-credentials: false
- name: Test Python SDK
- name: Test the installed Python SDK and its default runtime
shell: bash
run: |
set -euo pipefail
# Run inside a glibc Linux image so dependency resolution exercises
# the pinned manylinux runtime wheel that users install.
docker run --rm \
--user "$(id -u):$(id -g)" \
-e HOME=/tmp/codex-python-sdk-home \
@@ -30,23 +27,19 @@ jobs:
-w "${GITHUB_WORKSPACE}/sdk/python" \
python:3.12-slim \
sh -euxc '
python -m venv /tmp/uv
/tmp/uv/bin/python -m pip install uv==0.11.3
/tmp/uv/bin/uv sync --group dev --frozen
/tmp/uv/bin/uv run --frozen --no-sync ruff check --output-format=github .
/tmp/uv/bin/uv run --frozen --no-sync ruff format --check .
/tmp/uv/bin/uv run --frozen --no-sync pytest
python -m venv /tmp/build
/tmp/build/bin/python -m pip install uv==0.11.3
/tmp/build/bin/uv build --wheel --out-dir /tmp/dist
python -m venv /tmp/installed
/tmp/build/bin/uv pip install --python /tmp/installed/bin/python /tmp/dist/*.whl
env -u PYTHONPATH -u CODEX_EXEC_PATH /tmp/installed/bin/python tests/installed_sdk_smoke.py
'
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
sdks:
runs-on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-linux-x64
timeout-minutes: 10
timeout-minutes: 20
environment:
name: bazel
deployment: false
@@ -80,7 +73,7 @@ jobs:
with:
target: x86_64-unknown-linux-gnu
- name: Build codex with Bazel
- name: Build codex and the code-mode host with Bazel
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
shell: bash
@@ -88,8 +81,8 @@ jobs:
set -euo pipefail
# Use the shared CI wrapper so fork PRs fall back cleanly when
# BuildBuddy credentials are unavailable. This workflow needs the
# built `codex` binary on disk afterwards, so ask the wrapper to
# override CI's default remote_download_minimal behavior.
# built binaries on disk afterwards, so ask the wrapper to override
# CI's default remote_download_minimal behavior.
./.github/scripts/run-bazel-ci.sh \
--remote-download-toplevel \
-- \
@@ -97,42 +90,29 @@ jobs:
--build_metadata=COMMIT_SHA=${GITHUB_SHA} \
--build_metadata=TAG_job=sdk \
-- \
//codex-rs/cli:codex
//codex-rs/cli:codex \
//codex-rs/code-mode-host:codex-code-mode-host
# Resolve the exact output file using the same wrapper/config path as
# the build instead of guessing which Bazel convenience symlink is
# available on the runner.
cquery_output="$(
./.github/scripts/run-bazel-ci.sh \
-- \
cquery \
--output=files \
-- \
//codex-rs/cli:codex \
| grep -E '^(/|bazel-out/)' \
| tail -n 1
'set(//codex-rs/cli:codex //codex-rs/code-mode-host:codex-code-mode-host)' \
| grep -E '^(/|bazel-out/)'
)"
if [[ "${cquery_output}" = /* ]]; then
codex_bazel_output_path="${cquery_output}"
else
codex_bazel_output_path="${GITHUB_WORKSPACE}/${cquery_output}"
fi
if [[ -z "${codex_bazel_output_path}" ]]; then
echo "Bazel did not report an output path for //codex-rs/cli:codex." >&2
exit 1
fi
if [[ ! -e "${codex_bazel_output_path}" ]]; then
echo "Unable to locate the Bazel-built codex binary at ${codex_bazel_output_path}." >&2
mapfile -t bazel_output_paths <<< "${cquery_output}"
if [[ ${#bazel_output_paths[@]} -ne 2 ]]; then
echo "Expected both Bazel-built binaries, found ${#bazel_output_paths[@]}." >&2
exit 1
fi
# Stage the binary into the workspace and point the SDK tests at that
# stable path. The tests spawn `codex` directly many times, so using a
# normal executable path is more reliable than invoking Bazel for each
# test process.
# Stage both binaries together so the CLI can discover its sibling
# code-mode host while the SDK tests spawn the CLI directly.
install_dir="${GITHUB_WORKSPACE}/.tmp/sdk-ci"
mkdir -p "${install_dir}"
install -m 755 "${codex_bazel_output_path}" "${install_dir}/codex"
install -m 755 "${bazel_output_paths[@]}" "${install_dir}"
echo "CODEX_EXEC_PATH=${install_dir}/codex" >> "$GITHUB_ENV"
- name: Warm up Bazel-built codex
@@ -153,6 +133,19 @@ jobs:
- name: Test SDK packages
run: pnpm -r --filter ./sdk/typescript run test
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.3"
- name: Test Python SDK against the same CLI
working-directory: sdk/python
run: |
uv sync --only-group dev --frozen
uv run --only-group dev --frozen --no-sync ruff check --output-format=github .
uv run --only-group dev --frozen --no-sync ruff format --check .
uv run --only-group dev --frozen --no-sync pytest
- name: Save bazel repository cache
if: always() && !cancelled() && steps.setup_bazel.outputs.cache-hit != 'true'
continue-on-error: true