← Files Adaptive Codex OrchestratorARCHIVED FILE

scripts/build_release.py

14.5 KB · Sep 30, 2026 · 23:14 UTC

↓ Download file

#!/usr/bin/env python3
"""Build a deterministic full-plugin release ZIP and SHA-256 sidecar."""

from __future__ import annotations

import argparse
from dataclasses import dataclass
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import stat
import sys
import tempfile
from typing import Dict, Iterable, List, Mapping, Optional, Sequence, Tuple
import unicodedata
import zipfile


SCRIPT_DIR = Path(__file__).resolve().parent
if str(SCRIPT_DIR) not in sys.path:
    sys.path.insert(0, str(SCRIPT_DIR))

from validate_release_artifact import (
    ARCHIVE_NAME,
    FIXED_ZIP_TIMESTAMP,
    MAX_MEMBER_BYTES,
    MAX_MEMBERS,
    MAX_TOTAL_UNCOMPRESSED_BYTES,
    PACKAGE_NAME,
    PACKAGE_VERSION,
    ReleaseValidationError,
    is_secret_like_filename,
    private_path_markers,
    validate_archive,
    validate_member_name,
)


EXCLUDED_DIRECTORIES = frozenset(
    {
        "dist",
        "__pycache__",
        ".pytest_cache",
        ".mypy_cache",
        ".ruff_cache",
        ".cache",
        "htmlcov",
        "tmp",
        "temp",
        ".tmp",
    }
)
EXCLUDED_FILENAMES = frozenset(
    {".DS_Store", "Thumbs.db", ".coverage", "coverage.xml"}
)
EXCLUDED_SUFFIXES = (".pyc", ".pyo", ".tmp", ".swp", ".swo")


class ReleaseBuildError(RuntimeError):
    """Raised when source collection or reproducible output fails."""


@dataclass(frozen=True)
class SourceFile:
    path: Path
    relative: str
    size: int
    fingerprint: Tuple[int, int, int, int]


def _fingerprint(value: os.stat_result) -> Tuple[int, int, int, int]:
    return (value.st_dev, value.st_ino, value.st_size, value.st_mtime_ns)


def _is_reparse(stat_value: os.stat_result) -> bool:
    attributes = getattr(stat_value, "st_file_attributes", 0)
    marker = getattr(stat, "FILE_ATTRIBUTE_REPARSE_POINT", 0x400)
    return bool(attributes & marker)


def _check_not_link_or_reparse(path: Path, label: str) -> os.stat_result:
    try:
        value = path.lstat()
    except OSError as exc:
        raise ReleaseBuildError(f"cannot inspect {label}: {path}") from exc
    if stat.S_ISLNK(value.st_mode) or _is_reparse(value):
        raise ReleaseBuildError(f"symlink or reparse point is forbidden for {label}: {path}")
    return value


def _excluded_file(name: str) -> bool:
    if name in EXCLUDED_FILENAMES or name.startswith(".coverage."):
        return True
    if name.endswith("~"):
        return True
    return name.casefold().endswith(EXCLUDED_SUFFIXES)


def _collect_sources(plugin_root: Path) -> List[SourceFile]:
    root_stat = _check_not_link_or_reparse(plugin_root, "plugin root")
    if not stat.S_ISDIR(root_stat.st_mode):
        raise ReleaseBuildError("plugin root must be a directory")
    root = plugin_root.resolve(strict=True)
    records: List[SourceFile] = []
    portable_paths: Dict[str, str] = {}
    total_size = 0

    def visit(directory: Path) -> None:
        nonlocal total_size
        try:
            entries = sorted(
                os.scandir(directory),
                key=lambda entry: unicodedata.normalize("NFC", entry.name),
            )
        except OSError as exc:
            raise ReleaseBuildError(f"cannot enumerate source directory: {directory}") from exc
        for entry in entries:
            path = Path(entry.path)
            value = _check_not_link_or_reparse(path, "source entry")
            try:
                relative_path = path.relative_to(root)
            except ValueError as exc:
                raise ReleaseBuildError(f"source path escaped plugin root: {path}") from exc
            relative = PurePosixPath(*relative_path.parts).as_posix()
            if relative != unicodedata.normalize("NFC", relative):
                raise ReleaseBuildError(f"source path is not NFC-normalized: {relative!r}")

            if stat.S_ISDIR(value.st_mode):
                if entry.name in EXCLUDED_DIRECTORIES:
                    continue
                visit(path)
                continue
            if not stat.S_ISREG(value.st_mode):
                raise ReleaseBuildError(f"special source file is forbidden: {relative!r}")
            if _excluded_file(entry.name):
                continue
            if is_secret_like_filename(relative):
                raise ReleaseBuildError(f"secret-like source filename is forbidden: {relative!r}")
            archive_name = f"{PACKAGE_NAME}/{relative}"
            try:
                validate_member_name(archive_name)
            except ReleaseValidationError as exc:
                raise ReleaseBuildError(str(exc)) from exc
            portable = unicodedata.normalize("NFKC", archive_name).casefold()
            previous = portable_paths.get(portable)
            if previous is not None:
                raise ReleaseBuildError(
                    f"portable source path collision: {previous!r} and {relative!r}"
                )
            portable_paths[portable] = relative
            if value.st_size > MAX_MEMBER_BYTES:
                raise ReleaseBuildError(f"source file exceeds size limit: {relative!r}")
            total_size += value.st_size
            if total_size > MAX_TOTAL_UNCOMPRESSED_BYTES:
                raise ReleaseBuildError("source tree exceeds total release size limit")
            records.append(
                SourceFile(
                    path=path,
                    relative=relative,
                    size=value.st_size,
                    fingerprint=_fingerprint(value),
                )
            )

    visit(root)
    if not records or len(records) > MAX_MEMBERS:
        raise ReleaseBuildError("source file count is empty or exceeds the release limit")
    records.sort(key=lambda item: f"{PACKAGE_NAME}/{item.relative}")
    return records


