Files
codex/scripts/codex_package/archive.py
Michael Bolin f48be015d6 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`
2026-05-20 14:28:11 -07:00

113 lines
3.6 KiB
Python

"""Archive writers for canonical Codex package directories."""
import shutil
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):
raise RuntimeError(
f"Archive output must be outside the package directory: {archive_path}"
)
archive_path.parent.mkdir(parents=True, exist_ok=True)
if archive_path.exists():
if not force:
raise RuntimeError(f"Archive output already exists: {archive_path}")
archive_path.unlink()
archive_format = archive_format_for_path(archive_path)
if archive_format == "tar.gz":
write_tar_archive(package_dir, archive_path, mode="w:gz")
elif archive_format == "tar.zst":
write_tar_zst_archive(package_dir, archive_path)
elif archive_format == "zip":
write_zip_archive(package_dir, archive_path)
else:
raise AssertionError(f"unexpected archive format: {archive_format}")
def is_relative_to(path: Path, parent: Path) -> bool:
try:
path.relative_to(parent)
return True
except ValueError:
return False
def archive_format_for_path(path: Path) -> str:
suffixes = path.suffixes
if suffixes[-2:] == [".tar", ".gz"] or path.suffix == ".tgz":
return "tar.gz"
if suffixes[-2:] == [".tar", ".zst"]:
return "tar.zst"
if path.suffix == ".zip":
return "zip"
raise RuntimeError(
f"Unsupported archive suffix for {path}. Use .tar.gz, .tgz, .tar.zst, or .zip."
)
def write_tar_archive(package_dir: Path, archive_path: Path, *, mode: str) -> None:
with tarfile.open(archive_path, mode) as archive:
for path in package_entries(package_dir):
archive.add(
path,
arcname=path.relative_to(package_dir),
recursive=False,
)
def write_tar_zst_archive(package_dir: Path, archive_path: Path) -> None:
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_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:
with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
for path in package_entries(package_dir):
relative_path = path.relative_to(package_dir)
if path.is_dir():
archive.write(path, f"{relative_path}/")
else:
archive.write(path, relative_path)
def package_entries(package_dir: Path) -> list[Path]:
return sorted(package_dir.rglob("*"), key=lambda path: path.relative_to(package_dir).as_posix())