Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.9] gh-91172: Create a workflow for verifying bundled pip and setuptools (GH-31885) #94123

Merged
merged 1 commit into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/verify-ensurepip-wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Verify bundled pip and setuptools

on:
workflow_dispatch:
push:
paths:
- 'Lib/ensurepip/_bundled/**'
- '.github/workflows/verify-ensurepip-wheels.yml'
- 'Tools/scripts/verify_ensurepip_wheels.py'
pull_request:
paths:
- 'Lib/ensurepip/_bundled/**'
- '.github/workflows/verify-ensurepip-wheels.yml'
- 'Tools/scripts/verify_ensurepip_wheels.py'

permissions:
contents: read

jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3'
- name: Compare checksums of bundled pip and setuptools to ones published on PyPI
run: ./Tools/scripts/verify_ensurepip_wheels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Create a GitHub Actions workflow for verifying bundled pip and setuptools.
Patch by Illia Volochii and Adam Turner.
98 changes: 98 additions & 0 deletions Tools/scripts/verify_ensurepip_wheels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#! /usr/bin/env python3

"""
Compare checksums for wheels in :mod:`ensurepip` against the Cheeseshop.

When GitHub Actions executes the script, output is formatted accordingly.
https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message
"""

import hashlib
import json
import os
import re
from pathlib import Path
from urllib.request import urlopen

PACKAGE_NAMES = ("pip", "setuptools")
ENSURE_PIP_ROOT = Path(__file__).parent.parent.parent / "Lib/ensurepip"
WHEEL_DIR = ENSURE_PIP_ROOT / "_bundled"
ENSURE_PIP_INIT_PY_TEXT = (ENSURE_PIP_ROOT / "__init__.py").read_text(encoding="utf-8")
GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true"


def print_notice(file_path: str, message: str) -> None:
if GITHUB_ACTIONS:
message = f"::notice file={file_path}::{message}"
print(message, end="\n\n")


def print_error(file_path: str, message: str) -> None:
if GITHUB_ACTIONS:
message = f"::error file={file_path}::{message}"
print(message, end="\n\n")


def verify_wheel(package_name: str) -> bool:
# Find the package on disk
package_path = next(WHEEL_DIR.glob(f"{package_name}*.whl"), None)
if not package_path:
print_error("", f"Could not find a {package_name} wheel on disk.")
return False

print(f"Verifying checksum for {package_path}.")

# Find the version of the package used by ensurepip
package_version_match = re.search(
f'_{package_name.upper()}_VERSION = "([^"]+)', ENSURE_PIP_INIT_PY_TEXT
)
if not package_version_match:
print_error(
package_path,
f"No {package_name} version found in Lib/ensurepip/__init__.py.",
)
return False
package_version = package_version_match[1]

# Get the SHA 256 digest from the Cheeseshop
try:
raw_text = urlopen(f"https://pypi.org/pypi/{package_name}/json").read()
except (OSError, ValueError):
print_error(package_path, f"Could not fetch JSON metadata for {package_name}.")
return False

release_files = json.loads(raw_text)["releases"][package_version]
for release_info in release_files:
if package_path.name != release_info["filename"]:
continue
expected_digest = release_info["digests"].get("sha256", "")
break
else:
print_error(package_path, f"No digest for {package_name} found from PyPI.")
return False

# Compute the SHA 256 digest of the wheel on disk
actual_digest = hashlib.sha256(package_path.read_bytes()).hexdigest()

print(f"Expected digest: {expected_digest}")
print(f"Actual digest: {actual_digest}")

if actual_digest != expected_digest:
print_error(
package_path, f"Failed to verify the checksum of the {package_name} wheel."
)
return False

print_notice(
package_path,
f"Successfully verified the checksum of the {package_name} wheel.",
)
return True


if __name__ == "__main__":
exit_status = 0
for package_name in PACKAGE_NAMES:
if not verify_wheel(package_name):
exit_status = 1
raise SystemExit(exit_status)