Add authenticated GCS caching for Bazel CI and releases (#61187)

## What changed

- Authenticate eligible protected `main` runs and Rust release tags with GitHub OIDC. Allow cache uploads only on `main` pushes; use read-only access for manual runs and release tags, and leave pull requests without GCS credentials.
- Rename the shared wrapper to `.github/scripts/run_bazel.py` and route Bazel callers through it. Disable the disk cache in GitHub Actions and default macOS GCS connections to 20 while respecting explicit limits.
- Gate additional Bazel release artifacts on `CODEX_BAZEL_RELEASES` and cache configuration, sharing the selected build systems across building, signing, and packaging. Keep Cargo as the default and prepare native Windows dependencies for Bazel releases.
- Add a `Bazel result` check for the full workflow and extend Bazel job timeouts.

## Testing

Add wrapper tests for cache access by event, argument preservation, and macOS connection limits. The wrapper test suite passes.

GitOrigin-RevId: d85edbd083acfaa8bafaa8fd5241daed3e9ef871
This commit is contained in:
Dmitriy Kovalenko
2026-11-12 21:41:05 +00:00
committed by copyberry
parent d0221bc947
commit 4490060db6
25 changed files with 464 additions and 184 deletions
@@ -20,9 +20,9 @@ runs:
- name: Install Cargo for workspace metadata
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- name: Configure MSVC environment
- name: Prepare native Windows release dependencies
if: ${{ runner.os == 'Windows' }}
uses: ./.github/actions/setup-msvc-env
uses: ./.github/actions/setup-bazel-windows
with:
target: ${{ inputs.target }}
@@ -33,12 +33,12 @@ runs:
# Keep Git Bash from rewriting Bazel labels as Windows paths.
MSYS2_ARG_CONV_EXCL: "*"
run: |
python3 .github/scripts/run_bazel_with_buildbuddy.py build \
${{ runner.os == 'Windows' && '--config=ci-windows-msvc' || '' }} \
${{ runner.os == 'macOS' && '--config=ci-macos --remote_download_toplevel --build_metadata=VISIBILITY=PRIVATE' || '' }} \
./.github/scripts/run-bazel-ci.sh \
${{ runner.os == 'Windows' && '--windows-msvc-host-platform' || '' }} -- build \
${{ runner.os == 'macOS' && '--config=ci-macos --remote_download_toplevel' || '' }} \
"--platforms=@rules_rs//rs/platforms:$TARGET" \
"--//codex-rs:runtime_package_build_mode=$BUILD_MODE" \
"//codex-rs:runtime_package_$BUNDLE"
-- "//codex-rs:runtime_package_$BUNDLE"
- name: Extract staging archive
shell: bash
@@ -24,6 +24,58 @@ runs:
- id: setup_ci
uses: ./.github/actions/setup-ci
- name: Authenticate the Bazel cache
id: gcs
if: >-
github.repository == 'openai/codex' && github.ref_protected &&
env.CODEX_BAZEL_GCS_BUCKET != '' && env.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER != '' &&
((github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')) ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/rust-v')))
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ env.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
export_environment_variables: false
- name: Configure Bazel cache credentials
if: ${{ always() && steps.gcs.outcome != 'skipped' }}
shell: bash
env:
CREDENTIALS_FILE: ${{ steps.gcs.outputs.credentials_file_path }}
AUTH_OUTCOME: ${{ steps.gcs.outcome }}
run: |
if [[ "$RUNNER_OS" == "Windows" ]]; then
bazel_python=python
else
bazel_python=python3
fi
"$bazel_python" - <<'PYTHON'
import os
from pathlib import Path
import shutil
# auth creates the file in the checkout before it tests the token exchange.
# Keep it out of Bazel inputs, even when authentication failed.
if os.environ["CREDENTIALS_FILE"]:
source = Path(os.environ["CREDENTIALS_FILE"])
credentials = Path(os.environ["RUNNER_TEMP"]) / source.name
shutil.move(source, credentials)
credentials.chmod(0o600)
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as env:
print(f"GOOGLE_GHA_CREDS_PATH={credentials}", file=env) # auth post-job cleanup
if os.environ["AUTH_OUTCOME"] == "success":
print(f"CODEX_BAZEL_GCS_CREDENTIALS={credentials}", file=env)
print("Bazel GCS cache credentials configured.")
else:
print("Bazel GCS cache authentication failed. Credentials were moved for cleanup.")
else:
print("Bazel GCS authentication did not produce a credentials file.")
PYTHON
- name: Report skipped Bazel cache authentication
if: ${{ steps.gcs.outcome == 'skipped' }}
shell: bash
run: echo "Bazel GCS authentication skipped. Cache configuration is missing or this event is outside the allowed cache access policy."
- name: Set up Bazel
uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0
# Without an explicit Bazelisk version, setup-bazel leaves PATH unchanged
@@ -1,5 +1,5 @@
<#
BuildBuddy cache keys include the action and test environment, so Bazel should
Bazel action cache keys include the action and test environment, so Bazel should
not inherit the full hosted-runner PATH on Windows. That PATH includes volatile
tool entries, such as Maven, that can change independently of this repo and
cause avoidable cache misses.
+2 -2
View File
@@ -53,11 +53,11 @@ fi
run_bazel() {
if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
MSYS2_ARG_CONV_EXCL='*' "$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
MSYS2_ARG_CONV_EXCL='*' "$(dirname "${BASH_SOURCE[0]}")/run_bazel.py" "$@"
return
fi
"$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
"$(dirname "${BASH_SOURCE[0]}")/run_bazel.py" "$@"
}
run_bazel_with_startup_args() {
+2 -2
View File
@@ -17,11 +17,11 @@ query_expression="${@: -1}"
run_bazel() {
if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
MSYS2_ARG_CONV_EXCL='*' "$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
MSYS2_ARG_CONV_EXCL='*' "$(dirname "${BASH_SOURCE[0]}")/run_bazel.py" "$@"
return
fi
"$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
"$(dirname "${BASH_SOURCE[0]}")/run_bazel.py" "$@"
}
bazel_query_args=(query)
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
import os
import re
import subprocess
import sys
from collections.abc import Mapping
from collections.abc import Sequence
from pathlib import Path
# These configs select remote execution, which is unavailable on CI runners.
@@ -92,6 +94,41 @@ def bazel_args_with_remote_config(
arg.startswith(option_prefix) for arg in configured_args[:separator_idx]
)
]
# Ephemeral runners already retain active outputs in Bazel's output tree.
# Avoid storing a second copy while using the GCS action cache.
if env.get("GITHUB_ACTIONS") == "true":
cache_args.append("--disk_cache=")
bucket = env.get("CODEX_BAZEL_GCS_BUCKET")
credentials = env.get("CODEX_BAZEL_GCS_CREDENTIALS")
if (
bucket
and credentials
and args[command_idx] in {"build", "test", "run", "coverage"}
):
if not re.fullmatch(r"[a-z0-9][a-z0-9_-]{1,61}[a-z0-9]", bucket):
raise ValueError("Expected a GCS bucket name")
if not Path(credentials).is_absolute() or not Path(credentials).is_file():
raise ValueError(
"Missing GitHub workload identity credentials for the GCS cache"
)
cache_args.extend(
(
f"--remote_cache=https://storage.googleapis.com/{bucket}",
f"--google_credentials={credentials}",
)
)
# Bound HTTP socket pressure on macOS runners while reusing the GCS cache.
if env.get("RUNNER_OS") == "macOS" and not any(
arg == "--remote_max_connections"
or arg.startswith("--remote_max_connections=")
for arg in configured_args[:separator_idx]
):
cache_args.append("--remote_max_connections=20")
if (
env.get("GITHUB_EVENT_NAME") != "push"
or env.get("GITHUB_REF") != "refs/heads/main"
):
cache_args.append("--remote_upload_local_results=false")
return [
*configured_args[:separator_idx],
*cache_args,
+1 -1
View File
@@ -12,7 +12,7 @@ import sys
import tomllib
from pathlib import Path
from run_bazel_with_buildbuddy import bazel_command
from run_bazel import bazel_command
from rusty_v8_module_bazel import (
RustyV8ChecksumError,
check_module_bazel,
+201
View File
@@ -0,0 +1,201 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
import run_bazel
class RunBazelCiTest(unittest.TestCase):
def test_cache_permissions_and_run_arguments(self) -> None:
with TemporaryDirectory() as temp_dir:
credentials = Path(temp_dir) / "job credentials.json"
credentials.touch()
for event, ref, authenticated, mode in (
("push", "refs/heads/main", True, "write"),
("workflow_dispatch", "refs/heads/main", True, "read"),
("push", "refs/tags/rust-v1.0.0", True, "read"),
("pull_request", "refs/pull/1/merge", False, "local"),
):
with self.subTest(event=event, ref=ref, authenticated=authenticated):
env = {
"GITHUB_EVENT_NAME": event,
"GITHUB_REF": ref,
"CODEX_BAZEL_GCS_BUCKET": "example-public",
}
if authenticated:
env["CODEX_BAZEL_GCS_CREDENTIALS"] = str(credentials)
flags = []
if mode != "local":
flags = [
"--remote_cache=https://storage.googleapis.com/example-public",
f"--google_credentials={credentials}",
]
if mode == "read":
flags.append("--remote_upload_local_results=false")
self.assertEqual(
run_bazel.bazel_args_with_remote_config(
[
"run",
"--config=ci-linux",
"//:binary",
"--",
"--user-flag",
],
env,
),
["run", "//:binary", *flags, "--", "--user-flag"],
)
def test_macos_cache_connection_limit_respects_explicit_setting(self) -> None:
with TemporaryDirectory() as temp_dir:
credentials = Path(temp_dir) / "credentials.json"
credentials.touch()
env = {
"RUNNER_OS": "macOS",
"GITHUB_EVENT_NAME": "push",
"GITHUB_REF": "refs/heads/main",
"CODEX_BAZEL_GCS_BUCKET": "example-public",
"CODEX_BAZEL_GCS_CREDENTIALS": str(credentials),
}
for supplied in (
[],
["--remote_max_connections=8"],
["--remote_max_connections", "8"],
):
with self.subTest(supplied=supplied):
configured = run_bazel.bazel_args_with_remote_config(
["build", *supplied, "//:binary"], env
)
expected = [
"build",
*supplied,
"//:binary",
"--remote_cache=https://storage.googleapis.com/example-public",
f"--google_credentials={credentials}",
]
if not supplied:
expected.append("--remote_max_connections=20")
self.assertEqual(configured, expected)
def test_bazel_command_uses_configured_binary_locally(self) -> None:
self.assertEqual(
run_bazel.bazel_command(
"info",
"execution_root",
env={"CODEX_BAZEL_BIN": "fake-bazel"},
),
["fake-bazel", "info", "execution_root"],
)
def test_bazel_command_normalizes_github_actions_startup_options(self) -> None:
env = {
"BAZEL_OUTPUT_USER_ROOT": "/tmp/bazel-output",
"GITHUB_ACTIONS": "true",
}
self.assertEqual(
run_bazel.bazel_command("build", "//codex-rs/...", env=env),
[
"bazel",
"--output_user_root=/tmp/bazel-output",
"--noexperimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
"--disk_cache=",
],
)
self.assertEqual(
run_bazel.bazel_command(
"--experimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
env=env,
),
[
"bazel",
"--output_user_root=/tmp/bazel-output",
"--experimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
"--disk_cache=",
],
)
def test_bazel_command_uses_configured_local_caches(self) -> None:
env = {
"BAZEL_REPO_CONTENTS_CACHE": "/tmp/bazel-repo-contents",
"BAZEL_REPOSITORY_CACHE": "/tmp/bazel-repository",
}
self.assertEqual(
run_bazel.bazel_command(
"build",
"--config=local",
"//codex-rs/...",
env=env,
),
[
"bazel",
"build",
"--config=local",
"//codex-rs/...",
"--repo_contents_cache=/tmp/bazel-repo-contents",
"--repository_cache=/tmp/bazel-repository",
],
)
def test_bazel_command_adds_local_caches_before_separator(self) -> None:
self.assertEqual(
run_bazel.bazel_command(
"build",
"//codex-rs/...",
"--",
"--program-arg",
env={"BAZEL_REPOSITORY_CACHE": "/tmp/bazel-repository"},
),
[
"bazel",
"build",
"//codex-rs/...",
"--repository_cache=/tmp/bazel-repository",
"--",
"--program-arg",
],
)
def test_main_preserves_spaced_argument_and_child_exit_status(self) -> None:
spaced_arg = (
r"--test_env=PATH=C:\Program Files\PowerShell\7;C:\Program Files\Git\bin"
)
child_code = (
f"import sys; sys.exit(37 if sys.argv[1] == {spaced_arg!r} else 91)"
)
env = os.environ.copy()
env["CODEX_BAZEL_BIN"] = sys.executable
env.pop("BAZEL_OUTPUT_USER_ROOT", None)
env.pop("GITHUB_ACTIONS", None)
result = subprocess.run(
[
sys.executable,
str(Path(run_bazel.__file__)),
"-c",
child_code,
spaced_arg,
],
env=env,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 37, result.stderr)
if __name__ == "__main__":
unittest.main()
@@ -1,127 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
import unittest
from pathlib import Path
import run_bazel_with_buildbuddy
class RunBazelWithBuildBuddyTest(unittest.TestCase):
def test_bazel_command_uses_configured_binary_locally(self) -> None:
self.assertEqual(
run_bazel_with_buildbuddy.bazel_command(
"info",
"execution_root",
env={"CODEX_BAZEL_BIN": "fake-bazel"},
),
["fake-bazel", "info", "execution_root"],
)
def test_bazel_command_normalizes_github_actions_startup_options(self) -> None:
env = {
"BAZEL_OUTPUT_USER_ROOT": "/tmp/bazel-output",
"GITHUB_ACTIONS": "true",
}
self.assertEqual(
run_bazel_with_buildbuddy.bazel_command("build", "//codex-rs/...", env=env),
[
"bazel",
"--output_user_root=/tmp/bazel-output",
"--noexperimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
],
)
self.assertEqual(
run_bazel_with_buildbuddy.bazel_command(
"--experimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
env=env,
),
[
"bazel",
"--output_user_root=/tmp/bazel-output",
"--experimental_remote_repo_contents_cache",
"build",
"//codex-rs/...",
],
)
def test_bazel_command_uses_configured_local_caches(self) -> None:
env = {
"BAZEL_REPO_CONTENTS_CACHE": "/tmp/bazel-repo-contents",
"BAZEL_REPOSITORY_CACHE": "/tmp/bazel-repository",
}
self.assertEqual(
run_bazel_with_buildbuddy.bazel_command(
"build",
"--config=local",
"//codex-rs/...",
env=env,
),
[
"bazel",
"build",
"--config=local",
"//codex-rs/...",
"--repo_contents_cache=/tmp/bazel-repo-contents",
"--repository_cache=/tmp/bazel-repository",
],
)
def test_bazel_command_adds_local_caches_before_separator(self) -> None:
self.assertEqual(
run_bazel_with_buildbuddy.bazel_command(
"build",
"//codex-rs/...",
"--",
"--program-arg",
env={"BAZEL_REPOSITORY_CACHE": "/tmp/bazel-repository"},
),
[
"bazel",
"build",
"//codex-rs/...",
"--repository_cache=/tmp/bazel-repository",
"--",
"--program-arg",
],
)
def test_main_preserves_spaced_argument_and_child_exit_status(self) -> None:
spaced_arg = (
r"--test_env=PATH=C:\Program Files\PowerShell\7;C:\Program Files\Git\bin"
)
child_code = (
f"import sys; sys.exit(37 if sys.argv[1] == {spaced_arg!r} else 91)"
)
env = os.environ.copy()
env["CODEX_BAZEL_BIN"] = sys.executable
env.pop("BAZEL_OUTPUT_USER_ROOT", None)
env.pop("GITHUB_ACTIONS", None)
result = subprocess.run(
[
sys.executable,
str(Path(run_bazel_with_buildbuddy.__file__)),
"-c",
child_code,
spaced_arg,
],
env=env,
check=False,
capture_output=True,
text=True,
)
self.assertEqual(result.returncode, 37, result.stderr)
if __name__ == "__main__":
unittest.main()
+1 -1
View File
@@ -22,7 +22,7 @@ CANARY_PATH_PATTERNS = {
".bazelrc",
".github/actions/prepare-bazel-ci/**",
".github/actions/setup-ci/**",
".github/scripts/run_bazel_with_buildbuddy.py",
".github/scripts/run_bazel.py",
".github/scripts/rusty_v8_bazel.py",
".github/scripts/rusty_v8_module_bazel.py",
".github/scripts/setup-dev-drive.ps1",
+19 -1
View File
@@ -20,7 +20,7 @@ The workflows in this directory are split so that pull requests get fast, review
## Post-Merge On `main`
- `bazel.yml` also runs on pushes to `main`.
This re-verifies the merged Bazel path and helps keep the BuildBuddy caches warm.
This re-verifies the merged Bazel path and populates the public GCS cache.
- `rust-ci-full.yml` is the full Cargo-native verification workflow.
It keeps the heavier checks off the PR path while still validating them after merge:
- the full Cargo `clippy` matrix
@@ -35,3 +35,21 @@ The workflows in this directory are split so that pull requests get fast, review
- If a build/test/clippy check can be expressed in Bazel, prefer putting the PR-time version in `bazel.yml`.
- Keep `rust-ci.yml` fast enough that it usually does not dominate PR latency.
- Reserve `rust-ci-full.yml` for heavyweight Cargo-native coverage that Bazel does not replace yet.
### Public Bazel action cache
Set `CODEX_BAZEL_GCS_BUCKET` and `CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER`
as GitHub Actions secrets in `openai/codex`. They identify the bucket and its
GitHub OIDC provider. The job exchanges its identity for temporary Google
credentials. Protected main pushes populate the cache. Protected main
dispatches and Rust release tags can read it. Pull requests build without
GCS credentials.
After a main push populates the cache and a separate runner reports a remote
cache hit, set the repository variable `CODEX_BAZEL_RELEASES` to `true` to
build the additional Bazel artifacts on Rust release tags. The release
workflow selects one build-system matrix for the build, signing, and
packaging jobs. Cargo artifacts remain the default.
Once public Bazel CI passes, require the `Bazel result` check in the main
branch ruleset. It reports the result of the full Bazel workflow.
+25 -6
View File
@@ -13,12 +13,19 @@ concurrency:
group: concurrency-group::${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}${{ github.ref_name == 'main' && format('::{0}', github.run_id) || ''}}
cancel-in-progress: ${{ github.ref_name != 'main' }}
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
jobs:
test:
permissions:
contents: read
id-token: write
# PRs use the sharded Windows cross-compiled test jobs below. Post-merge
# pushes to main also run the native Windows test job for broader Windows
# signal without putting PR latency back on the critical path.
timeout-minutes: 30
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
@@ -133,8 +140,11 @@ jobs:
uses: ./.github/actions/check-clean-worktree
test-windows-shard:
permissions:
contents: read
id-token: write
# Build and test Windows shards on separate Windows runners.
timeout-minutes: 30
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
@@ -159,7 +169,7 @@ jobs:
- name: Test Bazel command wrapper
if: matrix.shard == 1
shell: pwsh
run: python .github/scripts/test_run_bazel_with_buildbuddy.py
run: python .github/scripts/test_run_bazel.py
- name: Test Windows Bazel target selector
if: matrix.shard == 1
@@ -255,11 +265,14 @@ jobs:
fi
test-windows-native-main:
permissions:
contents: read
id-token: write
# Native Windows Bazel tests are slower and frequently approach the
# 30-minute PR budget. Run this only for post-merge commits to main and give
# it a larger timeout. Also allow manual runs for native toolchain changes.
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
timeout-minutes: 40
timeout-minutes: 90
runs-on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
@@ -341,7 +354,10 @@ jobs:
uses: ./.github/actions/check-clean-worktree
clippy:
timeout-minutes: 30
permissions:
contents: read
id-token: write
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
@@ -446,7 +462,10 @@ jobs:
uses: ./.github/actions/check-clean-worktree
verify-release-build:
timeout-minutes: 30
permissions:
contents: read
id-token: write
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
+20 -2
View File
@@ -11,10 +11,24 @@ jobs:
# Keep reusable workflow calls alphabetized. The `required` job below is the
# version-controlled list that the main-branch ruleset should require.
bazel:
permissions:
contents: read
id-token: write
name: Bazel
uses: ./.github/workflows/bazel.yml
secrets: inherit
bazel-result:
name: Bazel result
needs: bazel
if: ${{ always() }}
runs-on: ubuntu-24.04
steps:
- name: Require the Bazel workflow to succeed
env:
BAZEL_RESULT: ${{ needs.bazel.result }}
run: test "$BAZEL_RESULT" = success
blob-size-policy:
name: Blob size policy
uses: ./.github/workflows/blob-size-policy.yml
@@ -36,11 +50,17 @@ jobs:
secrets: inherit
rust-ci:
permissions:
contents: read
id-token: write
name: rust-ci
uses: ./.github/workflows/rust-ci.yml
secrets: inherit
sdk:
permissions:
contents: read
id-token: write
name: sdk
uses: ./.github/workflows/sdk.yml
secrets: inherit
@@ -50,8 +70,6 @@ jobs:
# Without `always()`, GitHub skips this job after a failed dependency and a
# required check can appear successful instead of reporting the failure.
if: ${{ always() }}
# Bazel is advisory while the BuildBuddy key is revoked. Restore its dependency
# once the suite can run without BuildBuddy.
needs:
- blob-size-policy
- cargo-deny
+6
View File
@@ -10,11 +10,17 @@ jobs:
# Keep reusable workflow calls alphabetized. Each child retains its own
# workflow_dispatch trigger so maintainers can rerun flaky suites directly.
rust-ci-full:
permissions:
contents: read
id-token: write
name: rust-ci-full
uses: ./.github/workflows/rust-ci-full.yml
secrets: inherit
v8-canary:
permissions:
contents: read
id-token: write
name: v8-canary
uses: ./.github/workflows/v8-canary.yml
secrets: inherit
+8
View File
@@ -8,6 +8,10 @@ on:
- "**full-ci**"
workflow_dispatch:
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
jobs:
# --- CI that doesn't need specific targets ---------------------------------
general:
@@ -90,6 +94,9 @@ jobs:
RUST_MIN_STACK: "8388608" # 8 MiB
argument_comment_lint_prebuilt:
permissions:
contents: read
id-token: write
name: Argument comment lint - ${{ matrix.name }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
timeout-minutes: 120
@@ -127,6 +134,7 @@ jobs:
deployment: false
permissions:
contents: read
id-token: write
timeout-minutes: 120
strategy:
fail-fast: false
+8 -2
View File
@@ -3,6 +3,10 @@ on:
workflow_call:
workflow_dispatch:
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
jobs:
# --- Detect changed paths so the fast PR workflow only runs relevant jobs ---
changed:
@@ -162,9 +166,11 @@ jobs:
uses: ./.github/actions/check-clean-worktree
argument_comment_lint_prebuilt:
permissions:
contents: read
id-token: write
name: Argument comment lint - ${{ matrix.name }}
# This job runs through Bazel and is advisory while the BuildBuddy key is
# revoked. Restore blocking behavior once it can run without BuildBuddy.
# Keep this Bazel job advisory until the public build and cache rollout is verified.
continue-on-error: true
runs-on: ${{ matrix.runs_on || matrix.runner }}
timeout-minutes: ${{ matrix.timeout_minutes }}
@@ -1,4 +1,4 @@
# Verified Cargo side packages, published by rust-release.yml when enabled.
# Verified side packages, published by rust-release.yml when enabled.
# The legacy CLI, npm, installer and DMG packages are unchanged.
# Required secrets in the codesigning environment:
# - CODEX_CLI_PROVISIONING_PROFILE_BASE64: profile approved for public distribution.
@@ -11,6 +11,11 @@ name: Provisioned macOS CLI candidate
on:
workflow_call:
inputs:
build-systems:
description: Release build systems selected during tag validation, as a JSON array.
type: string
default: '["cargo"]'
permissions:
contents: read
@@ -32,7 +37,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(inputs.build-systems) }}
target: [aarch64-apple-darwin, x86_64-apple-darwin]
env:
TARGET: ${{ matrix.target }}
@@ -131,7 +136,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(inputs.build-systems) }}
target: [aarch64-apple-darwin, x86_64-apple-darwin]
include:
- target: aarch64-apple-darwin
+19 -5
View File
@@ -2,14 +2,20 @@ name: rust-release-windows
on:
workflow_call:
inputs:
build-systems:
description: Release build systems selected during tag validation, as a JSON array.
type: string
default: '["cargo"]'
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
WINDOWS_BINARIES: "codex codex-code-mode-host codex-exec-server codex-responses-api-proxy codex-windows-sandbox-setup codex-windows-sandbox-service codex-command-runner codex-app-server"
jobs:
build-windows-binaries:
name: Build Windows binaries - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }} - ${{ matrix.build-system }}
# Release Cargo artifacts only while Bazel releases are disabled.
runs-on: ${{ matrix.runs_on }}
environment:
name: bazel
@@ -19,6 +25,7 @@ jobs:
timeout-minutes: 120
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: codex-rs
@@ -29,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(inputs.build-systems) }}
target:
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
@@ -117,10 +124,17 @@ jobs:
target: ${{ matrix.target }}
- name: Configure MSVC environment
if: matrix.build-system == 'cargo'
uses: ./.github/actions/setup-msvc-env
with:
target: ${{ matrix.target }}
- name: Prepare native Windows release dependencies
if: matrix.build-system == 'bazel'
uses: ./.github/actions/setup-bazel-windows
with:
target: ${{ matrix.target }}
- name: Build Bazel release binaries and symbols
if: ${{ matrix.build-system == 'bazel' }}
uses: ./.github/actions/build-bazel-release
@@ -182,6 +196,7 @@ jobs:
timeout-minutes: 120
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
@@ -250,7 +265,6 @@ jobs:
env:
ARCH: ${{ matrix.arch }}
VOICE_ARCH: ${{ matrix.arch }}
# Use local Bazel while BuildBuddy credentials are unavailable.
run: |
(cd codex-rs && cargo update --workspace)
python .github/scripts/watch_voice_bazel.py "$(cygpath -w "$BASH")" ./.github/scripts/run-bazel-ci.sh \
@@ -318,7 +332,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(inputs.build-systems) }}
target:
- aarch64-pc-windows-msvc
- x86_64-pc-windows-msvc
@@ -382,7 +396,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(inputs.build-systems) }}
target:
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
+29 -15
View File
@@ -15,6 +15,8 @@ on:
- "rust-v*.*.*"
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
CODEX_REPO_ROOT: ${{ github.workspace }}
# The launcher uses this per-tag group as the migration boundary. Keep shared
@@ -25,6 +27,12 @@ concurrency:
jobs:
tag-check:
outputs:
build-systems: >-
${{ github.repository == 'openai/codex' && github.ref_protected &&
vars.CODEX_BAZEL_RELEASES == 'true' &&
secrets.CODEX_BAZEL_GCS_BUCKET != '' && secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER != '' &&
'["cargo","bazel"]' || '["cargo"]' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -59,7 +67,6 @@ jobs:
build:
needs: tag-check
name: Build - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }} - ${{ matrix.build-system }}
# Release Cargo artifacts only while Bazel releases are disabled.
runs-on: ${{ matrix.runner }}
environment:
name: bazel
@@ -83,7 +90,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(needs.tag-check.outputs.build-systems) }}
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
@@ -325,7 +332,6 @@ jobs:
shell: bash
env:
APP_TARGET: ${{ matrix.target }}
# Use local Bazel while BuildBuddy credentials are unavailable.
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
@@ -335,7 +341,7 @@ jobs:
x86_64-unknown-linux-gnu) prefix=linux_x86_64 ;;
*) exit 1 ;;
esac
python3 .github/scripts/run_bazel_with_buildbuddy.py build --config=release \
python3 .github/scripts/run_bazel.py build --config=release \
//codex-rs/voice-host:codex-voice-host //third_party/voice:native_runtime
source="bazel-bin/third_party/voice/native_runtime_${prefix}"
output="${RUNNER_TEMP}/signed-voice/${APP_TARGET}"
@@ -577,6 +583,7 @@ jobs:
timeout-minutes: 120
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
@@ -604,13 +611,12 @@ jobs:
env:
TARGET: ${{ matrix.target }}
PREFIX: ${{ matrix.prefix }}
# Use local Bazel while BuildBuddy credentials are unavailable.
run: |
set -euo pipefail
# Release tags bump Cargo.toml without rewriting the workspace lockfile.
# Refresh workspace versions before Bazel reads the Cargo dependency graph.
(cd codex-rs && cargo update --workspace)
python3 .github/scripts/run_bazel_with_buildbuddy.py build --config=release \
python3 .github/scripts/run_bazel.py build --config=release \
--macos_minimum_os=14.0 //codex-rs/voice-host:codex-voice-host //third_party/voice:native_runtime
runtime="bazel-bin/third_party/voice/native_runtime_${PREFIX}"
PYTHONPATH=third_party/voice python3 - "$runtime" "$TARGET" <<'PY'
@@ -632,7 +638,7 @@ jobs:
if-no-files-found: error
sign-macos-binaries:
needs: [build, build-macos-voice]
needs: [tag-check, build, build-macos-voice]
if: ${{ always() && needs.build.result == 'success' && needs.build-macos-voice.result == 'success' }}
name: Sign macOS binaries - ${{ matrix.target }} - ${{ matrix.bundle }} - ${{ matrix.build-system }}
runs-on: ubuntu-latest
@@ -650,7 +656,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(needs.tag-check.outputs.build-systems) }}
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
@@ -878,7 +884,7 @@ jobs:
if-no-files-found: warn
package-macos:
needs: sign-macos-binaries
needs: [tag-check, sign-macos-binaries]
name: Package macOS artifacts - ${{ matrix.target }} - ${{ matrix.bundle }} - ${{ matrix.build-system }}
runs-on: macos-15-xlarge
timeout-minutes: 45
@@ -894,7 +900,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(needs.tag-check.outputs.build-systems) }}
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
@@ -1165,7 +1171,7 @@ jobs:
if-no-files-found: error
sign-macos-dmg:
needs: package-macos
needs: [tag-check, package-macos]
name: Sign macOS DMG - ${{ matrix.target }} - ${{ matrix.build-system }}
runs-on: ubuntu-latest
timeout-minutes: 45
@@ -1182,7 +1188,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(needs.tag-check.outputs.build-systems) }}
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
@@ -1268,6 +1274,7 @@ jobs:
finalize-macos:
needs:
- tag-check
- package-macos
- sign-macos-dmg
name: Verify macOS artifacts - ${{ matrix.target }} - ${{ matrix.bundle }} - ${{ matrix.build-system }}
@@ -1285,7 +1292,7 @@ jobs:
strategy:
fail-fast: false
matrix:
build-system: [cargo] # Bazel releases are disabled while the BuildBuddy key is revoked.
build-system: ${{ fromJSON(needs.tag-check.outputs.build-systems) }}
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
@@ -1533,17 +1540,24 @@ jobs:
# Keep provisioned packages separate from the legacy CLI/npm/installer layout.
# Desktop consumes the versioned codex-provisioned manifest from this release.
provisioned-macos-candidate:
needs: finalize-macos
needs: [tag-check, finalize-macos]
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/rust-v') && vars.CODEX_PROVISIONED_MACOS_CANDIDATE == 'true'
permissions:
contents: read
id-token: write
uses: ./.github/workflows/rust-release-provisioned-macos.yml
with:
build-systems: ${{ needs.tag-check.outputs.build-systems }}
secrets: inherit
build-windows:
permissions:
contents: read
id-token: write
needs: tag-check
uses: ./.github/workflows/rust-release-windows.yml
with:
build-systems: ${{ needs.tag-check.outputs.build-systems }}
secrets: inherit
argument-comment-lint-release-assets:
@@ -1675,7 +1689,7 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
pattern: "{{aarch64,x86_64}-{apple-darwin{,-app-server},unknown-linux-musl{,-app-server},pc-windows-msvc},*-symbols,argument-comment-lint-*,python-runtime-wheel-*,npm-packages}"
pattern: "{{aarch64,x86_64}-{apple-darwin{,-app-server},unknown-linux-musl{,-app-server},pc-windows-msvc}{,-bazel},*-symbols{,-bazel},argument-comment-lint-*,python-runtime-wheel-*,npm-packages}"
- name: Download verified provisioned macOS packages
if: vars.CODEX_PROVISIONED_MACOS_CANDIDATE == 'true'
+1 -1
View File
@@ -191,7 +191,7 @@ jobs:
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
)
./.github/scripts/run_bazel_with_buildbuddy.py \
./.github/scripts/run_bazel.py \
--noexperimental_remote_repo_contents_cache \
"${bazel_args[@]}" \
"--config=${{ matrix.bazel_config }}"
+10 -4
View File
@@ -3,6 +3,10 @@ name: sdk
on:
workflow_call:
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
jobs:
python-sdk-installation:
runs-on:
@@ -36,13 +40,15 @@ jobs:
'
sdks:
# This job builds its CLI with Bazel. Keep it advisory while the BuildBuddy
# key is revoked; restore blocking behavior once it can run without BuildBuddy.
permissions:
contents: read
id-token: write
# Keep the Bazel CLI build advisory until the public build and cache rollout is verified.
continue-on-error: true
runs-on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-linux-x64
timeout-minutes: 20
timeout-minutes: 90
environment:
name: bazel
deployment: false
@@ -83,7 +89,7 @@ jobs:
run: |
set -euo pipefail
# Use the shared CI wrapper so fork PRs fall back cleanly when
# BuildBuddy credentials are unavailable. This workflow needs the
# 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 \
+6 -1
View File
@@ -13,6 +13,10 @@ concurrency:
group: ${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
env:
CODEX_BAZEL_GCS_BUCKET: ${{ secrets.CODEX_BAZEL_GCS_BUCKET }}
CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.CODEX_BAZEL_GCS_WORKLOAD_IDENTITY_PROVIDER }}
jobs:
metadata:
runs-on: ubuntu-latest
@@ -77,6 +81,7 @@ jobs:
runs-on: ${{ matrix.runner }}
permissions:
contents: read
id-token: write
actions: read
environment:
name: bazel
@@ -202,7 +207,7 @@ jobs:
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
)
./.github/scripts/run_bazel_with_buildbuddy.py \
./.github/scripts/run_bazel.py \
--noexperimental_remote_repo_contents_cache \
"${bazel_args[@]}" \
"--config=${{ matrix.bazel_config }}"