mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
## 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
158 lines
5.4 KiB
Python
Executable File
158 lines
5.4 KiB
Python
Executable File
#!/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.
|
|
REMOTE_EXECUTION_CONFIGS = {
|
|
"--config=ci-linux",
|
|
"--config=ci-macos",
|
|
"--config=ci-v8",
|
|
"--config=ci-windows-cross",
|
|
}
|
|
# Honor either explicit setting so the wrapper never overrides the caller's
|
|
# choice when it supplies the CI default below.
|
|
REMOTE_REPO_CONTENTS_CACHE_STARTUP_OPTIONS = {
|
|
"--experimental_remote_repo_contents_cache",
|
|
"--noexperimental_remote_repo_contents_cache",
|
|
}
|
|
|
|
|
|
def startup_args(args: Sequence[str], env: Mapping[str, str]) -> list[str]:
|
|
"""Return shared startup options that are missing from a Bazel invocation.
|
|
|
|
Bazel startup options must precede the command, and changing them restarts
|
|
the server and discards its analysis cache. GitHub Actions invokes Bazel
|
|
through several helpers, so normalize their startup options here while
|
|
preserving any explicit choice made by the caller.
|
|
"""
|
|
command_idx = next(
|
|
(idx for idx, arg in enumerate(args) if not arg.startswith("-")),
|
|
len(args),
|
|
)
|
|
configured_startup_args = args[:command_idx]
|
|
injected_args = []
|
|
|
|
output_user_root = env.get("BAZEL_OUTPUT_USER_ROOT")
|
|
if output_user_root and not any(
|
|
arg.startswith("--output_user_root=") for arg in configured_startup_args
|
|
):
|
|
injected_args.append(f"--output_user_root={output_user_root}")
|
|
|
|
if env.get("GITHUB_ACTIONS") == "true" and not any(
|
|
arg in REMOTE_REPO_CONTENTS_CACHE_STARTUP_OPTIONS
|
|
for arg in configured_startup_args
|
|
):
|
|
# Work around Bazel 9 overlay materialization failures seen in CI. This
|
|
# disables only the startup-level repo contents cache.
|
|
injected_args.append("--noexperimental_remote_repo_contents_cache")
|
|
|
|
return injected_args
|
|
|
|
|
|
def bazel_args_without_remote_execution(args: Sequence[str]) -> list[str]:
|
|
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]:
|
|
command_idx = next(
|
|
(idx for idx, arg in enumerate(args) if not arg.startswith("-")),
|
|
None,
|
|
)
|
|
if command_idx is None:
|
|
raise ValueError("expected a Bazel command")
|
|
|
|
configured_args = bazel_args_without_remote_execution(args)
|
|
try:
|
|
separator_idx = configured_args.index("--")
|
|
except ValueError:
|
|
separator_idx = len(configured_args)
|
|
|
|
cache_args = [
|
|
f"{option_prefix}{env[env_name]}"
|
|
for env_name, option_prefix in (
|
|
("BAZEL_REPO_CONTENTS_CACHE", "--repo_contents_cache="),
|
|
("BAZEL_REPOSITORY_CACHE", "--repository_cache="),
|
|
)
|
|
if env.get(env_name)
|
|
and not any(
|
|
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,
|
|
*configured_args[separator_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, *startup_args(args, env), *bazel_args_with_remote_config(args, env)]
|
|
|
|
|
|
def main() -> None:
|
|
command = bazel_command(*sys.argv[1:])
|
|
if os.name == "nt":
|
|
# Windows CRT exec can split arguments containing spaces and lose the
|
|
# eventual child exit status. Wait for Bazel and propagate its status.
|
|
result = subprocess.run(command, check=False)
|
|
raise SystemExit(result.returncode)
|
|
|
|
os.execvp(command[0], command)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|