mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Support alpha hotfix release versions (#34463)
## What changed - Map Python `aN.postM` versions to Codex `-alpha.N.M` release tags through shared release-version helpers. - Accept alpha hotfix versions in Python runtime workflows, Rust release validation, npm publishing, and the shell and PowerShell installers. ## Testing - Cover version conversion, workflow output, runtime setup, artifact staging, and installer handling for alpha hotfix releases. GitOrigin-RevId: b95edb56f7c93b435a8050c10f0202dc117e6669
This commit is contained in:
@@ -13,6 +13,15 @@ import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
_SDK_PYTHON_ROOT = str(Path(__file__).resolve().parent)
|
||||
if _SDK_PYTHON_ROOT not in sys.path:
|
||||
sys.path.insert(0, _SDK_PYTHON_ROOT)
|
||||
|
||||
from release_version import ( # noqa: E402
|
||||
codex_release_tag as _release_tag,
|
||||
normalize_codex_version as _normalized_package_version,
|
||||
)
|
||||
|
||||
PACKAGE_NAME = "openai-codex-cli-bin"
|
||||
SDK_PACKAGE_NAME = "openai-codex"
|
||||
REPO_SLUG = "openai/codex"
|
||||
@@ -326,34 +335,6 @@ def _github_token() -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _normalized_package_version(version: str) -> str:
|
||||
normalized = version.strip()
|
||||
if normalized.startswith("rust-v"):
|
||||
normalized = normalized.removeprefix("rust-v")
|
||||
elif normalized.startswith("v"):
|
||||
normalized = normalized.removeprefix("v")
|
||||
|
||||
normalized = re.sub(r"-alpha\.?([0-9]+)$", r"a\1", normalized)
|
||||
normalized = re.sub(r"-beta\.?([0-9]+)$", r"b\1", normalized)
|
||||
normalized = re.sub(r"-rc\.?([0-9]+)$", r"rc\1", normalized)
|
||||
return normalized
|
||||
|
||||
|
||||
def _codex_release_version(version: str) -> str:
|
||||
normalized = _normalized_package_version(version)
|
||||
match = re.fullmatch(r"([0-9]+(?:\.[0-9]+)*)(a|b|rc)([0-9]+)", normalized)
|
||||
if match is None:
|
||||
return normalized
|
||||
|
||||
base, prerelease, number = match.groups()
|
||||
prerelease_name = {"a": "alpha", "b": "beta", "rc": "rc"}[prerelease]
|
||||
return f"{base}-{prerelease_name}.{number}"
|
||||
|
||||
|
||||
def _release_tag(version: str) -> str:
|
||||
return f"rust-v{_codex_release_version(version)}"
|
||||
|
||||
|
||||
def _source_tree_runtime_dependency_version() -> str | None:
|
||||
"""Read the runtime dependency pin when the SDK is running from a checkout."""
|
||||
pyproject_path = Path(__file__).resolve().parent / "pyproject.toml"
|
||||
|
||||
86
sdk/python/release_version.py
Normal file
86
sdk/python/release_version.py
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
|
||||
_PYTHON_RUNTIME_VERSION_PATTERN = re.compile(r"[0-9]+\.[0-9]+\.[0-9]+(?:a[0-9]+(?:\.post[0-9]+)?)?")
|
||||
_NORMALIZED_CODEX_VERSION_PATTERN = re.compile(
|
||||
r"[0-9]+(?:\.[0-9]+)*(?:(?:a|b|rc)[0-9]+)?(?:\.post[0-9]+)?"
|
||||
)
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Resolve a Python runtime package version to its Codex release tag."
|
||||
)
|
||||
parser.add_argument("python_version")
|
||||
parser.add_argument("--github-output", type=Path, required=True)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
try:
|
||||
python_version, release_tag = resolve_python_runtime_release(args.python_version)
|
||||
except RuntimeError as exc:
|
||||
print(exc, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
with args.github_output.open("a", encoding="utf-8") as output:
|
||||
print(f"python_version={python_version}", file=output)
|
||||
print(f"release_tag={release_tag}", file=output)
|
||||
return 0
|
||||
|
||||
|
||||
def resolve_python_runtime_release(python_version: str) -> tuple[str, str]:
|
||||
if _PYTHON_RUNTIME_VERSION_PATTERN.fullmatch(python_version) is None:
|
||||
raise RuntimeError(
|
||||
"Python runtime version must be stable, a numbered alpha, or an "
|
||||
"alpha post-release, for example 0.136.0, 0.136.0a2, or "
|
||||
f"0.136.0a2.post1; found {python_version}"
|
||||
)
|
||||
return python_version, codex_release_tag(python_version)
|
||||
|
||||
|
||||
def codex_release_tag(version: str) -> str:
|
||||
return f"rust-v{codex_release_version(version)}"
|
||||
|
||||
|
||||
def codex_release_version(version: str) -> str:
|
||||
normalized = normalize_codex_version(version)
|
||||
alpha_hotfix = re.fullmatch(
|
||||
r"([0-9]+(?:\.[0-9]+)*)a([0-9]+)\.post([0-9]+)",
|
||||
normalized,
|
||||
)
|
||||
if alpha_hotfix is not None:
|
||||
base, alpha, hotfix = alpha_hotfix.groups()
|
||||
return f"{base}-alpha.{alpha}.{hotfix}"
|
||||
|
||||
prerelease = re.fullmatch(r"([0-9]+(?:\.[0-9]+)*)(a|b|rc)([0-9]+)", normalized)
|
||||
if prerelease is None:
|
||||
return normalized
|
||||
|
||||
base, prerelease_kind, number = prerelease.groups()
|
||||
prerelease_name = {"a": "alpha", "b": "beta", "rc": "rc"}[prerelease_kind]
|
||||
return f"{base}-{prerelease_name}.{number}"
|
||||
|
||||
|
||||
def normalize_codex_version(version: str) -> str:
|
||||
normalized = version.strip()
|
||||
if normalized.startswith("rust-v"):
|
||||
normalized = normalized.removeprefix("rust-v")
|
||||
elif normalized.startswith("v"):
|
||||
normalized = normalized.removeprefix("v")
|
||||
|
||||
normalized = re.sub(r"-alpha\.?([0-9]+)\.([0-9]+)$", r"a\1.post\2", normalized)
|
||||
normalized = re.sub(r"-alpha\.?([0-9]+)$", r"a\1", normalized)
|
||||
normalized = re.sub(r"-beta\.?([0-9]+)$", r"b\1", normalized)
|
||||
normalized = re.sub(r"-rc\.?([0-9]+)$", r"rc\1", normalized)
|
||||
|
||||
if _NORMALIZED_CODEX_VERSION_PATTERN.fullmatch(normalized) is None:
|
||||
raise RuntimeError(f"Could not normalize Codex version {version!r} to a PEP 440 version")
|
||||
return normalized
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -17,6 +17,12 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Sequence, get_args, get_origin
|
||||
|
||||
_SDK_PYTHON_ROOT = str(Path(__file__).resolve().parents[1])
|
||||
if _SDK_PYTHON_ROOT not in sys.path:
|
||||
sys.path.insert(0, _SDK_PYTHON_ROOT)
|
||||
|
||||
from release_version import normalize_codex_version # noqa: E402
|
||||
|
||||
SDK_DISTRIBUTION_NAME = "openai-codex"
|
||||
RUNTIME_DISTRIBUTION_NAME = "openai-codex-cli-bin"
|
||||
RUNTIME_PACKAGE_ROOT = Path("src") / "codex_cli_bin"
|
||||
@@ -130,22 +136,6 @@ def pinned_runtime_codex_path() -> Path:
|
||||
return codex_path
|
||||
|
||||
|
||||
def normalize_codex_version(version: str) -> str:
|
||||
normalized = version.strip()
|
||||
if normalized.startswith("rust-v"):
|
||||
normalized = normalized.removeprefix("rust-v")
|
||||
elif normalized.startswith("v"):
|
||||
normalized = normalized.removeprefix("v")
|
||||
|
||||
normalized = re.sub(r"-alpha\.?([0-9]+)$", r"a\1", normalized)
|
||||
normalized = re.sub(r"-beta\.?([0-9]+)$", r"b\1", normalized)
|
||||
normalized = re.sub(r"-rc\.?([0-9]+)$", r"rc\1", normalized)
|
||||
|
||||
if not re.fullmatch(r"[0-9]+(?:\.[0-9]+)*(?:(?:a|b|rc)[0-9]+)?", normalized):
|
||||
raise RuntimeError(f"Could not normalize Codex version {version!r} to a PEP 440 version")
|
||||
return normalized
|
||||
|
||||
|
||||
def _copy_package_tree(src: Path, dst: Path) -> None:
|
||||
if dst.exists():
|
||||
if dst.is_dir():
|
||||
@@ -1384,7 +1374,8 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
required=True,
|
||||
help=(
|
||||
"Codex release version to write into the staged runtime package. "
|
||||
"Accepts PEP 440 versions or release tags such as rust-v0.116.0-alpha.1."
|
||||
"Accepts PEP 440 versions or release tags such as "
|
||||
"rust-v0.116.0-alpha.1.2."
|
||||
),
|
||||
)
|
||||
stage_runtime_parser.add_argument(
|
||||
|
||||
@@ -3,6 +3,7 @@ import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import urllib.error
|
||||
@@ -51,6 +52,18 @@ def _load_runtime_setup_module():
|
||||
return module
|
||||
|
||||
|
||||
def _load_release_version_module():
|
||||
"""Load the shared release-version conversions used by release tooling."""
|
||||
script_path = ROOT / "release_version.py"
|
||||
spec = importlib.util.spec_from_file_location("release_version", script_path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise AssertionError(f"Failed to load release-version module: {script_path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _write_fake_codex_package(package_dir: Path, script) -> Path:
|
||||
(package_dir / "bin").mkdir(parents=True)
|
||||
(package_dir / "codex-resources").mkdir()
|
||||
@@ -571,13 +584,19 @@ def test_runtime_setup_reads_independent_runtime_pin_and_release_tags() -> None:
|
||||
"normalized_release_version": runtime_setup._normalized_package_version(
|
||||
"rust-v0.116.0-alpha.1"
|
||||
),
|
||||
"normalized_alpha_hotfix_version": runtime_setup._normalized_package_version(
|
||||
"rust-v0.116.0-alpha.1.2"
|
||||
),
|
||||
"release_tag": runtime_setup._release_tag("0.116.0a1"),
|
||||
"alpha_hotfix_release_tag": runtime_setup._release_tag("0.116.0a1.post2"),
|
||||
} == {
|
||||
"package_name": "openai-codex-cli-bin",
|
||||
"sdk_template_version": "0.0.0-dev",
|
||||
"runtime_pin": "0.144.4",
|
||||
"normalized_release_version": "0.116.0a1",
|
||||
"normalized_alpha_hotfix_version": "0.116.0a1.post2",
|
||||
"release_tag": "rust-v0.116.0-alpha.1",
|
||||
"alpha_hotfix_release_tag": "rust-v0.116.0-alpha.1.2",
|
||||
}
|
||||
|
||||
|
||||
@@ -703,11 +722,54 @@ def test_normalize_codex_version_accepts_release_tags_and_pep440_versions() -> N
|
||||
script = _load_update_script_module()
|
||||
|
||||
assert script.normalize_codex_version("rust-v0.116.0-alpha.1") == "0.116.0a1"
|
||||
assert script.normalize_codex_version("rust-v0.116.0-alpha.1.2") == "0.116.0a1.post2"
|
||||
assert script.normalize_codex_version("v0.116.0-beta.2") == "0.116.0b2"
|
||||
assert script.normalize_codex_version("0.116.0rc3") == "0.116.0rc3"
|
||||
assert script.normalize_codex_version("0.116.0") == "0.116.0"
|
||||
|
||||
|
||||
def test_release_version_conversions_map_python_versions_to_codex_tags() -> None:
|
||||
release_version = _load_release_version_module()
|
||||
|
||||
assert {
|
||||
version: release_version.codex_release_tag(version)
|
||||
for version in ["0.116.0", "0.116.0a1", "0.116.0a1.post2"]
|
||||
} == {
|
||||
"0.116.0": "rust-v0.116.0",
|
||||
"0.116.0a1": "rust-v0.116.0-alpha.1",
|
||||
"0.116.0a1.post2": "rust-v0.116.0-alpha.1.2",
|
||||
}
|
||||
|
||||
|
||||
def test_release_version_cli_writes_python_runtime_outputs(tmp_path: Path) -> None:
|
||||
github_output = tmp_path / "github-output"
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "release_version.py"),
|
||||
"0.116.0a1.post2",
|
||||
"--github-output",
|
||||
str(github_output),
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert {
|
||||
"returncode": result.returncode,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stderr,
|
||||
"github_output": github_output.read_text(),
|
||||
} == {
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"github_output": ("python_version=0.116.0a1.post2\nrelease_tag=rust-v0.116.0-alpha.1.2\n"),
|
||||
}
|
||||
|
||||
|
||||
def test_stage_runtime_release_replaces_existing_staging_dir(tmp_path: Path) -> None:
|
||||
script = _load_update_script_module()
|
||||
staging_dir = tmp_path / "runtime-stage"
|
||||
|
||||
Reference in New Issue
Block a user