diff --git a/sdk/python/scripts/update_sdk_artifacts.py b/sdk/python/scripts/update_sdk_artifacts.py index 0d0e739c78..d331b8238f 100755 --- a/sdk/python/scripts/update_sdk_artifacts.py +++ b/sdk/python/scripts/update_sdk_artifacts.py @@ -60,6 +60,10 @@ def staged_runtime_bin_path(root: Path) -> Path: return root / "src" / "codex_cli_bin" / "bin" / runtime_binary_name() +def staged_runtime_resource_path(root: Path, resource: Path) -> Path: + return root / "src" / "codex_cli_bin" / "bin" / resource.name + + def run(cmd: list[str], cwd: Path) -> None: subprocess.run(cmd, cwd=str(cwd), check=True) @@ -211,6 +215,7 @@ def stage_python_runtime_package( codex_version: str, binary_path: Path, platform_tag: str | None = None, + resource_binaries: Sequence[Path] = (), ) -> Path: package_version = normalize_codex_version(codex_version) _copy_package_tree(python_runtime_root(), staging_dir) @@ -230,6 +235,13 @@ def stage_python_runtime_package( out_bin.chmod( out_bin.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH ) + for resource_binary in resource_binaries: + out_resource = staged_runtime_resource_path(staging_dir, resource_binary) + shutil.copy2(resource_binary, out_resource) + if not _is_windows(): + out_resource.chmod( + out_resource.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) return staging_dir @@ -632,7 +644,9 @@ class PublicFieldSpec: class CliOps: generate_types: Callable[[], None] stage_python_sdk_package: Callable[[Path, str], Path] - stage_python_runtime_package: Callable[[Path, str, Path, str | None], Path] + stage_python_runtime_package: Callable[ + [Path, str, Path, str | None, Sequence[Path]], Path + ] current_sdk_version: Callable[[], str] @@ -1047,6 +1061,13 @@ def build_parser() -> argparse.ArgumentParser: "macosx_11_0_arm64 or musllinux_1_1_x86_64." ), ) + stage_runtime_parser.add_argument( + "--resource-binary", + action="append", + default=[], + type=Path, + help="Additional executable to package beside the codex runtime binary.", + ) return parser @@ -1101,6 +1122,7 @@ def run_command(args: argparse.Namespace, ops: CliOps) -> None: codex_version, args.runtime_binary.resolve(), args.platform_tag, + tuple(path.resolve() for path in args.resource_binary), ) diff --git a/sdk/python/tests/test_artifact_workflow_and_binaries.py b/sdk/python/tests/test_artifact_workflow_and_binaries.py index a30582517a..fc01d4beb6 100644 --- a/sdk/python/tests/test_artifact_workflow_and_binaries.py +++ b/sdk/python/tests/test_artifact_workflow_and_binaries.py @@ -8,6 +8,7 @@ import sys import tomllib import urllib.error from pathlib import Path +from typing import Sequence import pytest @@ -350,6 +351,34 @@ def test_stage_runtime_release_can_pin_wheel_platform_tag(tmp_path: Path) -> Non assert 'platform-tag = "musllinux_1_1_x86_64"' in pyproject +def test_stage_runtime_release_copies_resource_binaries(tmp_path: Path) -> None: + script = _load_update_script_module() + fake_binary = tmp_path / script.runtime_binary_name() + command_runner = tmp_path / "codex-command-runner.exe" + setup = tmp_path / "codex-windows-sandbox-setup.exe" + fake_binary.write_text("fake codex\n") + command_runner.write_text("fake command runner\n") + setup.write_text("fake setup\n") + + staged = script.stage_python_runtime_package( + tmp_path / "runtime-stage", + "1.2.3", + fake_binary, + resource_binaries=(command_runner, setup), + ) + + assert { + path.relative_to( + staged / "src" / "codex_cli_bin" / "bin" + ).as_posix(): path.read_text() + for path in (staged / "src" / "codex_cli_bin" / "bin").iterdir() + } == { + script.runtime_binary_name(): "fake codex\n", + "codex-command-runner.exe": "fake command runner\n", + "codex-windows-sandbox-setup.exe": "fake setup\n", + } + + def test_stage_sdk_release_injects_exact_runtime_pin(tmp_path: Path) -> None: script = _load_update_script_module() staged = script.stage_python_sdk_package( @@ -436,6 +465,7 @@ def test_stage_sdk_runs_type_generation_before_staging(tmp_path: Path) -> None: _runtime_version: str, _runtime_binary: Path, _platform_tag: str | None, + _resource_binaries: Sequence[Path], ) -> Path: raise AssertionError("runtime staging should not run for stage-sdk") @@ -476,7 +506,11 @@ def test_stage_sdk_rejects_mismatched_legacy_versions(tmp_path: Path) -> None: def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> None: script = _load_update_script_module() fake_binary = tmp_path / script.runtime_binary_name() + command_runner = tmp_path / "codex-command-runner.exe" + setup = tmp_path / "codex-windows-sandbox-setup.exe" fake_binary.write_text("fake codex\n") + command_runner.write_text("fake command runner\n") + setup.write_text("fake setup\n") calls: list[str] = [] args = script.parse_args( [ @@ -487,6 +521,10 @@ def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> "rust-v0.116.0-alpha.1", "--platform-tag", "musllinux_1_1_x86_64", + "--resource-binary", + str(command_runner), + "--resource-binary", + str(setup), ] ) @@ -501,8 +539,12 @@ def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> codex_version: str, _runtime_binary: Path, platform_tag: str | None, + resource_binaries: Sequence[Path], ) -> Path: - calls.append(f"stage_runtime:{codex_version}:{platform_tag}") + calls.append( + f"stage_runtime:{codex_version}:{platform_tag}:" + f"{','.join(path.name for path in resource_binaries)}" + ) return tmp_path / "runtime-stage" def fake_current_sdk_version() -> str: @@ -517,7 +559,10 @@ def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> script.run_command(args, ops) - assert calls == ["stage_runtime:0.116.0a1:musllinux_1_1_x86_64"] + assert calls == [ + "stage_runtime:0.116.0a1:musllinux_1_1_x86_64:" + "codex-command-runner.exe,codex-windows-sandbox-setup.exe" + ] def test_default_runtime_is_resolved_from_installed_runtime_package(