release/npm: bundle standalone bwrap on Linux (#21257)

This commit is contained in:
Michael Bolin
2026-05-05 18:21:52 -07:00
committed by GitHub
parent db22c91e61
commit a736cb55a2
7 changed files with 72 additions and 17 deletions

View File

@@ -84,6 +84,18 @@
}
}
},
"bwrap": {
"platforms": {
"linux-x86_64": {
"regex": "^bwrap-x86_64-unknown-linux-musl\\.zst$",
"path": "bwrap"
},
"linux-aarch64": {
"regex": "^bwrap-aarch64-unknown-linux-musl\\.zst$",
"path": "bwrap"
}
}
},
"codex-command-runner": {
"platforms": {
"windows-x86_64": {

View File

@@ -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"

View File

@@ -96,7 +96,7 @@ jobs:
target: x86_64-unknown-linux-musl
bundle: primary
artifact_name: x86_64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy bwrap"
build_dmg: "false"
- runner: ubuntu-24.04
target: x86_64-unknown-linux-musl
@@ -108,7 +108,7 @@ jobs:
target: aarch64-unknown-linux-musl
bundle: primary
artifact_name: aarch64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy"
binaries: "codex codex-responses-api-proxy bwrap"
build_dmg: "false"
- runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
@@ -255,7 +255,7 @@ jobs:
with:
target: ${{ matrix.target }}
- if: ${{ contains(matrix.target, 'linux') }}
- if: ${{ contains(matrix.target, 'linux') && matrix.bundle == 'primary' }}
name: Build bwrap and export digest
shell: bash
run: |
@@ -296,7 +296,7 @@ jobs:
with:
target: ${{ matrix.target }}
artifacts-dir: ${{ github.workspace }}/codex-rs/target/${{ matrix.target }}/release
binaries: ${{ matrix.binaries }} bwrap
binaries: ${{ matrix.binaries }}
- if: ${{ runner.os == 'macOS' }}
name: MacOS code signing (binaries)
@@ -379,12 +379,6 @@ jobs:
fi
done
if [[ "${{ matrix.target }}" == *linux* && "${{ matrix.bundle }}" == "primary" ]]; then
cp "target/${{ matrix.target }}/release/bwrap" "$dest/bwrap-${{ matrix.target }}"
cp "target/${{ matrix.target }}/release/bwrap.sigstore" \
"$dest/bwrap-${{ matrix.target }}.sigstore"
fi
if [[ "${{ matrix.build_dmg }}" == "true" ]]; then
cp target/${{ matrix.target }}/release/codex-${{ matrix.target }}.dmg "$dest/codex-${{ matrix.target }}.dmg"
fi

View File

@@ -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}"
)

View File

@@ -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",
@@ -135,7 +142,7 @@ def parse_args() -> argparse.Namespace:
choices=tuple(list(BINARY_COMPONENTS) + ["rg"]),
help=(
"Limit installation to the specified components."
" May be repeated. Defaults to codex, codex-windows-sandbox-setup,"
" May be repeated. Defaults to bwrap, codex, codex-windows-sandbox-setup,"
" codex-command-runner, and rg."
),
)
@@ -159,6 +166,7 @@ def main() -> int:
vendor_dir.mkdir(parents=True, exist_ok=True)
components = args.components or [
"bwrap",
"codex",
"codex-windows-sandbox-setup",
"codex-command-runner",

View File

@@ -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() {

View File

@@ -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: