mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
feat: publish standalone installer archives
This commit is contained in:
@@ -540,13 +540,41 @@ jobs:
|
||||
--release-version "$RELEASE_VERSION" \
|
||||
--package codex \
|
||||
--package codex-responses-api-proxy \
|
||||
--package codex-sdk
|
||||
--package codex-sdk \
|
||||
--standalone-output-dir dist/installer
|
||||
|
||||
- name: Stage installer scripts
|
||||
run: |
|
||||
cp scripts/install/install.sh dist/install.sh
|
||||
cp scripts/install/install.ps1 dist/install.ps1
|
||||
|
||||
- name: Stage installer checksums
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.release_name.outputs.name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
checksums="dist/codex-installer_SHA256SUMS"
|
||||
found=false
|
||||
: > "$checksums"
|
||||
|
||||
for archive in dist/installer/codex-standalone-*-"${RELEASE_VERSION}".tar.gz; do
|
||||
if [[ ! -e "$archive" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
found=true
|
||||
digest="$(sha256sum "$archive" | awk '{print $1}')"
|
||||
printf '%s %s\n' "$digest" "$(basename "$archive")" >> "$checksums"
|
||||
done
|
||||
|
||||
if [[ "$found" != true ]]; then
|
||||
echo "No Codex standalone installer archives found for ${RELEASE_VERSION}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sort -k2,2 "$checksums" -o "$checksums"
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
|
||||
with:
|
||||
|
||||
@@ -16,6 +16,9 @@ from pathlib import Path
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
BUILD_SCRIPT = REPO_ROOT / "codex-cli" / "scripts" / "build_npm_package.py"
|
||||
INSTALL_NATIVE_DEPS = REPO_ROOT / "codex-cli" / "scripts" / "install_native_deps.py"
|
||||
STAGE_STANDALONE_INSTALLER_ARCHIVES = (
|
||||
REPO_ROOT / "scripts" / "stage_standalone_installer_archives.py"
|
||||
)
|
||||
WORKFLOW_NAME = ".github/workflows/rust-release.yml"
|
||||
GITHUB_REPO = "openai/codex"
|
||||
|
||||
@@ -53,6 +56,12 @@ def parse_args() -> argparse.Namespace:
|
||||
default=None,
|
||||
help="Directory where npm tarballs should be written (default: dist/npm).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--standalone-output-dir",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Directory where standalone installer archives should be written.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--keep-staging-dirs",
|
||||
action="store_true",
|
||||
@@ -130,6 +139,24 @@ def run_command(cmd: list[str]) -> None:
|
||||
subprocess.run(cmd, cwd=REPO_ROOT, check=True)
|
||||
|
||||
|
||||
def stage_standalone_installer_archives(
|
||||
release_version: str,
|
||||
vendor_src: Path,
|
||||
output_dir: Path,
|
||||
) -> None:
|
||||
run_command(
|
||||
[
|
||||
str(STAGE_STANDALONE_INSTALLER_ARCHIVES),
|
||||
"--release-version",
|
||||
release_version,
|
||||
"--vendor-src",
|
||||
str(vendor_src),
|
||||
"--output-dir",
|
||||
str(output_dir),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def tarball_name_for_package(package: str, version: str) -> str:
|
||||
if package in CODEX_PLATFORM_PACKAGES:
|
||||
platform = package.removeprefix("codex-")
|
||||
@@ -192,6 +219,24 @@ def main() -> int:
|
||||
shutil.rmtree(staging_dir, ignore_errors=True)
|
||||
|
||||
final_messages.append(f"Staged {package} at {pack_output}")
|
||||
|
||||
if args.standalone_output_dir is not None:
|
||||
if vendor_src is None:
|
||||
raise RuntimeError(
|
||||
"--standalone-output-dir requires staged native binaries, but no selected "
|
||||
"package installed native components."
|
||||
)
|
||||
|
||||
standalone_output_dir = args.standalone_output_dir
|
||||
standalone_output_dir.mkdir(parents=True, exist_ok=True)
|
||||
stage_standalone_installer_archives(
|
||||
args.release_version,
|
||||
vendor_src,
|
||||
standalone_output_dir,
|
||||
)
|
||||
final_messages.append(
|
||||
f"Staged standalone installer archives in {standalone_output_dir}"
|
||||
)
|
||||
finally:
|
||||
if vendor_temp_root is not None and not args.keep_staging_dirs:
|
||||
shutil.rmtree(vendor_temp_root, ignore_errors=True)
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stage standalone installer archives for Codex releases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib.util
|
||||
import shutil
|
||||
import tarfile
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
BUILD_SCRIPT = REPO_ROOT / "codex-cli" / "scripts" / "build_npm_package.py"
|
||||
|
||||
_SPEC = importlib.util.spec_from_file_location("codex_build_npm_package", BUILD_SCRIPT)
|
||||
if _SPEC is None or _SPEC.loader is None:
|
||||
raise RuntimeError(f"Unable to load module from {BUILD_SCRIPT}")
|
||||
_BUILD_MODULE = importlib.util.module_from_spec(_SPEC)
|
||||
_SPEC.loader.exec_module(_BUILD_MODULE)
|
||||
CODEX_PLATFORM_PACKAGES = getattr(_BUILD_MODULE, "CODEX_PLATFORM_PACKAGES", {})
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--release-version",
|
||||
required=True,
|
||||
help="Version to stage (e.g. 0.1.0 or 0.1.0-alpha.1).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--vendor-src",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="Directory containing native binaries under vendor/<target>.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
type=Path,
|
||||
default=REPO_ROOT / "dist" / "installer",
|
||||
help="Directory where standalone archives should be written.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def archive_name(platform_tag: str, version: str) -> str:
|
||||
return f"codex-standalone-{platform_tag}-{version}.tar.gz"
|
||||
|
||||
|
||||
def copy_executable(source: Path, destination: Path) -> None:
|
||||
if not source.exists():
|
||||
raise RuntimeError(f"Missing standalone installer archive input: {source}")
|
||||
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(source, destination)
|
||||
destination.chmod(0o755)
|
||||
|
||||
|
||||
def stage_target(vendor_src: Path, staging_dir: Path, target: str, is_windows: bool) -> None:
|
||||
target_root = vendor_src / target
|
||||
codex_root = target_root / "codex"
|
||||
path_root = target_root / "path"
|
||||
resources_dir = staging_dir / "codex-resources"
|
||||
resources_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if is_windows:
|
||||
copy_executable(codex_root / "codex.exe", staging_dir / "codex.exe")
|
||||
copy_executable(
|
||||
codex_root / "codex-command-runner.exe",
|
||||
resources_dir / "codex-command-runner.exe",
|
||||
)
|
||||
copy_executable(
|
||||
codex_root / "codex-windows-sandbox-setup.exe",
|
||||
resources_dir / "codex-windows-sandbox-setup.exe",
|
||||
)
|
||||
copy_executable(path_root / "rg.exe", resources_dir / "rg.exe")
|
||||
return
|
||||
|
||||
copy_executable(codex_root / "codex", staging_dir / "codex")
|
||||
copy_executable(path_root / "rg", resources_dir / "rg")
|
||||
|
||||
|
||||
def write_archive(staging_dir: Path, output_path: Path) -> None:
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with tarfile.open(output_path, "w:gz") as archive:
|
||||
for path in sorted(staging_dir.rglob("*")):
|
||||
archive.add(path, arcname=path.relative_to(staging_dir))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
vendor_src = args.vendor_src.resolve()
|
||||
output_dir = args.output_dir.resolve()
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for package in sorted(CODEX_PLATFORM_PACKAGES):
|
||||
package_config = CODEX_PLATFORM_PACKAGES[package]
|
||||
platform_tag = package_config["npm_tag"]
|
||||
target = package_config["target_triple"]
|
||||
is_windows = package_config["os"] == "win32"
|
||||
output_path = output_dir / archive_name(platform_tag, args.release_version)
|
||||
|
||||
with tempfile.TemporaryDirectory(
|
||||
prefix=f"codex-standalone-{platform_tag}-"
|
||||
) as staging_dir_str:
|
||||
staging_dir = Path(staging_dir_str)
|
||||
stage_target(vendor_src, staging_dir, target, is_windows)
|
||||
write_archive(staging_dir, output_path)
|
||||
|
||||
print(f"Staged standalone installer archive at {output_path}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user