Release tool improvements (#80641)

* Provide reproducible sdist builds.
* Use reproducible wheel builds.
* Add PyPI artifact checks.
pull/80689/head
Matt Clay 2 years ago committed by GitHub
parent 06dcb4ad7a
commit d37678c5ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,6 +10,7 @@ import dataclasses
import datetime import datetime
import enum import enum
import functools import functools
import gzip
import hashlib import hashlib
import http.client import http.client
import inspect import inspect
@ -21,6 +22,7 @@ import re
import secrets import secrets
import shlex import shlex
import shutil import shutil
import stat
import subprocess import subprocess
import sys import sys
import tarfile import tarfile
@ -765,6 +767,41 @@ def set_ansible_version(current_version: Version, requested_version: Version) ->
ANSIBLE_RELEASE_FILE.write_text(updated) ANSIBLE_RELEASE_FILE.write_text(updated)
def create_reproducible_sdist(original_path: pathlib.Path, output_path: pathlib.Path, mtime: int) -> None:
"""Read the specified sdist and write out a new copy with uniform file metadata at the specified location."""
with tarfile.open(original_path) as original_archive:
with tempfile.TemporaryDirectory() as temp_dir:
tar_file = pathlib.Path(temp_dir) / "sdist.tar"
with tarfile.open(tar_file, mode="w") as tar_archive:
for original_info in original_archive.getmembers(): # type: tarfile.TarInfo
tar_archive.addfile(create_reproducible_tar_info(original_info, mtime), original_archive.extractfile(original_info))
with tar_file.open("rb") as tar_archive:
with gzip.GzipFile(output_path, "wb", mtime=mtime) as output_archive:
shutil.copyfileobj(tar_archive, output_archive)
def create_reproducible_tar_info(original: tarfile.TarInfo, mtime: int) -> tarfile.TarInfo:
"""Return a copy of the given TarInfo with uniform file metadata."""
sanitized = tarfile.TarInfo()
sanitized.name = original.name
sanitized.size = original.size
sanitized.mtime = mtime
sanitized.mode = (original.mode & ~(stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)) | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR
sanitized.type = original.type
sanitized.linkname = original.linkname
sanitized.uid = 0
sanitized.gid = 0
sanitized.uname = "root"
sanitized.gname = "root"
if original.mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
sanitized.mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
return sanitized
def test_built_artifact(path: pathlib.Path) -> None: def test_built_artifact(path: pathlib.Path) -> None:
"""Test the specified built artifact by installing it in a venv and running some basic commands.""" """Test the specified built artifact by installing it in a venv and running some basic commands."""
with tempfile.TemporaryDirectory() as temp_dir_name: with tempfile.TemporaryDirectory() as temp_dir_name:
@ -823,6 +860,12 @@ def get_release_artifact_details(repository: str, version: Version, validate: bo
artifacts = [describe_release_artifact(version, item, validate) for item in data["urls"]] artifacts = [describe_release_artifact(version, item, validate) for item in data["urls"]]
expected_artifact_types = {"bdist_wheel", "sdist"}
found_artifact_types = set(artifact.package_type for artifact in artifacts)
if found_artifact_types != expected_artifact_types:
raise RuntimeError(f"Expected {expected_artifact_types} artifact types, but found {found_artifact_types} instead.")
return artifacts return artifacts
@ -1228,12 +1271,18 @@ def build(allow_dirty: bool = False) -> None:
temp_dir = pathlib.Path(temp_dir_name) temp_dir = pathlib.Path(temp_dir_name)
dist_dir = temp_dir / "dist" dist_dir = temp_dir / "dist"
commit_time = int(git("show", "-s", "--format=%ct", capture_output=True).stdout)
env.update(
SOURCE_DATE_EPOCH=str(commit_time),
)
git("worktree", "add", "-d", temp_dir) git("worktree", "add", "-d", temp_dir)
try: try:
run("python", "-m", "build", "--config-setting=--build-manpages", env=env, cwd=temp_dir) run("python", "-m", "build", "--config-setting=--build-manpages", env=env, cwd=temp_dir)
get_sdist_path(version, dist_dir).rename(sdist_file) create_reproducible_sdist(get_sdist_path(version, dist_dir), sdist_file, commit_time)
get_wheel_path(version, dist_dir).rename(wheel_file) get_wheel_path(version, dist_dir).rename(wheel_file)
finally: finally:
git("worktree", "remove", temp_dir) git("worktree", "remove", temp_dir)

Loading…
Cancel
Save