- Add sha256 field to Windows PythonConfiguration (PyPy/GraalPy have direct download URLs on Windows too) - Pass sha256 to install_pypy() and install_graalpy() in windows.py - Fix update_pythons.py: handle empty sha256 from CPython API (older versions) by streaming download to compute it; fix condition to check 'not sha256' rather than 'not in dict' - Fix update_virtualenv.py: compute sha256 even when version unchanged but sha256 is empty (first-time population) - Fix update_python_build_standalone.py: resolve file path relative to the script itself (not the installed package) so writes go to source checkout, not the uv cache - Populate actual sha256 values by running all three update scripts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: copilot-cli:claude-sonnet-4.6
153 lines
4.2 KiB
Python
Executable File
153 lines
4.2 KiB
Python
Executable File
#!/usr/bin/env -S uv run --script
|
|
|
|
# /// script
|
|
# dependencies = [
|
|
# "click",
|
|
# "packaging",
|
|
# "requests",
|
|
# "rich",
|
|
# "cibuildwheel",
|
|
# ]
|
|
#
|
|
# [tool.uv.sources]
|
|
# cibuildwheel = { path = ".." }
|
|
# ///
|
|
|
|
|
|
import dataclasses
|
|
import difflib
|
|
import hashlib
|
|
import logging
|
|
import tomllib
|
|
from pathlib import Path
|
|
from typing import Final
|
|
|
|
import click
|
|
import requests
|
|
import rich
|
|
from packaging.version import Version
|
|
from rich.logging import RichHandler
|
|
from rich.syntax import Syntax
|
|
|
|
from cibuildwheel.extra import github_api_request
|
|
|
|
log = logging.getLogger("cibw")
|
|
|
|
# Looking up the dir instead of using utils.resources_dir
|
|
# since we want to write to it.
|
|
DIR: Final[Path] = Path(__file__).parent.parent.resolve()
|
|
RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
|
|
|
|
GET_VIRTUALENV_GITHUB: Final[str] = "https://github.com/pypa/get-virtualenv"
|
|
GET_VIRTUALENV_URL_TEMPLATE: Final[str] = (
|
|
f"{GET_VIRTUALENV_GITHUB}/blob/{{version}}/public/virtualenv.pyz?raw=true"
|
|
)
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True, order=True)
|
|
class VersionTuple:
|
|
name: str
|
|
download_url: str
|
|
version: Version
|
|
|
|
|
|
def get_latest_virtualenv_release() -> VersionTuple:
|
|
response = github_api_request("repos/pypa/get-virtualenv/releases/latest")
|
|
tag_name = response["tag_name"]
|
|
|
|
asset = next(
|
|
(asset for asset in response["assets"] if asset["name"] == "virtualenv.pyz"),
|
|
None,
|
|
)
|
|
if not asset:
|
|
msg = "No asset named 'virtualenv.pyz' found in the latest release of get-virtualenv."
|
|
raise RuntimeError(msg)
|
|
|
|
return VersionTuple(
|
|
version=Version(tag_name), name=tag_name, download_url=asset["browser_download_url"]
|
|
)
|
|
|
|
|
|
@click.command()
|
|
@click.option("--force", is_flag=True)
|
|
@click.option(
|
|
"--level", default="INFO", type=click.Choice(["WARNING", "INFO", "DEBUG"], case_sensitive=False)
|
|
)
|
|
def update_virtualenv(force: bool, level: str) -> None:
|
|
logging.basicConfig(
|
|
level="INFO",
|
|
format="%(message)s",
|
|
datefmt="[%X]",
|
|
handlers=[RichHandler(rich_tracebacks=True, markup=True)],
|
|
)
|
|
log.setLevel(level)
|
|
|
|
toml_file_path = RESOURCES_DIR / "virtualenv.toml"
|
|
|
|
original_toml = toml_file_path.read_text()
|
|
with toml_file_path.open("rb") as f:
|
|
configurations = tomllib.load(f)
|
|
default = configurations.pop("default")
|
|
local_version = str(default["version"])
|
|
|
|
latest_release = get_latest_virtualenv_release()
|
|
|
|
if latest_release.version > Version(local_version):
|
|
version = latest_release.name
|
|
url = latest_release.download_url
|
|
sha256 = "" # recomputed below
|
|
else:
|
|
version = local_version
|
|
url = default["url"]
|
|
sha256 = default.get("sha256", "")
|
|
|
|
# Compute sha256 if not already stored (new version or first-time population)
|
|
if not sha256:
|
|
log.info("Computing sha256 for %s...", url)
|
|
response = requests.get(url, stream=True)
|
|
response.raise_for_status()
|
|
hasher = hashlib.sha256()
|
|
for chunk in response.iter_content(65536):
|
|
hasher.update(chunk)
|
|
sha256 = hasher.hexdigest()
|
|
|
|
configurations["default"] = {
|
|
"version": version,
|
|
"url": url,
|
|
"sha256": sha256,
|
|
}
|
|
result_toml = "".join(
|
|
f'{key} = {{ version = "{value["version"]}", url = "{value["url"]}", sha256 = "{value.get("sha256", "")}" }}\n'
|
|
for key, value in configurations.items()
|
|
)
|
|
|
|
rich.print() # spacer
|
|
|
|
if original_toml == result_toml:
|
|
rich.print("[green]Check complete, virtualenv version unchanged.")
|
|
return
|
|
|
|
rich.print("virtualenv version updated.")
|
|
rich.print("Changes:")
|
|
rich.print()
|
|
|
|
toml_relpath = toml_file_path.relative_to(DIR).as_posix()
|
|
diff_lines = difflib.unified_diff(
|
|
original_toml.splitlines(keepends=True),
|
|
result_toml.splitlines(keepends=True),
|
|
fromfile=toml_relpath,
|
|
tofile=toml_relpath,
|
|
)
|
|
rich.print(Syntax("".join(diff_lines), "diff", theme="ansi_light"))
|
|
rich.print()
|
|
|
|
if force:
|
|
toml_file_path.write_text(result_toml)
|
|
rich.print("[green]TOML file updated.")
|
|
else:
|
|
rich.print("[yellow]File left unchanged. Use --force flag to update.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
update_virtualenv()
|