def _read_source(record: SourceFile, root: Path) -> bytes:
    try:
        before = record.path.lstat()
        resolved = record.path.resolve(strict=True)
        resolved.relative_to(root)
    except (OSError, ValueError) as exc:
        raise ReleaseBuildError(f"source file became unavailable: {record.relative!r}") from exc
    if (
        stat.S_ISLNK(before.st_mode)
        or _is_reparse(before)
        or not stat.S_ISREG(before.st_mode)
        or _fingerprint(before) != record.fingerprint
    ):
        raise ReleaseBuildError(f"source file changed before packaging: {record.relative!r}")

    flags = os.O_RDONLY
    flags |= getattr(os, "O_BINARY", 0)
    flags |= getattr(os, "O_CLOEXEC", 0)
    flags |= getattr(os, "O_NOFOLLOW", 0)
    try:
        descriptor = os.open(str(record.path), flags)
    except OSError as exc:
        raise ReleaseBuildError(f"source file cannot be opened safely: {record.relative!r}") from exc
    try:
        opened = os.fstat(descriptor)
        if not stat.S_ISREG(opened.st_mode) or _fingerprint(opened) != record.fingerprint:
            raise ReleaseBuildError(f"source file changed while opening: {record.relative!r}")
        chunks: List[bytes] = []
        remaining = MAX_MEMBER_BYTES + 1
        while remaining > 0:
            chunk = os.read(descriptor, min(1024 * 1024, remaining))
            if not chunk:
                break
            chunks.append(chunk)
            remaining -= len(chunk)
        data = b"".join(chunks)
        after = os.fstat(descriptor)
    finally:
        os.close(descriptor)
    if len(data) != record.size or len(data) > MAX_MEMBER_BYTES:
        raise ReleaseBuildError(f"source size changed while reading: {record.relative!r}")
    if _fingerprint(after) != record.fingerprint:
        raise ReleaseBuildError(f"source file changed during packaging: {record.relative!r}")
    markers = private_path_markers(data)
    if markers:
        raise ReleaseBuildError(
            f"private absolute path in {record.relative!r}: {', '.join(markers)}"
        )
    return data


def _zip_info(name: str) -> zipfile.ZipInfo:
    info = zipfile.ZipInfo(filename=name, date_time=FIXED_ZIP_TIMESTAMP)
    info.compress_type = zipfile.ZIP_DEFLATED
    info.create_system = 3
    permissions = 0o755 if name.endswith(".sh") else 0o644
    info.external_attr = (stat.S_IFREG | permissions) << 16
    info.extra = b""
    info.comment = b""
    return info


def _build_once(target: Path, records: Sequence[SourceFile], root: Path) -> None:
    try:
        with zipfile.ZipFile(
            target,
            "w",
            compression=zipfile.ZIP_DEFLATED,
            compresslevel=9,
            allowZip64=False,
        ) as archive:
            archive.comment = b""
            for record in records:
                name = f"{PACKAGE_NAME}/{record.relative}"
                archive.writestr(
                    _zip_info(name),
                    _read_source(record, root),
                    compress_type=zipfile.ZIP_DEFLATED,
                    compresslevel=9,
                )
    except (OSError, RuntimeError, ValueError, zipfile.LargeZipFile) as exc:
        if isinstance(exc, ReleaseBuildError):
            raise
        raise ReleaseBuildError(f"failed to build deterministic ZIP: {exc}") from exc


