Store SHA256 hashes when running update scripts and verify them
when downloading files at build time. This improves security by
detecting unexpected changes to downloaded artifacts.
Platforms covered: macOS (CPython, PyPy, GraalPy), iOS, Android,
virtualenv, and python-build-standalone. Windows (nuget) and
Linux (Docker) are excluded.
SHA256 sources per platform:
- macOS/iOS/Android CPython (python.org): sha256_sum from API
- GraalPy: .sha256 sidecar assets from GitHub releases
- python-build-standalone: SHA256SUMS file in release
- PyPy, BeeWare iOS, Maven (Chaquopy): stream-download and compute
Changes:
- cibuildwheel/util/file.py: add sha256 param to download()
- cibuildwheel/platforms/{macos,ios,android}.py: add sha256 to
PythonConfiguration and pass to download()
- cibuildwheel/venv.py: read sha256 from toml and pass to download()
- cibuildwheel/util/python_build_standalone.py: add sha256 to
PythonBuildStandaloneAsset and pass to download()
- cibuildwheel/resources/build-platforms.toml: add sha256 fields
- cibuildwheel/resources/virtualenv.toml: add sha256 field
- cibuildwheel/resources/python-build-standalone-releases.json: add sha256
- bin/update_pythons.py: compute/store sha256 per source strategy
- bin/update_virtualenv.py: compute sha256 by streaming download
- bin/update_python_build_standalone.py: parse SHA256SUMS file
Closes #908
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: copilot-cli:claude-sonnet-4.6
91 lines
2.7 KiB
Python
Executable File
91 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env -S uv run --script
|
|
|
|
# /// script
|
|
# dependencies = [
|
|
# "cibuildwheel",
|
|
# "requests",
|
|
# ]
|
|
#
|
|
# [tool.uv.sources]
|
|
# cibuildwheel = { path = ".." }
|
|
# ///
|
|
|
|
import json
|
|
|
|
import requests
|
|
|
|
from cibuildwheel.extra import github_api_request
|
|
from cibuildwheel.util.python_build_standalone import (
|
|
PythonBuildStandaloneAsset,
|
|
PythonBuildStandaloneReleaseData,
|
|
)
|
|
from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES
|
|
|
|
|
|
def main() -> None:
|
|
"""
|
|
This script updates the vendored list of release assets to the latest
|
|
version of astral-sh/python-build-standalone.
|
|
"""
|
|
|
|
# Get the latest release tag from the GitHub API
|
|
latest_release = github_api_request("repos/astral-sh/python-build-standalone/releases/latest")
|
|
latest_tag = latest_release["tag_name"]
|
|
|
|
# Get the list of assets for the latest release
|
|
github_assets = github_api_request(
|
|
f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
|
|
)["assets"]
|
|
|
|
# Build a sha256 map from the SHA256SUMS file in the release
|
|
sha256_sums_urls = [
|
|
ga["browser_download_url"] for ga in github_assets if ga["name"] == "SHA256SUMS"
|
|
]
|
|
name_to_sha256: dict[str, str] = {}
|
|
if sha256_sums_urls:
|
|
response = requests.get(sha256_sums_urls[0])
|
|
response.raise_for_status()
|
|
for line in response.text.splitlines():
|
|
parts = line.split()
|
|
if len(parts) == 2:
|
|
sha256_hex, filename = parts
|
|
# The filename may have a leading "./" or spaces - strip it
|
|
name_to_sha256[filename.lstrip("./")] = sha256_hex
|
|
|
|
assets = [
|
|
PythonBuildStandaloneAsset(
|
|
name=ga["name"],
|
|
url=ga["browser_download_url"],
|
|
sha256=name_to_sha256.get(ga["name"], ""),
|
|
)
|
|
for ga in github_assets
|
|
if ga["name"].endswith("install_only.tar.gz")
|
|
]
|
|
|
|
# Try to keep output order stable
|
|
assets = sorted(assets, key=lambda x: x["name"])
|
|
|
|
# Write the assets to the JSON file. One day, we might need to support
|
|
# multiple releases, but for now, we only support the latest one
|
|
json_file_contents = PythonBuildStandaloneReleaseData(
|
|
releases=[
|
|
{
|
|
"tag": latest_tag,
|
|
"assets": assets,
|
|
}
|
|
]
|
|
)
|
|
|
|
with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f:
|
|
json.dump(json_file_contents, f, indent=2)
|
|
# Add a trailing newline, our pre-commit hook requires it
|
|
f.write("\n")
|
|
|
|
print(
|
|
f"Updated {PYTHON_BUILD_STANDALONE_RELEASES.name} with {len(assets)} assets for tag {latest_tag}"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|