mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
ebb7980369
## Why Bazel remote configuration was selected in several CI scripts and workflow steps. That made the BuildBuddy tenant policy easy to duplicate and harder to audit, especially for fork pull requests that must not use the OpenAI tenant. This builds on [sluongng/buildbuddy-ci-host-routing](https://github.com/openai/codex/compare/main...sluongng:codex:sluongng/buildbuddy-ci-host-routing) and consolidates the policy in one place. ## What to do if this breaks you See `codex-rs/docs/bazel.md` for details. TLDR: 1. make a BuildBuddy API key and put it in `~/.bazelrc` 2. if you're an OpenAI employee, add `common --config=buildbuddy-openai-rbe` to `user.bazelrc` in the repo root Run `just bazel-test` to ensure it works. Note that `just bazel-remote-test` no longer exists, you need to select a remote configuration as documented to use RBE. ## What changed - Add `.github/scripts/run_bazel_with_buildbuddy.py` as the shared Bazel wrapper and Python library. It selects the OpenAI host only for trusted upstream GitHub Actions runs, routes keyed fork runs to the generic host, and falls back to local Bazel execution when no key is available. - Move endpoint selection into explicit `.bazelrc` configurations and update Bazel CI, query helpers, and `rusty_v8` staging to use the shared policy. Loading-phase target-discovery queries remain local. - Add wrapper and `rusty_v8` unit coverage, plus `just test-scripts` for the `.github/scripts` Python tests. - Document local Bazel usage, `user.bazelrc` setup, BuildBuddy configurations, and CI behavior in `codex-rs/docs/bazel.md`. ## Validation - `just test-scripts` - `bash -n .github/scripts/run-bazel-ci.sh .github/scripts/run-bazel-query-ci.sh .github/scripts/run-argument-comment-lint-bazel.sh scripts/list-bazel-clippy-targets.sh` - `python3 -m py_compile .github/scripts/run_bazel_with_buildbuddy.py .github/scripts/test_run_bazel_with_buildbuddy.py .github/scripts/test_rusty_v8_bazel.py .github/scripts/rusty_v8_bazel.py` - `ruff check .github/scripts/run_bazel_with_buildbuddy.py .github/scripts/test_run_bazel_with_buildbuddy.py .github/scripts/test_rusty_v8_bazel.py .github/scripts/rusty_v8_bazel.py`
143 lines
4.8 KiB
Python
Executable File
143 lines
4.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from collections.abc import Mapping
|
|
from collections.abc import Sequence
|
|
from pathlib import Path
|
|
|
|
|
|
OPENAI_REPOSITORY = "openai/codex"
|
|
# Remote configurations select cache/BES/download endpoints. Their -rbe forms
|
|
# also select the matching remote executor endpoint.
|
|
GENERIC_REMOTE_CONFIG = "buildbuddy-generic"
|
|
OPENAI_REMOTE_CONFIG = "buildbuddy-openai"
|
|
# These CI configurations require remote build execution. The wrapper supplies
|
|
# an RBE configuration, which also includes the common `remote` settings.
|
|
REMOTE_EXECUTION_CONFIGS = {
|
|
"--config=ci-linux",
|
|
"--config=ci-macos",
|
|
"--config=ci-v8",
|
|
"--config=ci-windows-cross",
|
|
}
|
|
# Only authenticated workflow runs executing trusted upstream code may use the
|
|
# OpenAI BuildBuddy host. A pull request event without proof that its head is
|
|
# in the upstream repository fails closed to the generic host.
|
|
def is_trusted_upstream_run(env: Mapping[str, str]) -> bool:
|
|
# `GITHUB_REPOSITORY` is easy to set locally. Requiring GitHub's workflow
|
|
# marker prevents a local command from opting itself into the OpenAI host.
|
|
if (
|
|
env.get("GITHUB_ACTIONS") != "true"
|
|
or env.get("GITHUB_REPOSITORY") != OPENAI_REPOSITORY
|
|
):
|
|
return False
|
|
# Non-PR workflow runs in `openai/codex` execute upstream refs, so they are
|
|
# trusted. Fork code reaches these workflows only through pull requests.
|
|
if env.get("GITHUB_EVENT_NAME") != "pull_request":
|
|
return True
|
|
|
|
event_path = env.get("GITHUB_EVENT_PATH")
|
|
if not event_path:
|
|
return False
|
|
try:
|
|
event = json.loads(Path(event_path).read_text(encoding="utf-8"))
|
|
except (OSError, json.JSONDecodeError):
|
|
return False
|
|
|
|
try:
|
|
return event["pull_request"]["head"]["repo"]["fork"] is False
|
|
except (KeyError, TypeError):
|
|
return False
|
|
|
|
|
|
def uses_openai_host(env: Mapping[str, str]) -> bool:
|
|
return bool(env.get("BUILDBUDDY_API_KEY")) and is_trusted_upstream_run(env)
|
|
|
|
|
|
def uses_remote_execution(args: Sequence[str]) -> bool:
|
|
try:
|
|
separator_idx = args.index("--")
|
|
except ValueError:
|
|
separator_idx = len(args)
|
|
return any(arg in REMOTE_EXECUTION_CONFIGS for arg in args[:separator_idx])
|
|
|
|
|
|
def remote_config(args: Sequence[str], env: Mapping[str, str]) -> str | None:
|
|
if not env.get("BUILDBUDDY_API_KEY"):
|
|
return None
|
|
|
|
config = OPENAI_REMOTE_CONFIG if uses_openai_host(env) else GENERIC_REMOTE_CONFIG
|
|
if uses_remote_execution(args):
|
|
config += "-rbe"
|
|
return config
|
|
|
|
|
|
def bazel_args_without_remote_execution(args: Sequence[str]) -> list[str]:
|
|
# Remote CI configs require BuildBuddy credentials. Removing them preserves
|
|
# the local fallback used for fork pull requests.
|
|
try:
|
|
separator_idx = args.index("--")
|
|
except ValueError:
|
|
separator_idx = len(args)
|
|
return [
|
|
*(arg for arg in args[:separator_idx] if arg not in REMOTE_EXECUTION_CONFIGS),
|
|
*args[separator_idx:],
|
|
]
|
|
|
|
|
|
def bazel_args_with_remote_config(
|
|
args: Sequence[str], env: Mapping[str, str]
|
|
) -> list[str]:
|
|
config = remote_config(args, env)
|
|
if config is None:
|
|
return bazel_args_without_remote_execution(args)
|
|
|
|
# `remote_config()` returns a configuration only when this key is present.
|
|
api_key = env["BUILDBUDDY_API_KEY"]
|
|
remote_args = [
|
|
f"--config={config}",
|
|
f"--remote_header=x-buildbuddy-api-key={api_key}",
|
|
]
|
|
|
|
# Insert immediately after the Bazel command. This keeps wrapper-added
|
|
# options out of positional payloads and lets later CI configs override
|
|
# shared RBE defaults such as the Windows cross-compilation exec platforms.
|
|
insertion_idx = next(
|
|
(idx + 1 for idx, arg in enumerate(args) if not arg.startswith("-")),
|
|
len(args),
|
|
)
|
|
return [*args[:insertion_idx], *remote_args, *args[insertion_idx:]]
|
|
|
|
|
|
def bazel_command(*args: str, env: Mapping[str, str] | None = None) -> list[str]:
|
|
env = os.environ if env is None else env
|
|
bazel = env.get("CODEX_BAZEL_BIN", "bazel")
|
|
return [bazel, *bazel_args_with_remote_config(args, env)]
|
|
|
|
|
|
def main() -> None:
|
|
config = remote_config(sys.argv[1:], os.environ)
|
|
if config is None:
|
|
print(
|
|
"BuildBuddy key unavailable; using local Bazel configuration.",
|
|
file=sys.stderr,
|
|
)
|
|
else:
|
|
host_description = (
|
|
"OpenAI tenant" if uses_openai_host(os.environ) else "generic"
|
|
)
|
|
print(
|
|
f"Using {host_description} BuildBuddy configuration: {config}.",
|
|
file=sys.stderr,
|
|
)
|
|
|
|
command = bazel_command(*sys.argv[1:])
|
|
# Replace the wrapper so Bazel receives signals directly and supplies the
|
|
# command exit status; a subprocess parent would have no remaining work.
|
|
os.execvp(command[0], command)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|