mirror of
https://github.com/openai/codex.git
synced 2026-05-03 02:46:39 +00:00
Compare commits
1 Commits
jchu/proac
...
dev/shaqay
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f412b39118 |
@@ -1,9 +1,17 @@
|
|||||||
# Codex CLI Runtime for Python SDK
|
# Codex CLI Runtime for Python SDK
|
||||||
|
|
||||||
Platform-specific runtime package consumed by the published `codex-app-server-sdk`.
|
Platform-specific runtime package consumed by the published `openai-codex` SDK.
|
||||||
|
|
||||||
This package is staged during release so the SDK can pin an exact Codex CLI
|
This package is staged during release so the SDK can pin an exact Codex CLI
|
||||||
version without checking platform binaries into the repo.
|
version without checking platform binaries into the repo. The distribution name
|
||||||
|
is `openai-codex-cli-bin`, while the import module remains `codex_cli_bin`.
|
||||||
|
|
||||||
`codex-cli-bin` is intentionally wheel-only. Do not build or publish an sdist
|
`openai-codex-cli-bin` is intentionally wheel-only. Do not build or publish an
|
||||||
for this package.
|
sdist for this package.
|
||||||
|
|
||||||
|
Expected wheel contents:
|
||||||
|
|
||||||
|
- macOS/Linux: `codex_cli_bin/bin/codex`
|
||||||
|
- Windows: `codex_cli_bin/bin/codex.exe`,
|
||||||
|
`codex_cli_bin/bin/codex-command-runner.exe`, and
|
||||||
|
`codex_cli_bin/bin/codex-windows-sandbox-setup.exe`
|
||||||
|
|||||||
@@ -1,15 +1,34 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
||||||
|
|
||||||
|
PLATFORM_TAG_BY_TARGET = {
|
||||||
|
"aarch64-apple-darwin": "macosx_11_0_arm64",
|
||||||
|
"x86_64-apple-darwin": "macosx_10_12_x86_64",
|
||||||
|
"aarch64-unknown-linux-musl": "musllinux_1_2_aarch64",
|
||||||
|
"x86_64-unknown-linux-musl": "musllinux_1_2_x86_64",
|
||||||
|
"aarch64-pc-windows-msvc": "win_arm64",
|
||||||
|
"x86_64-pc-windows-msvc": "win_amd64",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RuntimeBuildHook(BuildHookInterface):
|
class RuntimeBuildHook(BuildHookInterface):
|
||||||
def initialize(self, version: str, build_data: dict[str, object]) -> None:
|
def initialize(self, version: str, build_data: dict[str, object]) -> None:
|
||||||
del version
|
del version
|
||||||
if self.target_name == "sdist":
|
if self.target_name == "sdist":
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"codex-cli-bin is wheel-only; build and publish platform wheels only."
|
"openai-codex-cli-bin is wheel-only; build and publish platform wheels only."
|
||||||
)
|
)
|
||||||
|
|
||||||
build_data["pure_python"] = False
|
build_data["pure_python"] = False
|
||||||
build_data["infer_tag"] = True
|
target = os.environ.get("CODEX_PYTHON_RUNTIME_TARGET")
|
||||||
|
if target is None:
|
||||||
|
build_data["infer_tag"] = True
|
||||||
|
return
|
||||||
|
|
||||||
|
platform_tag = PLATFORM_TAG_BY_TARGET.get(target)
|
||||||
|
if platform_tag is None:
|
||||||
|
raise RuntimeError(f"Unsupported Codex Python runtime target: {target}")
|
||||||
|
build_data["tag"] = f"py3-none-{platform_tag}"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ requires = ["hatchling>=1.24.0"]
|
|||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "codex-cli-bin"
|
name = "openai-codex-cli-bin"
|
||||||
version = "0.0.0-dev"
|
version = "0.0.0-dev"
|
||||||
description = "Pinned Codex CLI runtime for the Python SDK"
|
description = "Pinned Codex CLI runtime for the Python SDK"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -3,12 +3,25 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PACKAGE_NAME = "codex-cli-bin"
|
PACKAGE_NAME = "openai-codex-cli-bin"
|
||||||
|
|
||||||
|
|
||||||
|
def bundled_bin_dir() -> Path:
|
||||||
|
return Path(__file__).resolve().parent / "bin"
|
||||||
|
|
||||||
|
|
||||||
|
def bundled_runtime_files() -> tuple[Path, ...]:
|
||||||
|
names = (
|
||||||
|
("codex.exe", "codex-command-runner.exe", "codex-windows-sandbox-setup.exe")
|
||||||
|
if os.name == "nt"
|
||||||
|
else ("codex",)
|
||||||
|
)
|
||||||
|
return tuple(bundled_bin_dir() / name for name in names)
|
||||||
|
|
||||||
|
|
||||||
def bundled_codex_path() -> Path:
|
def bundled_codex_path() -> Path:
|
||||||
exe = "codex.exe" if os.name == "nt" else "codex"
|
exe = "codex.exe" if os.name == "nt" else "codex"
|
||||||
path = Path(__file__).resolve().parent / "bin" / exe
|
path = bundled_bin_dir() / exe
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
f"{PACKAGE_NAME} is installed but missing its packaged codex binary at {path}"
|
f"{PACKAGE_NAME} is installed but missing its packaged codex binary at {path}"
|
||||||
@@ -16,4 +29,9 @@ def bundled_codex_path() -> Path:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["PACKAGE_NAME", "bundled_codex_path"]
|
__all__ = [
|
||||||
|
"PACKAGE_NAME",
|
||||||
|
"bundled_bin_dir",
|
||||||
|
"bundled_codex_path",
|
||||||
|
"bundled_runtime_files",
|
||||||
|
]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import urllib.request
|
|||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PACKAGE_NAME = "codex-cli-bin"
|
PACKAGE_NAME = "openai-codex-cli-bin"
|
||||||
PINNED_RUNTIME_VERSION = "0.116.0-alpha.1"
|
PINNED_RUNTIME_VERSION = "0.116.0-alpha.1"
|
||||||
REPO_SLUG = "openai/codex"
|
REPO_SLUG = "openai/codex"
|
||||||
|
|
||||||
@@ -39,17 +39,20 @@ def ensure_runtime_package_installed(
|
|||||||
installed_version = _installed_runtime_version(python_executable)
|
installed_version = _installed_runtime_version(python_executable)
|
||||||
normalized_requested = _normalized_package_version(requested_version)
|
normalized_requested = _normalized_package_version(requested_version)
|
||||||
|
|
||||||
if installed_version is not None and _normalized_package_version(installed_version) == normalized_requested:
|
if (
|
||||||
|
installed_version is not None
|
||||||
|
and _normalized_package_version(installed_version) == normalized_requested
|
||||||
|
):
|
||||||
return requested_version
|
return requested_version
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory(prefix="codex-python-runtime-") as temp_root_str:
|
with tempfile.TemporaryDirectory(prefix="codex-python-runtime-") as temp_root_str:
|
||||||
temp_root = Path(temp_root_str)
|
temp_root = Path(temp_root_str)
|
||||||
archive_path = _download_release_archive(requested_version, temp_root)
|
archive_path = _download_release_archive(requested_version, temp_root)
|
||||||
runtime_binary = _extract_runtime_binary(archive_path, temp_root)
|
runtime_bundle_dir = _extract_runtime_bundle(archive_path, temp_root)
|
||||||
staged_runtime_dir = _stage_runtime_package(
|
staged_runtime_dir = _stage_runtime_package(
|
||||||
sdk_python_dir,
|
sdk_python_dir,
|
||||||
requested_version,
|
requested_version,
|
||||||
runtime_binary,
|
runtime_bundle_dir,
|
||||||
temp_root / "runtime-stage",
|
temp_root / "runtime-stage",
|
||||||
)
|
)
|
||||||
_install_runtime_package(python_executable, staged_runtime_dir, install_target)
|
_install_runtime_package(python_executable, staged_runtime_dir, install_target)
|
||||||
@@ -61,7 +64,10 @@ def ensure_runtime_package_installed(
|
|||||||
importlib.invalidate_caches()
|
importlib.invalidate_caches()
|
||||||
|
|
||||||
installed_version = _installed_runtime_version(python_executable)
|
installed_version = _installed_runtime_version(python_executable)
|
||||||
if installed_version is None or _normalized_package_version(installed_version) != normalized_requested:
|
if (
|
||||||
|
installed_version is None
|
||||||
|
or _normalized_package_version(installed_version) != normalized_requested
|
||||||
|
):
|
||||||
raise RuntimeSetupError(
|
raise RuntimeSetupError(
|
||||||
f"Expected {PACKAGE_NAME} {requested_version} in {python_executable}, "
|
f"Expected {PACKAGE_NAME} {requested_version} in {python_executable}, "
|
||||||
f"but found {installed_version!r} after installation."
|
f"but found {installed_version!r} after installation."
|
||||||
@@ -105,7 +111,7 @@ def _installed_runtime_version(python_executable: str | Path) -> str | None:
|
|||||||
"try:\n"
|
"try:\n"
|
||||||
" from codex_cli_bin import bundled_codex_path\n"
|
" from codex_cli_bin import bundled_codex_path\n"
|
||||||
" bundled_codex_path()\n"
|
" bundled_codex_path()\n"
|
||||||
" print(json.dumps({'version': importlib.metadata.version('codex-cli-bin')}))\n"
|
f" print(json.dumps({{'version': importlib.metadata.version({PACKAGE_NAME!r})}}))\n"
|
||||||
"except Exception:\n"
|
"except Exception:\n"
|
||||||
" sys.exit(1)\n"
|
" sys.exit(1)\n"
|
||||||
)
|
)
|
||||||
@@ -172,7 +178,9 @@ def _download_release_archive(version: str, temp_root: Path) -> Path:
|
|||||||
metadata = _release_metadata(version)
|
metadata = _release_metadata(version)
|
||||||
assets = metadata.get("assets")
|
assets = metadata.get("assets")
|
||||||
if not isinstance(assets, list):
|
if not isinstance(assets, list):
|
||||||
raise RuntimeSetupError(f"Release rust-v{version} returned malformed assets metadata.")
|
raise RuntimeSetupError(
|
||||||
|
f"Release rust-v{version} returned malformed assets metadata."
|
||||||
|
)
|
||||||
asset = next(
|
asset = next(
|
||||||
(
|
(
|
||||||
item
|
item
|
||||||
@@ -198,7 +206,10 @@ def _download_release_archive(version: str, temp_root: Path) -> Path:
|
|||||||
headers=_github_api_headers("application/octet-stream"),
|
headers=_github_api_headers("application/octet-stream"),
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(request) as response, archive_path.open("wb") as fh:
|
with (
|
||||||
|
urllib.request.urlopen(request) as response,
|
||||||
|
archive_path.open("wb") as fh,
|
||||||
|
):
|
||||||
shutil.copyfileobj(response, fh)
|
shutil.copyfileobj(response, fh)
|
||||||
return archive_path
|
return archive_path
|
||||||
except urllib.error.HTTPError:
|
except urllib.error.HTTPError:
|
||||||
@@ -236,7 +247,7 @@ def _download_release_archive(version: str, temp_root: Path) -> Path:
|
|||||||
return archive_path
|
return archive_path
|
||||||
|
|
||||||
|
|
||||||
def _extract_runtime_binary(archive_path: Path, temp_root: Path) -> Path:
|
def _extract_runtime_bundle(archive_path: Path, temp_root: Path) -> Path:
|
||||||
extract_dir = temp_root / "extracted"
|
extract_dir = temp_root / "extracted"
|
||||||
extract_dir.mkdir(parents=True, exist_ok=True)
|
extract_dir.mkdir(parents=True, exist_ok=True)
|
||||||
if archive_path.name.endswith(".tar.gz"):
|
if archive_path.name.endswith(".tar.gz"):
|
||||||
@@ -249,38 +260,24 @@ def _extract_runtime_binary(archive_path: Path, temp_root: Path) -> Path:
|
|||||||
with zipfile.ZipFile(archive_path) as zip_file:
|
with zipfile.ZipFile(archive_path) as zip_file:
|
||||||
zip_file.extractall(extract_dir)
|
zip_file.extractall(extract_dir)
|
||||||
else:
|
else:
|
||||||
raise RuntimeSetupError(f"Unsupported release archive format: {archive_path.name}")
|
|
||||||
|
|
||||||
binary_name = runtime_binary_name()
|
|
||||||
archive_stem = archive_path.name.removesuffix(".tar.gz").removesuffix(".zip")
|
|
||||||
candidates = [
|
|
||||||
path
|
|
||||||
for path in extract_dir.rglob("*")
|
|
||||||
if path.is_file()
|
|
||||||
and (
|
|
||||||
path.name == binary_name
|
|
||||||
or path.name == archive_stem
|
|
||||||
or path.name.startswith("codex-")
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if not candidates:
|
|
||||||
raise RuntimeSetupError(
|
raise RuntimeSetupError(
|
||||||
f"Failed to find {binary_name} in extracted runtime archive {archive_path.name}."
|
f"Unsupported release archive format: {archive_path.name}"
|
||||||
)
|
)
|
||||||
return candidates[0]
|
|
||||||
|
return extract_dir
|
||||||
|
|
||||||
|
|
||||||
def _stage_runtime_package(
|
def _stage_runtime_package(
|
||||||
sdk_python_dir: Path,
|
sdk_python_dir: Path,
|
||||||
runtime_version: str,
|
runtime_version: str,
|
||||||
runtime_binary: Path,
|
runtime_bundle_dir: Path,
|
||||||
staging_dir: Path,
|
staging_dir: Path,
|
||||||
) -> Path:
|
) -> Path:
|
||||||
script_module = _load_update_script_module(sdk_python_dir)
|
script_module = _load_update_script_module(sdk_python_dir)
|
||||||
return script_module.stage_python_runtime_package( # type: ignore[no-any-return]
|
return script_module.stage_python_runtime_package( # type: ignore[no-any-return]
|
||||||
staging_dir,
|
staging_dir,
|
||||||
runtime_version,
|
runtime_version,
|
||||||
runtime_binary.resolve(),
|
runtime_bundle_dir.resolve(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ from dataclasses import dataclass
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Sequence, get_args, get_origin
|
from typing import Any, Callable, Sequence, get_args, get_origin
|
||||||
|
|
||||||
|
SDK_PKG_NAME = "openai-codex"
|
||||||
|
RUNTIME_PKG_NAME = "openai-codex-cli-bin"
|
||||||
|
|
||||||
|
|
||||||
def repo_root() -> Path:
|
def repo_root() -> Path:
|
||||||
return Path(__file__).resolve().parents[3]
|
return Path(__file__).resolve().parents[3]
|
||||||
@@ -45,16 +48,30 @@ def schema_root_dir() -> Path:
|
|||||||
return repo_root() / "codex-rs" / "app-server-protocol" / "schema" / "json"
|
return repo_root() / "codex-rs" / "app-server-protocol" / "schema" / "json"
|
||||||
|
|
||||||
|
|
||||||
def _is_windows() -> bool:
|
def _is_windows(system_name: str | None = None) -> bool:
|
||||||
return platform.system().lower().startswith("win")
|
return (system_name or platform.system()).lower().startswith("win")
|
||||||
|
|
||||||
|
|
||||||
def runtime_binary_name() -> str:
|
def runtime_binary_name(system_name: str | None = None) -> str:
|
||||||
return "codex.exe" if _is_windows() else "codex"
|
return "codex.exe" if _is_windows(system_name) else "codex"
|
||||||
|
|
||||||
|
|
||||||
|
def runtime_file_names(system_name: str | None = None) -> tuple[str, ...]:
|
||||||
|
if _is_windows(system_name):
|
||||||
|
return (
|
||||||
|
"codex.exe",
|
||||||
|
"codex-command-runner.exe",
|
||||||
|
"codex-windows-sandbox-setup.exe",
|
||||||
|
)
|
||||||
|
return ("codex",)
|
||||||
|
|
||||||
|
|
||||||
|
def staged_runtime_bin_dir(root: Path) -> Path:
|
||||||
|
return root / "src" / "codex_cli_bin" / "bin"
|
||||||
|
|
||||||
|
|
||||||
def staged_runtime_bin_path(root: Path) -> Path:
|
def staged_runtime_bin_path(root: Path) -> Path:
|
||||||
return root / "src" / "codex_cli_bin" / "bin" / runtime_binary_name()
|
return staged_runtime_bin_dir(root) / runtime_binary_name()
|
||||||
|
|
||||||
|
|
||||||
def run(cmd: list[str], cwd: Path) -> None:
|
def run(cmd: list[str], cwd: Path) -> None:
|
||||||
@@ -110,6 +127,39 @@ def _rewrite_project_version(pyproject_text: str, version: str) -> str:
|
|||||||
return updated
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
def _rewrite_project_name(pyproject_text: str, name: str) -> str:
|
||||||
|
updated, count = re.subn(
|
||||||
|
r'^name = "[^"]+"$',
|
||||||
|
f'name = "{name}"',
|
||||||
|
pyproject_text,
|
||||||
|
count=1,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
if count != 1:
|
||||||
|
raise RuntimeError("Could not rewrite project name in pyproject.toml")
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_python_package_version(version: str) -> str:
|
||||||
|
stripped = version.strip()
|
||||||
|
if re.fullmatch(r"\d+\.\d+\.\d+(?:a\d+|b\d+|\.dev\d+)?", stripped):
|
||||||
|
return stripped
|
||||||
|
|
||||||
|
prerelease_match = re.fullmatch(
|
||||||
|
r"(\d+\.\d+\.\d+)-(alpha|beta)\.(\d+)",
|
||||||
|
stripped,
|
||||||
|
)
|
||||||
|
if prerelease_match is not None:
|
||||||
|
base, prerelease, number = prerelease_match.groups()
|
||||||
|
marker = "a" if prerelease == "alpha" else "b"
|
||||||
|
return f"{base}{marker}{number}"
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
"Unsupported Python package version. Expected x.y.z, x.y.z-alpha.n, "
|
||||||
|
f"x.y.z-beta.n, or an already-normalized PEP 440 version; got {version!r}."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _rewrite_sdk_runtime_dependency(pyproject_text: str, runtime_version: str) -> str:
|
def _rewrite_sdk_runtime_dependency(pyproject_text: str, runtime_version: str) -> str:
|
||||||
match = re.search(r"^dependencies = \[(.*?)\]$", pyproject_text, flags=re.MULTILINE)
|
match = re.search(r"^dependencies = \[(.*?)\]$", pyproject_text, flags=re.MULTILINE)
|
||||||
if match is None:
|
if match is None:
|
||||||
@@ -118,15 +168,46 @@ def _rewrite_sdk_runtime_dependency(pyproject_text: str, runtime_version: str) -
|
|||||||
)
|
)
|
||||||
|
|
||||||
raw_items = [item.strip() for item in match.group(1).split(",") if item.strip()]
|
raw_items = [item.strip() for item in match.group(1).split(",") if item.strip()]
|
||||||
raw_items = [item for item in raw_items if "codex-cli-bin" not in item]
|
raw_items = [
|
||||||
raw_items.append(f'"codex-cli-bin=={runtime_version}"')
|
item
|
||||||
|
for item in raw_items
|
||||||
|
if "codex-cli-bin" not in item and RUNTIME_PKG_NAME not in item
|
||||||
|
]
|
||||||
|
raw_items.append(f'"{RUNTIME_PKG_NAME}=={runtime_version}"')
|
||||||
replacement = "dependencies = [\n " + ",\n ".join(raw_items) + ",\n]"
|
replacement = "dependencies = [\n " + ",\n ".join(raw_items) + ",\n]"
|
||||||
return pyproject_text[: match.start()] + replacement + pyproject_text[match.end() :]
|
return pyproject_text[: match.start()] + replacement + pyproject_text[match.end() :]
|
||||||
|
|
||||||
|
|
||||||
|
def _rewrite_sdk_init_version(init_text: str, sdk_version: str) -> str:
|
||||||
|
updated, count = re.subn(
|
||||||
|
r'^__version__ = "[^"]+"$',
|
||||||
|
f'__version__ = "{sdk_version}"',
|
||||||
|
init_text,
|
||||||
|
count=1,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
if count != 1:
|
||||||
|
raise RuntimeError("Could not rewrite SDK __version__")
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
def _rewrite_sdk_client_version(client_text: str, sdk_version: str) -> str:
|
||||||
|
updated, count = re.subn(
|
||||||
|
r'client_version: str = "[^"]+"',
|
||||||
|
f'client_version: str = "{sdk_version}"',
|
||||||
|
client_text,
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
|
if count != 1:
|
||||||
|
raise RuntimeError("Could not rewrite AppServerConfig.client_version")
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
def stage_python_sdk_package(
|
def stage_python_sdk_package(
|
||||||
staging_dir: Path, sdk_version: str, runtime_version: str
|
staging_dir: Path, sdk_version: str, runtime_version: str
|
||||||
) -> Path:
|
) -> Path:
|
||||||
|
sdk_version = normalize_python_package_version(sdk_version)
|
||||||
|
runtime_version = normalize_python_package_version(runtime_version)
|
||||||
_copy_package_tree(sdk_root(), staging_dir)
|
_copy_package_tree(sdk_root(), staging_dir)
|
||||||
sdk_bin_dir = staging_dir / "src" / "codex_app_server" / "bin"
|
sdk_bin_dir = staging_dir / "src" / "codex_app_server" / "bin"
|
||||||
if sdk_bin_dir.exists():
|
if sdk_bin_dir.exists():
|
||||||
@@ -134,32 +215,88 @@ def stage_python_sdk_package(
|
|||||||
|
|
||||||
pyproject_path = staging_dir / "pyproject.toml"
|
pyproject_path = staging_dir / "pyproject.toml"
|
||||||
pyproject_text = pyproject_path.read_text()
|
pyproject_text = pyproject_path.read_text()
|
||||||
|
pyproject_text = _rewrite_project_name(pyproject_text, SDK_PKG_NAME)
|
||||||
pyproject_text = _rewrite_project_version(pyproject_text, sdk_version)
|
pyproject_text = _rewrite_project_version(pyproject_text, sdk_version)
|
||||||
pyproject_text = _rewrite_sdk_runtime_dependency(pyproject_text, runtime_version)
|
pyproject_text = _rewrite_sdk_runtime_dependency(pyproject_text, runtime_version)
|
||||||
pyproject_path.write_text(pyproject_text)
|
pyproject_path.write_text(pyproject_text)
|
||||||
|
|
||||||
|
init_path = staging_dir / "src" / "codex_app_server" / "__init__.py"
|
||||||
|
init_path.write_text(_rewrite_sdk_init_version(init_path.read_text(), sdk_version))
|
||||||
|
|
||||||
|
client_path = staging_dir / "src" / "codex_app_server" / "client.py"
|
||||||
|
client_path.write_text(
|
||||||
|
_rewrite_sdk_client_version(client_path.read_text(), sdk_version)
|
||||||
|
)
|
||||||
return staging_dir
|
return staging_dir
|
||||||
|
|
||||||
|
|
||||||
def stage_python_runtime_package(
|
def stage_python_runtime_package(
|
||||||
staging_dir: Path, runtime_version: str, binary_path: Path
|
staging_dir: Path, runtime_version: str, runtime_bundle_dir: Path
|
||||||
) -> Path:
|
) -> Path:
|
||||||
|
runtime_version = normalize_python_package_version(runtime_version)
|
||||||
_copy_package_tree(python_runtime_root(), staging_dir)
|
_copy_package_tree(python_runtime_root(), staging_dir)
|
||||||
|
|
||||||
pyproject_path = staging_dir / "pyproject.toml"
|
pyproject_path = staging_dir / "pyproject.toml"
|
||||||
pyproject_path.write_text(
|
pyproject_text = _rewrite_project_name(pyproject_path.read_text(), RUNTIME_PKG_NAME)
|
||||||
_rewrite_project_version(pyproject_path.read_text(), runtime_version)
|
pyproject_text = _rewrite_project_version(pyproject_text, runtime_version)
|
||||||
)
|
pyproject_path.write_text(pyproject_text)
|
||||||
|
|
||||||
out_bin = staged_runtime_bin_path(staging_dir)
|
out_bin_dir = staged_runtime_bin_dir(staging_dir)
|
||||||
out_bin.parent.mkdir(parents=True, exist_ok=True)
|
out_bin_dir.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(binary_path, out_bin)
|
for runtime_file_name in runtime_file_names():
|
||||||
if not _is_windows():
|
source = _find_runtime_bundle_file(runtime_bundle_dir, runtime_file_name)
|
||||||
out_bin.chmod(
|
out_path = out_bin_dir / runtime_file_name
|
||||||
out_bin.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
shutil.copy2(source, out_path)
|
||||||
)
|
if not _is_windows():
|
||||||
|
out_path.chmod(
|
||||||
|
out_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
||||||
|
)
|
||||||
return staging_dir
|
return staging_dir
|
||||||
|
|
||||||
|
|
||||||
|
def _find_runtime_bundle_file(runtime_bundle_dir: Path, destination_name: str) -> Path:
|
||||||
|
if not runtime_bundle_dir.is_dir():
|
||||||
|
raise RuntimeError(f"Runtime bundle directory not found: {runtime_bundle_dir}")
|
||||||
|
|
||||||
|
exact = runtime_bundle_dir / destination_name
|
||||||
|
if exact.is_file():
|
||||||
|
return exact
|
||||||
|
|
||||||
|
patterns = {
|
||||||
|
"codex": re.compile(r"^codex-(?!responses-api-proxy)[^.]+$"),
|
||||||
|
"codex.exe": re.compile(
|
||||||
|
r"^codex-(?!command-runner|windows-sandbox-setup|responses-api-proxy).+\.exe$"
|
||||||
|
),
|
||||||
|
"codex-command-runner.exe": re.compile(r"^codex-command-runner-.+\.exe$"),
|
||||||
|
"codex-windows-sandbox-setup.exe": re.compile(
|
||||||
|
r"^codex-windows-sandbox-setup-.+\.exe$"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
pattern = patterns.get(destination_name)
|
||||||
|
candidates = (
|
||||||
|
[]
|
||||||
|
if pattern is None
|
||||||
|
else sorted(
|
||||||
|
path
|
||||||
|
for path in runtime_bundle_dir.iterdir()
|
||||||
|
if path.is_file() and pattern.fullmatch(path.name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if len(candidates) == 1:
|
||||||
|
return candidates[0]
|
||||||
|
if len(candidates) > 1:
|
||||||
|
candidate_names = ", ".join(path.name for path in candidates)
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Runtime bundle has multiple candidates for {destination_name}: "
|
||||||
|
f"{candidate_names}"
|
||||||
|
)
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Runtime bundle {runtime_bundle_dir} is missing required file "
|
||||||
|
f"{destination_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _flatten_string_enum_one_of(definition: dict[str, Any]) -> bool:
|
def _flatten_string_enum_one_of(definition: dict[str, Any]) -> bool:
|
||||||
branches = definition.get("oneOf")
|
branches = definition.get("oneOf")
|
||||||
if not isinstance(branches, list) or not branches:
|
if not isinstance(branches, list) or not branches:
|
||||||
@@ -928,7 +1065,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
stage_sdk_parser.add_argument(
|
stage_sdk_parser.add_argument(
|
||||||
"--runtime-version",
|
"--runtime-version",
|
||||||
required=True,
|
required=True,
|
||||||
help="Pinned codex-cli-bin version for the staged SDK package",
|
help=f"Pinned {RUNTIME_PKG_NAME} version for the staged SDK package",
|
||||||
)
|
)
|
||||||
stage_sdk_parser.add_argument(
|
stage_sdk_parser.add_argument(
|
||||||
"--sdk-version",
|
"--sdk-version",
|
||||||
@@ -945,9 +1082,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
help="Output directory for the staged runtime package",
|
help="Output directory for the staged runtime package",
|
||||||
)
|
)
|
||||||
stage_runtime_parser.add_argument(
|
stage_runtime_parser.add_argument(
|
||||||
"runtime_binary",
|
"runtime_bundle_dir",
|
||||||
type=Path,
|
type=Path,
|
||||||
help="Path to the codex binary to package for this platform",
|
help="Directory containing the Codex runtime files to package for this platform",
|
||||||
)
|
)
|
||||||
stage_runtime_parser.add_argument(
|
stage_runtime_parser.add_argument(
|
||||||
"--runtime-version",
|
"--runtime-version",
|
||||||
@@ -984,7 +1121,7 @@ def run_command(args: argparse.Namespace, ops: CliOps) -> None:
|
|||||||
ops.stage_python_runtime_package(
|
ops.stage_python_runtime_package(
|
||||||
args.staging_dir,
|
args.staging_dir,
|
||||||
args.runtime_version,
|
args.runtime_version,
|
||||||
args.runtime_binary.resolve(),
|
args.runtime_bundle_dir.resolve(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ from .retry import retry_on_overload
|
|||||||
|
|
||||||
ModelT = TypeVar("ModelT", bound=BaseModel)
|
ModelT = TypeVar("ModelT", bound=BaseModel)
|
||||||
ApprovalHandler = Callable[[str, JsonObject | None], JsonObject]
|
ApprovalHandler = Callable[[str, JsonObject | None], JsonObject]
|
||||||
RUNTIME_PKG_NAME = "codex-cli-bin"
|
SDK_PKG_NAME = "openai-codex"
|
||||||
|
RUNTIME_PKG_NAME = "openai-codex-cli-bin"
|
||||||
|
|
||||||
|
|
||||||
def _params_dict(
|
def _params_dict(
|
||||||
|
|||||||
@@ -29,7 +29,22 @@ def _load_runtime_setup_module():
|
|||||||
runtime_setup_path = ROOT / "_runtime_setup.py"
|
runtime_setup_path = ROOT / "_runtime_setup.py"
|
||||||
spec = importlib.util.spec_from_file_location("_runtime_setup", runtime_setup_path)
|
spec = importlib.util.spec_from_file_location("_runtime_setup", runtime_setup_path)
|
||||||
if spec is None or spec.loader is None:
|
if spec is None or spec.loader is None:
|
||||||
raise AssertionError(f"Failed to load runtime setup module: {runtime_setup_path}")
|
raise AssertionError(
|
||||||
|
f"Failed to load runtime setup module: {runtime_setup_path}"
|
||||||
|
)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
sys.modules[spec.name] = module
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def _load_runtime_package_module(package_root: Path):
|
||||||
|
runtime_init = package_root / "src" / "codex_cli_bin" / "__init__.py"
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"codex_cli_bin_under_test", runtime_init
|
||||||
|
)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise AssertionError(f"Failed to load runtime package module: {runtime_init}")
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
sys.modules[spec.name] = module
|
sys.modules[spec.name] = module
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
@@ -168,7 +183,9 @@ def test_examples_readme_matches_pinned_runtime_version() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_release_metadata_retries_without_invalid_auth(monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_release_metadata_retries_without_invalid_auth(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
runtime_setup = _load_runtime_setup_module()
|
runtime_setup = _load_runtime_setup_module()
|
||||||
authorizations: list[str | None] = []
|
authorizations: list[str | None] = []
|
||||||
|
|
||||||
@@ -198,6 +215,14 @@ def test_runtime_package_is_wheel_only_and_builds_platform_specific_wheels() ->
|
|||||||
)
|
)
|
||||||
hook_source = (ROOT.parent / "python-runtime" / "hatch_build.py").read_text()
|
hook_source = (ROOT.parent / "python-runtime" / "hatch_build.py").read_text()
|
||||||
hook_tree = ast.parse(hook_source)
|
hook_tree = ast.parse(hook_source)
|
||||||
|
platform_tag_assignment = next(
|
||||||
|
node
|
||||||
|
for node in hook_tree.body
|
||||||
|
if isinstance(node, ast.Assign)
|
||||||
|
and len(node.targets) == 1
|
||||||
|
and isinstance(node.targets[0], ast.Name)
|
||||||
|
and node.targets[0].id == "PLATFORM_TAG_BY_TARGET"
|
||||||
|
)
|
||||||
initialize_fn = next(
|
initialize_fn = next(
|
||||||
node
|
node
|
||||||
for node in ast.walk(hook_tree)
|
for node in ast.walk(hook_tree)
|
||||||
@@ -235,6 +260,7 @@ def test_runtime_package_is_wheel_only_and_builds_platform_specific_wheels() ->
|
|||||||
and isinstance(node.value, ast.Constant)
|
and isinstance(node.value, ast.Constant)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert pyproject["project"]["name"] == "openai-codex-cli-bin"
|
||||||
assert pyproject["tool"]["hatch"]["build"]["targets"]["wheel"] == {
|
assert pyproject["tool"]["hatch"]["build"]["targets"]["wheel"] == {
|
||||||
"packages": ["src/codex_cli_bin"],
|
"packages": ["src/codex_cli_bin"],
|
||||||
"include": ["src/codex_cli_bin/bin/**"],
|
"include": ["src/codex_cli_bin/bin/**"],
|
||||||
@@ -244,23 +270,51 @@ def test_runtime_package_is_wheel_only_and_builds_platform_specific_wheels() ->
|
|||||||
"hooks": {"custom": {}},
|
"hooks": {"custom": {}},
|
||||||
}
|
}
|
||||||
assert sdist_guard is not None
|
assert sdist_guard is not None
|
||||||
assert build_data_assignments == {"pure_python": False, "infer_tag": True}
|
assert build_data_assignments == {"pure_python": False}
|
||||||
|
assert ast.literal_eval(platform_tag_assignment.value) == {
|
||||||
|
"aarch64-apple-darwin": "macosx_11_0_arm64",
|
||||||
|
"x86_64-apple-darwin": "macosx_10_12_x86_64",
|
||||||
|
"aarch64-unknown-linux-musl": "musllinux_1_2_aarch64",
|
||||||
|
"x86_64-unknown-linux-musl": "musllinux_1_2_x86_64",
|
||||||
|
"aarch64-pc-windows-msvc": "win_arm64",
|
||||||
|
"x86_64-pc-windows-msvc": "win_amd64",
|
||||||
|
}
|
||||||
|
assert "CODEX_PYTHON_RUNTIME_TARGET" in hook_source
|
||||||
|
assert '"infer_tag"' in hook_source
|
||||||
|
assert '"tag"' in hook_source
|
||||||
|
|
||||||
|
|
||||||
def test_stage_runtime_release_copies_binary_and_sets_version(tmp_path: Path) -> None:
|
def test_python_release_version_normalization() -> None:
|
||||||
script = _load_update_script_module()
|
script = _load_update_script_module()
|
||||||
fake_binary = tmp_path / script.runtime_binary_name()
|
|
||||||
|
assert script.normalize_python_package_version("1.2.3") == "1.2.3"
|
||||||
|
assert script.normalize_python_package_version("1.2.3-alpha.4") == "1.2.3a4"
|
||||||
|
assert script.normalize_python_package_version("1.2.3-beta.5") == "1.2.3b5"
|
||||||
|
assert script.normalize_python_package_version("1.2.3a4") == "1.2.3a4"
|
||||||
|
assert script.normalize_python_package_version("0.0.0.dev0") == "0.0.0.dev0"
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError, match="Unsupported Python package version"):
|
||||||
|
script.normalize_python_package_version("1.2.3-rc.1")
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_runtime_release_copies_bundle_and_sets_version(tmp_path: Path) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
fake_binary = bundle_dir / script.runtime_binary_name()
|
||||||
fake_binary.write_text("fake codex\n")
|
fake_binary.write_text("fake codex\n")
|
||||||
|
|
||||||
staged = script.stage_python_runtime_package(
|
staged = script.stage_python_runtime_package(
|
||||||
tmp_path / "runtime-stage",
|
tmp_path / "runtime-stage",
|
||||||
"1.2.3",
|
"1.2.3-alpha.4",
|
||||||
fake_binary,
|
bundle_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert staged == tmp_path / "runtime-stage"
|
assert staged == tmp_path / "runtime-stage"
|
||||||
assert script.staged_runtime_bin_path(staged).read_text() == "fake codex\n"
|
assert script.staged_runtime_bin_path(staged).read_text() == "fake codex\n"
|
||||||
assert 'version = "1.2.3"' in (staged / "pyproject.toml").read_text()
|
pyproject = (staged / "pyproject.toml").read_text()
|
||||||
|
assert 'name = "openai-codex-cli-bin"' in pyproject
|
||||||
|
assert 'version = "1.2.3a4"' in pyproject
|
||||||
|
|
||||||
|
|
||||||
def test_stage_runtime_release_replaces_existing_staging_dir(tmp_path: Path) -> None:
|
def test_stage_runtime_release_replaces_existing_staging_dir(tmp_path: Path) -> None:
|
||||||
@@ -270,13 +324,15 @@ def test_stage_runtime_release_replaces_existing_staging_dir(tmp_path: Path) ->
|
|||||||
old_file.parent.mkdir(parents=True)
|
old_file.parent.mkdir(parents=True)
|
||||||
old_file.write_text("stale")
|
old_file.write_text("stale")
|
||||||
|
|
||||||
fake_binary = tmp_path / script.runtime_binary_name()
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
fake_binary = bundle_dir / script.runtime_binary_name()
|
||||||
fake_binary.write_text("fake codex\n")
|
fake_binary.write_text("fake codex\n")
|
||||||
|
|
||||||
staged = script.stage_python_runtime_package(
|
staged = script.stage_python_runtime_package(
|
||||||
staging_dir,
|
staging_dir,
|
||||||
"1.2.3",
|
"1.2.3",
|
||||||
fake_binary,
|
bundle_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert staged == staging_dir
|
assert staged == staging_dir
|
||||||
@@ -284,13 +340,132 @@ def test_stage_runtime_release_replaces_existing_staging_dir(tmp_path: Path) ->
|
|||||||
assert script.staged_runtime_bin_path(staged).read_text() == "fake codex\n"
|
assert script.staged_runtime_bin_path(staged).read_text() == "fake codex\n"
|
||||||
|
|
||||||
|
|
||||||
def test_stage_sdk_release_injects_exact_runtime_pin(tmp_path: Path) -> None:
|
def test_stage_runtime_release_normalizes_target_suffixed_names(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
script = _load_update_script_module()
|
script = _load_update_script_module()
|
||||||
staged = script.stage_python_sdk_package(tmp_path / "sdk-stage", "0.2.1", "1.2.3")
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / "codex-x86_64-unknown-linux-musl").write_text("fake codex\n")
|
||||||
|
|
||||||
|
staged = script.stage_python_runtime_package(
|
||||||
|
tmp_path / "runtime-stage",
|
||||||
|
"1.2.3",
|
||||||
|
bundle_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (staged / "src" / "codex_cli_bin" / "bin" / "codex").read_text() == (
|
||||||
|
"fake codex\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_runtime_release_requires_complete_windows_bundle(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
monkeypatch.setattr(script.platform, "system", lambda: "Windows")
|
||||||
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / "codex-x86_64-pc-windows-msvc.exe").write_text("codex\n")
|
||||||
|
(bundle_dir / "codex-command-runner-x86_64-pc-windows-msvc.exe").write_text(
|
||||||
|
"runner\n"
|
||||||
|
)
|
||||||
|
(bundle_dir / "codex-windows-sandbox-setup-x86_64-pc-windows-msvc.exe").write_text(
|
||||||
|
"setup\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
staged = script.stage_python_runtime_package(
|
||||||
|
tmp_path / "runtime-stage",
|
||||||
|
"1.2.3",
|
||||||
|
bundle_dir,
|
||||||
|
)
|
||||||
|
bin_dir = staged / "src" / "codex_cli_bin" / "bin"
|
||||||
|
|
||||||
|
assert (bin_dir / "codex.exe").read_text() == "codex\n"
|
||||||
|
assert (bin_dir / "codex-command-runner.exe").read_text() == "runner\n"
|
||||||
|
assert (bin_dir / "codex-windows-sandbox-setup.exe").read_text() == "setup\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_runtime_release_fails_for_missing_required_file(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
monkeypatch.setattr(script.platform, "system", lambda: "Windows")
|
||||||
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / "codex.exe").write_text("codex\n")
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError, match="codex-command-runner.exe"):
|
||||||
|
script.stage_python_runtime_package(
|
||||||
|
tmp_path / "runtime-stage",
|
||||||
|
"1.2.3",
|
||||||
|
bundle_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_package_helpers_return_packaged_paths(tmp_path: Path) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / "codex").write_text("fake codex\n")
|
||||||
|
staged = script.stage_python_runtime_package(
|
||||||
|
tmp_path / "runtime-stage",
|
||||||
|
"1.2.3",
|
||||||
|
bundle_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
runtime_module = _load_runtime_package_module(staged)
|
||||||
|
|
||||||
|
assert runtime_module.PACKAGE_NAME == "openai-codex-cli-bin"
|
||||||
|
assert runtime_module.bundled_bin_dir() == staged / "src" / "codex_cli_bin" / "bin"
|
||||||
|
assert runtime_module.bundled_runtime_files() == (
|
||||||
|
staged / "src" / "codex_cli_bin" / "bin" / "codex",
|
||||||
|
)
|
||||||
|
assert runtime_module.bundled_codex_path() == (
|
||||||
|
staged / "src" / "codex_cli_bin" / "bin" / "codex"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_package_helpers_report_missing_binary(tmp_path: Path) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
bundle_dir = tmp_path / "bundle"
|
||||||
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / "codex").write_text("fake codex\n")
|
||||||
|
staged = script.stage_python_runtime_package(
|
||||||
|
tmp_path / "runtime-stage",
|
||||||
|
"1.2.3",
|
||||||
|
bundle_dir,
|
||||||
|
)
|
||||||
|
(staged / "src" / "codex_cli_bin" / "bin" / "codex").unlink()
|
||||||
|
|
||||||
|
runtime_module = _load_runtime_package_module(staged)
|
||||||
|
|
||||||
|
with pytest.raises(FileNotFoundError, match="openai-codex-cli-bin"):
|
||||||
|
runtime_module.bundled_codex_path()
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_sdk_release_injects_exact_runtime_pin_and_versions(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
script = _load_update_script_module()
|
||||||
|
staged = script.stage_python_sdk_package(
|
||||||
|
tmp_path / "sdk-stage",
|
||||||
|
"0.2.1-beta.2",
|
||||||
|
"1.2.3-alpha.4",
|
||||||
|
)
|
||||||
|
|
||||||
pyproject = (staged / "pyproject.toml").read_text()
|
pyproject = (staged / "pyproject.toml").read_text()
|
||||||
assert 'version = "0.2.1"' in pyproject
|
assert 'name = "openai-codex"' in pyproject
|
||||||
assert '"codex-cli-bin==1.2.3"' in pyproject
|
assert 'version = "0.2.1b2"' in pyproject
|
||||||
|
assert '"openai-codex-cli-bin==1.2.3a4"' in pyproject
|
||||||
|
assert (
|
||||||
|
'__version__ = "0.2.1b2"'
|
||||||
|
in (staged / "src" / "codex_app_server" / "__init__.py").read_text()
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
'client_version: str = "0.2.1b2"'
|
||||||
|
in (staged / "src" / "codex_app_server" / "client.py").read_text()
|
||||||
|
)
|
||||||
assert not any((staged / "src" / "codex_app_server").glob("bin/**"))
|
assert not any((staged / "src" / "codex_app_server").glob("bin/**"))
|
||||||
|
|
||||||
|
|
||||||
@@ -329,7 +504,7 @@ def test_stage_sdk_runs_type_generation_before_staging(tmp_path: Path) -> None:
|
|||||||
return tmp_path / "sdk-stage"
|
return tmp_path / "sdk-stage"
|
||||||
|
|
||||||
def fake_stage_runtime_package(
|
def fake_stage_runtime_package(
|
||||||
_staging_dir: Path, _runtime_version: str, _runtime_binary: Path
|
_staging_dir: Path, _runtime_version: str, _runtime_bundle_dir: Path
|
||||||
) -> Path:
|
) -> Path:
|
||||||
raise AssertionError("runtime staging should not run for stage-sdk")
|
raise AssertionError("runtime staging should not run for stage-sdk")
|
||||||
|
|
||||||
@@ -350,14 +525,15 @@ def test_stage_sdk_runs_type_generation_before_staging(tmp_path: Path) -> None:
|
|||||||
|
|
||||||
def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> None:
|
def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) -> None:
|
||||||
script = _load_update_script_module()
|
script = _load_update_script_module()
|
||||||
fake_binary = tmp_path / script.runtime_binary_name()
|
bundle_dir = tmp_path / "bundle"
|
||||||
fake_binary.write_text("fake codex\n")
|
bundle_dir.mkdir()
|
||||||
|
(bundle_dir / script.runtime_binary_name()).write_text("fake codex\n")
|
||||||
calls: list[str] = []
|
calls: list[str] = []
|
||||||
args = script.parse_args(
|
args = script.parse_args(
|
||||||
[
|
[
|
||||||
"stage-runtime",
|
"stage-runtime",
|
||||||
str(tmp_path / "runtime-stage"),
|
str(tmp_path / "runtime-stage"),
|
||||||
str(fake_binary),
|
str(bundle_dir),
|
||||||
"--runtime-version",
|
"--runtime-version",
|
||||||
"1.2.3",
|
"1.2.3",
|
||||||
]
|
]
|
||||||
@@ -372,7 +548,7 @@ def test_stage_runtime_stages_binary_without_type_generation(tmp_path: Path) ->
|
|||||||
raise AssertionError("sdk staging should not run for stage-runtime")
|
raise AssertionError("sdk staging should not run for stage-runtime")
|
||||||
|
|
||||||
def fake_stage_runtime_package(
|
def fake_stage_runtime_package(
|
||||||
_staging_dir: Path, _runtime_version: str, _runtime_binary: Path
|
_staging_dir: Path, _runtime_version: str, _runtime_bundle_dir: Path
|
||||||
) -> Path:
|
) -> Path:
|
||||||
calls.append("stage_runtime")
|
calls.append("stage_runtime")
|
||||||
return tmp_path / "runtime-stage"
|
return tmp_path / "runtime-stage"
|
||||||
|
|||||||
Reference in New Issue
Block a user