name: sdk on: push: branches: [main] pull_request: {} jobs: sdks: runs-on: group: codex-runners labels: codex-linux-x64 timeout-minutes: 10 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - 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 with: node-version: 22 cache: pnpm - name: Set up Bazel CI id: setup_bazel uses: ./.github/actions/setup-bazel-ci with: target: x86_64-unknown-linux-gnu - name: Build codex with 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 `codex` binary 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 # 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 )" 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 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. install_dir="${GITHUB_WORKSPACE}/.tmp/sdk-ci" mkdir -p "${install_dir}" install -m 755 "${codex_bazel_output_path}" "${install_dir}/codex" 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: Setup Python uses: actions/setup-python@v6 with: python-version: "3.13" - name: Build and smoke test Python SDK packages shell: bash run: | set -euo pipefail python -m pip install --upgrade pip python -m pip install build hatchling datamodel-code-generator==0.31.2 "ruff>=0.11" pytest python sdk/python/scripts/update_sdk_artifacts.py generate-types git diff --exit-code -- \ sdk/python/src/codex_app_server/generated \ sdk/python/src/codex_app_server/api.py runtime_bundle="${RUNNER_TEMP}/codex-runtime-bundle" runtime_stage="${RUNNER_TEMP}/openai-codex-cli-bin-stage" sdk_stage="${RUNNER_TEMP}/openai-codex-stage" runtime_venv="${RUNNER_TEMP}/python-runtime-venv" sdk_venv="${RUNNER_TEMP}/python-sdk-venv" rm -rf "$runtime_bundle" "$runtime_stage" "$sdk_stage" "$runtime_venv" "$sdk_venv" mkdir -p "$runtime_bundle" install -m 755 "$CODEX_EXEC_PATH" "$runtime_bundle/codex" python sdk/python/scripts/update_sdk_artifacts.py \ stage-runtime \ "$runtime_stage" \ "$runtime_bundle" \ --runtime-version 0.0.0.dev0 python -m build --wheel "$runtime_stage" python -m venv "$runtime_venv" "$runtime_venv/bin/python" -m pip install --upgrade pip runtime_wheel="$(ls "$runtime_stage"/dist/openai_codex_cli_bin-*.whl)" "$runtime_venv/bin/python" -m pip install "$runtime_wheel" "$runtime_venv/bin/python" - <<'PY' import subprocess from codex_cli_bin import bundled_codex_path, bundled_runtime_files path = bundled_codex_path() assert bundled_runtime_files() == (path,) subprocess.run([str(path), "--version"], check=True) PY python sdk/python/scripts/update_sdk_artifacts.py \ stage-sdk \ "$sdk_stage" \ --sdk-version 0.0.0.dev0 \ --runtime-version 0.0.0.dev0 python -m build "$sdk_stage" python -m venv "$sdk_venv" "$sdk_venv/bin/python" -m pip install --upgrade pip sdk_wheel="$(ls "$sdk_stage"/dist/openai_codex-*.whl)" "$sdk_venv/bin/python" -m pip install "$runtime_wheel" "$sdk_venv/bin/python" -m pip install "$sdk_wheel" "$sdk_venv/bin/python" - <<'PY' import codex_app_server assert codex_app_server.__version__ == "0.0.0.dev0" PY - 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: Save bazel repository cache if: always() && !cancelled() && steps.setup_bazel.outputs.cache-hit != 'true' continue-on-error: true uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: | ~/.cache/bazel-repo-cache key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}