diff --git a/.github/build-requirements.txt b/.github/build-requirements.txt new file mode 100644 index 0000000..d53e503 --- /dev/null +++ b/.github/build-requirements.txt @@ -0,0 +1,5 @@ +# Keep these hashes synchronized with the exact versions in pyproject.toml. +setuptools==80.9.0 \ + --hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 +wheel==0.45.1 \ + --hash=sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 32f4d2a..dbec2cd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,31 +13,51 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: '3.12' - name: Install build dependencies - run: | - python -m pip install --upgrade pip - python -m pip install build twine + run: python -m pip install --require-hashes -r .github/build-requirements.txt - name: Build package - run: python -m build + run: | + python - <<'PY' + from pathlib import Path + from setuptools import build_meta + + Path("release/packages").mkdir(parents=True) + build_meta.build_sdist("release/packages") + build_meta.build_wheel("release/packages") + PY - name: Validate distributions - run: python -m twine check dist/* + run: | + python - <<'PY' + import tarfile + import zipfile + from pathlib import Path + + artifacts = sorted(Path("release/packages").iterdir()) + assert {path.suffix for path in artifacts} == {".gz", ".whl"}, artifacts + with tarfile.open(next(path for path in artifacts if path.suffix == ".gz")) as archive: + archive.getmembers() + with zipfile.ZipFile(next(path for path in artifacts if path.suffix == ".whl")) as archive: + assert archive.testzip() is None + PY + cd release + sha256sum --binary packages/*.tar.gz packages/*.whl > SHA256SUMS - name: Upload built distributions - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: python-package-distributions - path: dist/ + path: release/ publish-to-testpypi: name: Publish to TestPyPI 🧪 @@ -50,14 +70,27 @@ jobs: id-token: write steps: - name: Download distribution artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: python-package-distributions - path: dist/ + path: release/ + + - name: Verify distribution artifacts + working-directory: release + run: | + manifest_files="$(sed -n 's/^[0-9a-f]\{64\} [*]packages\/\(.*\)$/\1/p' SHA256SUMS | LC_ALL=C sort)" + package_entries="$(find packages -mindepth 1 -maxdepth 1 -printf '%f\n' | LC_ALL=C sort)" + if [ "$manifest_files" != "$package_entries" ]; then + echo "SHA256SUMS does not list exactly the package directory contents" >&2 + diff --unified <(printf '%s\n' "$manifest_files") <(printf '%s\n' "$package_entries") || true + exit 1 + fi + sha256sum --strict --check SHA256SUMS - name: Publish to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # release/v1 with: + packages-dir: release/packages/ repository-url: https://test.pypi.org/legacy/ publish-to-pypi: @@ -71,10 +104,24 @@ jobs: id-token: write steps: - name: Download distribution artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: python-package-distributions - path: dist/ + path: release/ + + - name: Verify distribution artifacts + working-directory: release + run: | + manifest_files="$(sed -n 's/^[0-9a-f]\{64\} [*]packages\/\(.*\)$/\1/p' SHA256SUMS | LC_ALL=C sort)" + package_entries="$(find packages -mindepth 1 -maxdepth 1 -printf '%f\n' | LC_ALL=C sort)" + if [ "$manifest_files" != "$package_entries" ]; then + echo "SHA256SUMS does not list exactly the package directory contents" >&2 + diff --unified <(printf '%s\n' "$manifest_files") <(printf '%s\n' "$package_entries") || true + exit 1 + fi + sha256sum --strict --check SHA256SUMS - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # release/v1 + with: + packages-dir: release/packages/ diff --git a/pyproject.toml b/pyproject.toml index a1424e1..8bf74ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=70.0.0", "wheel"] +requires = ["setuptools==80.9.0", "wheel==0.45.1"] build-backend = "setuptools.build_meta" [project]