scripts/validate_docker_update.py
Rama main · réplica de lectura del repositorio original.
"""Validate a managed-Docker update artifact without installing it."""
from __future__ import annotations
import argparse
import os
import platform
import shutil
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from src.docker_launcher.contracts import ( # noqa: E402
DOCKER_ASSET_RE,
safe_extract_package,
validate_package_tree,
)
FORBIDDEN_PARTS = {
".git", ".codex", ".claude", "data", "tests", "node_modules",
"frontend-v2", "test-results", "playwright-report",
}
def _native_runtime_matches_target() -> bool:
machine = platform.machine().lower()
return (
sys.platform.startswith("linux")
and machine in {"amd64", "x86_64"}
and sys.version_info[:2] == (3, 11)
)
def validate_runtime_dependencies(package_root: Path) -> None:
"""Ensure the bundled Linux runtime is complete and actually importable."""
site_packages = package_root / "runtime" / "site-packages"
missing: list[str] = []
for package_name in ("cffi", "pycparser"):
if not (site_packages / package_name).is_dir():
missing.append(package_name)
if not any(site_packages.glob("_cffi_backend*.so")):
missing.append("_cffi_backend")
if missing:
raise ValueError(
"Docker update runtime is missing required dependencies: "
+ ", ".join(missing)
)
# The archive targets Linux AMD64 CPython 3.11. On the matching release
# runner, load the compiled modules too; other hosts still get the
# platform-neutral completeness checks above.
if not _native_runtime_matches_target():
return
command = [
sys.executable,
"-S",
"-c",
(
"import sys; "
"sys.path.insert(0, sys.argv[1]); "
"import _cffi_backend; "
"from cryptography import x509; "
"from cryptography.hazmat.bindings import _rust"
),
str(site_packages.resolve()),
]
environment = os.environ.copy()
environment.pop("PYTHONPATH", None)
environment["PYTHONNOUSERSITE"] = "1"
result = subprocess.run(
command,
cwd=package_root,
env=environment,
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
detail = (result.stderr or result.stdout).strip().splitlines()
message = detail[-1] if detail else f"exit code {result.returncode}"
raise ValueError(f"Docker update runtime import failed: {message}")
def validate_archive(archive: Path, expected_version: str | None = None) -> dict:
match = DOCKER_ASSET_RE.fullmatch(archive.name)
if not match:
raise ValueError(f"invalid Docker update asset name: {archive.name}")
filename_version = match.group("version")
if expected_version and filename_version != expected_version.strip().lstrip("vV"):
raise ValueError("Docker update filename version does not match the build version")
with zipfile.ZipFile(archive) as package:
for name in package.namelist():
parts = set(Path(name.replace("\\", "/")).parts)
if parts & FORBIDDEN_PARTS or Path(name).name.startswith(".env"):
raise ValueError(f"Docker update contains forbidden content: {name}")
temporary = Path(tempfile.mkdtemp(prefix="diceframe-docker-validate-"))
try:
package_root = safe_extract_package(archive, temporary)
if package_root.name != archive.stem:
raise ValueError("Docker update top-level directory must match the asset name")
manifest = validate_package_tree(package_root, expected_version=filename_version)
validate_runtime_dependencies(package_root)
return manifest
finally:
shutil.rmtree(temporary, ignore_errors=True)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("archive", type=Path)
parser.add_argument("--version", default=None)
args = parser.parse_args()
manifest = validate_archive(args.archive.resolve(), args.version)
print(
f"valid Docker update: version={manifest['version']} "
f"platform={manifest['platform']} runtime_api={manifest['runtime_api']}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())