diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb5ed5b5bf..699ab070c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,10 +52,12 @@ jobs: CODEX_VERSION=0.125.0 WORKFLOW_URL="https://github.com/openai/codex/actions/runs/24901475298" OUTPUT_DIR="${RUNNER_TEMP}" + # This reused workflow predates the standalone bwrap artifact. python3 ./scripts/stage_npm_packages.py \ --release-version "$CODEX_VERSION" \ --workflow-url "$WORKFLOW_URL" \ --package codex \ + --allow-missing-native-component bwrap \ --output-dir "$OUTPUT_DIR" PACK_OUTPUT="${OUTPUT_DIR}/codex-npm-${CODEX_VERSION}.tgz" echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT" diff --git a/codex-cli/scripts/build_npm_package.py b/codex-cli/scripts/build_npm_package.py index eda6c26152..c38fed68d9 100755 --- a/codex-cli/scripts/build_npm_package.py +++ b/codex-cli/scripts/build_npm_package.py @@ -69,8 +69,8 @@ PACKAGE_EXPANSIONS: dict[str, list[str]] = { PACKAGE_NATIVE_COMPONENTS: dict[str, list[str]] = { "codex": [], - "codex-linux-x64": ["codex", "rg"], - "codex-linux-arm64": ["codex", "rg"], + "codex-linux-x64": ["bwrap", "codex", "rg"], + "codex-linux-arm64": ["bwrap", "codex", "rg"], "codex-darwin-x64": ["codex", "rg"], "codex-darwin-arm64": ["codex", "rg"], "codex-win32-x64": ["codex", "rg", "codex-windows-sandbox-setup", "codex-command-runner"], @@ -87,6 +87,7 @@ PACKAGE_TARGET_FILTERS: dict[str, str] = { PACKAGE_CHOICES = tuple(PACKAGE_NATIVE_COMPONENTS) COMPONENT_DEST_DIR: dict[str, str] = { + "bwrap": "codex-resources", "codex": "codex", "codex-responses-api-proxy": "codex-responses-api-proxy", "codex-windows-sandbox-setup": "codex", @@ -137,6 +138,16 @@ def parse_args() -> argparse.Namespace: type=Path, help="Directory containing pre-installed native binaries to bundle (vendor root).", ) + parser.add_argument( + "--allow-missing-native-component", + dest="allow_missing_native_components", + action="append", + default=[], + help=( + "Native component that may be absent from --vendor-src. Intended for CI " + "compatibility with older artifact workflows; releases should not use this." + ), + ) return parser.parse_args() @@ -177,6 +188,7 @@ def main() -> int: staging_dir, native_components, target_filter={target_filter} if target_filter else None, + allow_missing_components=set(args.allow_missing_native_components), ) if release_version: @@ -365,12 +377,14 @@ def copy_native_binaries( staging_dir: Path, components: list[str], target_filter: set[str] | None = None, + allow_missing_components: set[str] | None = None, ) -> None: vendor_src = vendor_src.resolve() if not vendor_src.exists(): raise RuntimeError(f"Vendor source directory not found: {vendor_src}") components_set = {component for component in components if component in COMPONENT_DEST_DIR} + allow_missing_components = allow_missing_components or set() if not components_set: return @@ -399,6 +413,8 @@ def copy_native_binaries( src_component_dir = target_dir / dest_dir_name if not src_component_dir.exists(): + if component in allow_missing_components: + continue raise RuntimeError( f"Missing native component '{component}' in vendor source: {src_component_dir}" ) diff --git a/codex-cli/scripts/install_native_deps.py b/codex-cli/scripts/install_native_deps.py index 58fbd370fc..19d0af5bae 100755 --- a/codex-cli/scripts/install_native_deps.py +++ b/codex-cli/scripts/install_native_deps.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Install Codex native binaries (Rust CLI plus ripgrep helpers).""" +"""Install Codex native binaries (Rust CLI, bwrap, and ripgrep helpers).""" import argparse from contextlib import contextmanager @@ -42,8 +42,15 @@ class BinaryComponent: WINDOWS_TARGETS = tuple(target for target in BINARY_TARGETS if "windows" in target) +LINUX_TARGETS = tuple(target for target in BINARY_TARGETS if "linux" in target) BINARY_COMPONENTS = { + "bwrap": BinaryComponent( + artifact_prefix="bwrap", + dest_dir="codex-resources", + binary_basename="bwrap", + targets=LINUX_TARGETS, + ), "codex": BinaryComponent( artifact_prefix="codex", dest_dir="codex", diff --git a/scripts/install/install.sh b/scripts/install/install.sh index 8c225e4d3b..2fc585d7e9 100755 --- a/scripts/install/install.sh +++ b/scripts/install/install.sh @@ -596,6 +596,10 @@ install_release() { cp "$vendor_root/path/rg" "$stage_release/codex-resources/rg" chmod 0755 "$stage_release/codex" chmod 0755 "$stage_release/codex-resources/rg" + if [ -f "$vendor_root/codex-resources/bwrap" ]; then + cp "$vendor_root/codex-resources/bwrap" "$stage_release/codex-resources/bwrap" + chmod 0755 "$stage_release/codex-resources/bwrap" + fi if [ -e "$release_dir" ] || [ -L "$release_dir" ]; then rm -rf "$release_dir" @@ -611,7 +615,11 @@ release_dir_is_complete() { [ -d "$release_dir" ] && [ -x "$release_dir/codex" ] && [ -x "$release_dir/codex-resources/rg" ] && - [ "$(basename "$release_dir")" = "$expected_version-$expected_target" ] + [ "$(basename "$release_dir")" = "$expected_version-$expected_target" ] && + case "$expected_target" in + *linux*) [ -x "$release_dir/codex-resources/bwrap" ] ;; + *) true ;; + esac } update_current_link() { diff --git a/scripts/stage_npm_packages.py b/scripts/stage_npm_packages.py index 5bbee755e9..2a0cd0028d 100755 --- a/scripts/stage_npm_packages.py +++ b/scripts/stage_npm_packages.py @@ -58,6 +58,16 @@ def parse_args() -> argparse.Namespace: action="store_true", help="Retain temporary staging directories instead of deleting them.", ) + parser.add_argument( + "--allow-missing-native-component", + dest="allow_missing_native_components", + action="append", + default=[], + help=( + "Native component that may be absent from reused workflow artifacts. " + "Intended for CI compatibility only; release staging should not use this." + ), + ) return parser.parse_args() @@ -147,6 +157,8 @@ def main() -> int: packages = expand_packages(list(args.packages)) native_components = collect_native_components(packages) + allow_missing_native_components = set(args.allow_missing_native_components) + native_components_to_install = native_components - allow_missing_native_components vendor_temp_root: Path | None = None vendor_src: Path | None = None @@ -155,12 +167,12 @@ def main() -> int: final_messages = [] try: - if native_components: + if native_components_to_install: workflow_url, resolved_head_sha = resolve_workflow_url( args.release_version, args.workflow_url ) vendor_temp_root = Path(tempfile.mkdtemp(prefix="npm-native-", dir=runner_temp)) - install_native_components(workflow_url, native_components, vendor_temp_root) + install_native_components(workflow_url, native_components_to_install, vendor_temp_root) vendor_src = vendor_temp_root / "vendor" if resolved_head_sha: @@ -185,6 +197,9 @@ def main() -> int: if vendor_src is not None: cmd.extend(["--vendor-src", str(vendor_src)]) + for component in sorted(allow_missing_native_components): + cmd.extend(["--allow-missing-native-component", component]) + try: run_command(cmd) finally: