mirror of
https://github.com/openai/codex.git
synced 2026-05-22 03:54:18 +00:00
release: use DotSlash zstd for package archives (#23752)
## Why
The Windows release job installed DotSlash successfully, but package
archive creation still failed while writing `codex-package-*.tar.zst`.
The Python archiver used `shutil.which("zstd")`, which does not reliably
find the extensionless DotSlash manifest at `.github/workflows/zstd`
from native Windows Python.
That left release packaging dependent on a command named exactly `zstd`
being discoverable on `PATH`, even though the repository already carries
a DotSlash wrapper for Windows runners.
## What changed
- Add `resolve_zstd_command()` to prefer a real `zstd` binary when
present.
- Fall back to invoking `dotslash .github/workflows/zstd` when `zstd` is
not on `PATH`.
- Keep the error explicit when neither `zstd` nor the DotSlash fallback
is available.
- Add unit coverage for direct `zstd`, DotSlash fallback, and
missing-tool error paths.
## Verification
- `python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'`
- `python3 -m py_compile scripts/codex_package/*.py`
This commit is contained in:
@@ -5,8 +5,14 @@ import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import zipfile
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
from .targets import REPO_ROOT
|
||||
|
||||
|
||||
ZSTD_DOTSLASH = REPO_ROOT / ".github" / "workflows" / "zstd"
|
||||
|
||||
|
||||
def write_archive(package_dir: Path, archive_path: Path, *, force: bool) -> None:
|
||||
if is_relative_to(archive_path, package_dir):
|
||||
@@ -63,14 +69,33 @@ def write_tar_archive(package_dir: Path, archive_path: Path, *, mode: str) -> No
|
||||
|
||||
|
||||
def write_tar_zst_archive(package_dir: Path, archive_path: Path) -> None:
|
||||
zstd = shutil.which("zstd")
|
||||
if zstd is None:
|
||||
raise RuntimeError("zstd is required to write .tar.zst archives.")
|
||||
zstd_command = resolve_zstd_command()
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="codex-package-archive-") as temp_dir_str:
|
||||
tar_path = Path(temp_dir_str) / "package.tar"
|
||||
write_tar_archive(package_dir, tar_path, mode="w")
|
||||
subprocess.check_call([zstd, "-T0", "-19", "-f", str(tar_path), "-o", str(archive_path)])
|
||||
subprocess.check_call(
|
||||
[*zstd_command, "-T0", "-19", "-f", str(tar_path), "-o", str(archive_path)]
|
||||
)
|
||||
|
||||
|
||||
def resolve_zstd_command(
|
||||
*,
|
||||
dotslash_manifest: Path = ZSTD_DOTSLASH,
|
||||
which: Callable[[str], str | None] = shutil.which,
|
||||
) -> list[str]:
|
||||
zstd = which("zstd")
|
||||
if zstd is not None:
|
||||
return [zstd]
|
||||
|
||||
dotslash = which("dotslash")
|
||||
if dotslash is not None and dotslash_manifest.is_file():
|
||||
return [dotslash, str(dotslash_manifest)]
|
||||
|
||||
raise RuntimeError(
|
||||
"zstd is required to write .tar.zst archives. Install zstd, or install "
|
||||
f"DotSlash so the repository wrapper can run: {dotslash_manifest}"
|
||||
)
|
||||
|
||||
|
||||
def write_zip_archive(package_dir: Path, archive_path: Path) -> None:
|
||||
|
||||
47
scripts/codex_package/test_archive.py
Normal file
47
scripts/codex_package/test_archive.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from codex_package.archive import resolve_zstd_command
|
||||
|
||||
|
||||
class ResolveZstdCommandTest(unittest.TestCase):
|
||||
def test_prefers_zstd_from_path(self) -> None:
|
||||
def which(name: str) -> str | None:
|
||||
return {"zstd": "/usr/bin/zstd", "dotslash": "/usr/bin/dotslash"}.get(name)
|
||||
|
||||
self.assertEqual(resolve_zstd_command(which=which), ["/usr/bin/zstd"])
|
||||
|
||||
def test_falls_back_to_dotslash_manifest(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
manifest = Path(temp_dir) / "zstd"
|
||||
manifest.write_text("#!/usr/bin/env dotslash\n{}\n", encoding="utf-8")
|
||||
|
||||
def which(name: str) -> str | None:
|
||||
return {"dotslash": "/usr/bin/dotslash"}.get(name)
|
||||
|
||||
self.assertEqual(
|
||||
resolve_zstd_command(dotslash_manifest=manifest, which=which),
|
||||
["/usr/bin/dotslash", str(manifest)],
|
||||
)
|
||||
|
||||
def test_errors_when_no_zstd_or_dotslash_manifest_is_available(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
missing_manifest = Path(temp_dir) / "zstd"
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "zstd is required"):
|
||||
resolve_zstd_command(
|
||||
dotslash_manifest=missing_manifest,
|
||||
which=lambda _name: None,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user