pyodide/tools/update_cross_build_releases.py

128 lines
3.7 KiB
Python

import argparse
import hashlib
import json
import shutil
import tempfile
from collections.abc import Generator
from contextlib import contextmanager
from pathlib import Path
import requests
from packaging.version import Version
from pyodide_build.xbuildenv_releases import (
CrossBuildEnvMetaSpec,
CrossBuildEnvReleaseSpec,
)
METADATA_FILE = Path(__file__).parents[1] / "pyodide-cross-build-environments.json"
BASE_URL = "https://github.com/pyodide/pyodide/releases/download/{version}/xbuildenv-{version}.tar.bz2"
# Pyodide build version that is compatible with the latest cross-build environment
# Note for maintainers: update this value when there is a breaking changes in the cross-build environment
MIN_COMPATIBLE_PYODIDE_BUILD_VERSION = "0.26.0"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser("Update cross-build environments files")
parser.add_argument("version", help="version of the cross-build environment")
return parser.parse_args()
def get_archive(url: str) -> bytes:
resp = requests.get(url)
resp.raise_for_status()
return resp.content
def parse_env_var(content: str, var_name: str) -> str:
# A very dummy parser for env vars.
for line in content.splitlines():
if line.startswith(f"export {var_name}"):
return line.split("=")[1].strip()
return ""
@contextmanager
def extract_archive(archive: bytes) -> Generator[Path, None, None]:
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_dir_path = Path(tmp_dir)
archive_path = tmp_dir_path / "xbuildenv.tar.bz2"
archive_path.write_bytes(archive)
# Extract the archive
shutil.unpack_archive(str(archive_path), extract_dir=tmp_dir)
yield tmp_dir_path
def add_version(
raw_metadata: str,
version: str,
url: str,
digest: str,
python_version: str | None = None,
emscripten_version: str | None = None,
min_pyodide_build_version: str | None = None,
max_pyodide_build_version: str | None = None,
) -> str:
metadata = CrossBuildEnvMetaSpec.parse_raw(raw_metadata)
new_release = CrossBuildEnvReleaseSpec(
version=version,
url=url,
sha256=digest,
python_version=python_version or "FIXME",
emscripten_version=emscripten_version or "FIXME",
min_pyodide_build_version=min_pyodide_build_version or "FIXME",
# Max version is optional, and maintainers should update it when needed.
max_pyodide_build_version=max_pyodide_build_version or None,
)
metadata.releases[version] = new_release
# Sort releases in reverse order
metadata.releases = dict(
sorted(metadata.releases.items(), reverse=True, key=lambda x: Version(x[0]))
)
dictionary = metadata.dict(exclude_none=True)
return json.dumps(dictionary, indent=2)
def main():
args = parse_args()
version = args.version
full_url = BASE_URL.format(version=version)
content = get_archive(full_url)
digest = hashlib.sha256(content).hexdigest()
with extract_archive(content) as extracted:
makefile_path = extracted / "xbuildenv" / "pyodide-root" / "Makefile.envs"
makefile_content = makefile_path.read_text()
python_version = parse_env_var(makefile_content, "PYVERSION")
emscripten_version = parse_env_var(
makefile_content, "PYODIDE_EMSCRIPTEN_VERSION"
)
metadata = METADATA_FILE.read_text()
new_metadata = add_version(
metadata,
version,
full_url,
digest,
python_version=python_version,
emscripten_version=emscripten_version,
min_pyodide_build_version=MIN_COMPATIBLE_PYODIDE_BUILD_VERSION,
)
METADATA_FILE.write_text(new_metadata + "\n")
if __name__ == "__main__":
main()