name: sdk on: workflow_call: jobs: python-sdk-installation: runs-on: group: ${{ github.event.repository.name }}-runners labels: ${{ github.event.repository.name }}-linux-x64 timeout-minutes: 10 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Test the installed Python SDK and its default runtime shell: bash run: | set -euo pipefail docker run --rm \ --user "$(id -u):$(id -g)" \ -e HOME=/tmp/codex-python-sdk-home \ -e UV_LINK_MODE=copy \ -v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \ -w "${GITHUB_WORKSPACE}/sdk/python" \ python:3.12-slim \ sh -euxc ' 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 ' sdks: runs-on: group: ${{ github.event.repository.name }}-runners labels: ${{ github.event.repository.name }}-linux-x64 timeout-minutes: 20 environment: name: bazel deployment: false steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Install Linux bwrap build dependencies shell: bash run: | set -euo pipefail sudo apt-get update -y sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - name: Setup pnpm uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5 with: run_install: false - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: 22 cache: pnpm - name: Prepare Bazel CI id: setup_bazel uses: ./.github/actions/prepare-bazel-ci with: target: x86_64-unknown-linux-gnu cache-scope: sdk - name: Build codex and the code-mode host with Bazel id: build_bazel env: BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} shell: bash run: | set -euo pipefail # Use the shared CI wrapper so fork PRs fall back cleanly when # BuildBuddy credentials are unavailable. This workflow needs the # 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 \ -- \ build \ --build_metadata=COMMIT_SHA=${GITHUB_SHA} \ --build_metadata=TAG_job=sdk \ -- \ //codex-rs/cli:codex \ //codex-rs/code-mode-host:codex-code-mode-host cquery_output="$( ./.github/scripts/run-bazel-ci.sh \ -- \ cquery \ --output=files \ -- \ 'set(//codex-rs/cli:codex //codex-rs/code-mode-host:codex-code-mode-host)' \ | grep -E '^(/|bazel-out/)' )" 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 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 "${bazel_output_paths[@]}" "${install_dir}" echo "CODEX_EXEC_PATH=${install_dir}/codex" >> "$GITHUB_ENV" - name: Warm up Bazel-built codex shell: bash run: | set -euo pipefail "${CODEX_EXEC_PATH}" --version - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build SDK packages run: pnpm -r --filter ./sdk/typescript run build - name: Lint SDK packages run: pnpm -r --filter ./sdk/typescript run lint - 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.build_bazel.outcome == 'success' && steps.setup_bazel.outputs.repository-cache-hit != 'true' continue-on-error: true uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ${{ steps.setup_bazel.outputs.repository-cache-path }} key: ${{ steps.setup_bazel.outputs.repository-cache-key }} - name: Check for a clean worktree if: always() && !cancelled() uses: ./.github/actions/check-clean-worktree