def _atomic_write(path: Path, data: bytes) -> None:
    descriptor: Optional[int] = None
    temporary: Optional[Path] = None
    try:
        descriptor, temporary_name = tempfile.mkstemp(
            prefix=f".{path.name}.", suffix=".tmp", dir=str(path.parent)
        )
        temporary = Path(temporary_name)
        with os.fdopen(descriptor, "wb") as handle:
            descriptor = None
            handle.write(data)
            handle.flush()
            os.fsync(handle.fileno())
        if os.name != "nt":
            temporary.chmod(0o644)
        os.replace(temporary, path)
        temporary = None
        if os.name != "nt":
            directory_flags = getattr(os, "O_DIRECTORY", 0) | os.O_RDONLY
            directory_descriptor = os.open(str(path.parent), directory_flags)
            try:
                os.fsync(directory_descriptor)
            finally:
                os.close(directory_descriptor)
    except OSError as exc:
        raise ReleaseBuildError(f"atomic write failed for {path.name!r}") from exc
    finally:
        if descriptor is not None:
            os.close(descriptor)
        if temporary is not None:
            try:
                temporary.unlink(missing_ok=True)
            except OSError:
                pass


def _load_and_check_manifest(root: Path) -> None:
    path = root / ".codex-plugin" / "plugin.json"
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
        raise ReleaseBuildError("plugin manifest is missing or invalid") from exc
    if not isinstance(value, Mapping):
        raise ReleaseBuildError("plugin manifest root must be an object")
    if value.get("name") != PACKAGE_NAME or value.get("version") != PACKAGE_VERSION:
        raise ReleaseBuildError(
            f"manifest must declare {PACKAGE_NAME!r} version {PACKAGE_VERSION!r}"
        )


def _prepare_dist(root: Path) -> Path:
    dist = root / "dist"
    if dist.exists() or dist.is_symlink():
        value = _check_not_link_or_reparse(dist, "dist directory")
        if not stat.S_ISDIR(value.st_mode):
            raise ReleaseBuildError("dist exists but is not a directory")
    else:
        try:
            dist.mkdir(mode=0o755)
        except OSError as exc:
            raise ReleaseBuildError("could not create dist directory") from exc
        _check_not_link_or_reparse(dist, "dist directory")
    return dist


def build_release(plugin_root: Path) -> Mapping[str, object]:
    requested_root = Path(plugin_root)
    _check_not_link_or_reparse(requested_root, "plugin root")
    root = requested_root.resolve(strict=True)
    if root.name != PACKAGE_NAME:
        raise ReleaseBuildError(f"plugin root folder must be named {PACKAGE_NAME!r}")
    _load_and_check_manifest(root)
    records = _collect_sources(root)

    with tempfile.TemporaryDirectory(prefix="adaptive-release-build-") as temporary:
        work = Path(temporary)
        first = work / ARCHIVE_NAME
        second = work / ("second-" + ARCHIVE_NAME)
        _build_once(first, records, root)
        _build_once(second, records, root)
        first_bytes = first.read_bytes()
        second_bytes = second.read_bytes()
        if first_bytes != second_bytes:
            raise ReleaseBuildError("two independent builds were not byte-identical")
        first_report = validate_archive(first, trusted_source_root=root)

        # The validator requires the canonical filename, so independently copy
        # the second build to a canonical validation location before checking it.
        second_canonical = work / "verification" / ARCHIVE_NAME
        second_canonical.parent.mkdir()
        second_canonical.write_bytes(second_bytes)
        second_report = validate_archive(second_canonical, trusted_source_root=root)
        if first_report["sha256"] != second_report["sha256"]:
            raise ReleaseBuildError("independent validation digests do not match")

        dist = _prepare_dist(root)
        archive_path = dist / ARCHIVE_NAME
        digest = str(first_report["sha256"])
        sidecar_path = dist / (ARCHIVE_NAME + ".sha256")
        sidecar = f"{digest}  {ARCHIVE_NAME}\n".encode("ascii")
        _atomic_write(archive_path, first_bytes)
        _atomic_write(sidecar_path, sidecar)

    final_report = dict(
        validate_archive(
            archive_path,
            trusted_source_root=root,
            require_sidecar=True,
        )
    )
    final_report["sidecar"] = str(sidecar_path.resolve())
    final_report["reproducible_builds"] = 2
    return final_report


def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--plugin-root",
        type=Path,
        default=SCRIPT_DIR.parent,
        help="Plugin root to package (defaults to the parent of this script directory)",
    )
    return parser.parse_args(argv)


def main(argv: Optional[Sequence[str]] = None) -> int:
    args = parse_args(argv)
    try:
        report = build_release(args.plugin_root)
    except (OSError, ReleaseBuildError, ReleaseValidationError) as exc:
        print(f"Release build failed: {exc}", file=sys.stderr)
        return 1
    print(json.dumps(report, ensure_ascii=False, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: 8002c3f0d7de4e2fdf8be6045967854190095f410e652e94673b4a9dc195b296