Merge #106: proofbundle 3.6.2 — security patch (never-raise/DoS, 5 it… #44
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # Build the distribution ONCE, attach a signed SLSA build provenance attestation to those exact | |
| # bytes, then publish *those same bytes* to PyPI via Trusted Publishing (OIDC, no token). The | |
| # artifact that lands on PyPI is the artifact that was attested — proven by a sha256 gate before | |
| # upload. This is the same trust-layer idea the library verifies: what you ship must carry a | |
| # verifiable proof of where it came from, and the proof must cover the shipped bytes, not a rebuild. | |
| # | |
| # Supply-chain hardening: all actions are pinned to full commit SHAs (version in a comment), not | |
| # tags — after the tj-actions incident (March 2025) a tag can be silently repointed. Dependabot | |
| # (.github/dependabot.yml) bumps the pin. The `pypi` environment SHOULD have required reviewers | |
| # configured (repo Settings → Environments → pypi) so a `v*` tag cannot publish unreviewed. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| permissions: | |
| contents: read | |
| jobs: | |
| build-and-attest: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # create the GitHub Release | |
| id-token: write # keyless OIDC (build-provenance attestation) | |
| attestations: write # record the provenance attestation | |
| outputs: | |
| digest-wheel: ${{ steps.digests.outputs.wheel }} | |
| digest-sdist: ${{ steps.digests.outputs.sdist }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.12" | |
| - name: Pre-tag adversarial audit gate (Front-Load F7 — the audit ran for THIS release) | |
| # The six-lens / master-prompt-v2 adversarial audit is a gate before EVERY tag, not only the | |
| # audit-candidate (Loek's decoy-parent finding landed AFTER 3.3.0 shipped). This step fails | |
| # the release if the CHANGELOG section for the tag records no adversarial/N-lens audit and no | |
| # audit_artifacts file names the version — enforcing the discipline the project already follows. | |
| run: python scripts/pre_tag_audit_gate.py --repo . --version "${GITHUB_REF_NAME#v}" --strict | |
| - name: Build sdist and wheel (the ONLY build in this workflow) | |
| run: | | |
| python -m pip install --upgrade build | |
| # F2: ship the byte-reproducible, normalised sdist (SOURCE_DATE_EPOCH + canonical tar), | |
| # then the wheel alongside it. Same dist/ layout as before; the reproducibility gate | |
| # (published-artifact-gate.yml) proves two builds are byte-identical. | |
| # PB-2026-0717-03: export the SAME epoch build_reproducible.py uses (HEAD commit time) so the | |
| # WHEEL is byte-reproducible too — a bare `python -m build --wheel` embeds the current | |
| # wall-clock mtime and is NOT reproducible across two clean builds. | |
| export SOURCE_DATE_EPOCH="$(git log -1 --format=%ct)" | |
| python scripts/build_reproducible.py --outdir dist | |
| python -m build --wheel --outdir dist | |
| - name: Record artifact digests (the attested subjects) | |
| id: digests | |
| run: | | |
| echo "wheel=$(sha256sum dist/*.whl | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" | |
| echo "sdist=$(sha256sum dist/*.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" | |
| sha256sum dist/* | tee dist/SHA256SUMS | |
| - name: Generate SLSA build provenance for the artifacts | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: "dist/*.whl,dist/*.tar.gz" | |
| - name: Upload the attested dist for the publish job (exact bytes, no rebuild) | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: attested-dist | |
| path: dist/ | |
| if-no-files-found: error | |
| - name: Classify the tag (PEP 440 pre-release → GitHub pre-release, not Latest) | |
| id: relmeta | |
| run: | | |
| # vX.Y.ZaN / bN / rcN is a PEP 440 pre-release (beta 2.0.0b2 etc.). Such a release must NOT | |
| # become the GitHub "Latest" (that would shadow the stable line). 2026-07-05: v2.0.0b2 was | |
| # wrongly published as Latest because this step did not exist. | |
| if [[ "${GITHUB_REF_NAME}" =~ (a|b|rc)[0-9]+$ ]]; then | |
| echo "prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "prerelease=false" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create GitHub Release with the attested artifacts + SHA256SUMS | |
| uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 | |
| with: | |
| files: | | |
| dist/*.whl | |
| dist/*.tar.gz | |
| dist/SHA256SUMS | |
| generate_release_notes: true | |
| prerelease: ${{ steps.relmeta.outputs.prerelease }} | |
| make_latest: ${{ steps.relmeta.outputs.make_latest }} | |
| publish-pypi: | |
| # Publishes with PyPI Trusted Publishing (OIDC) — no API token. Requires a one-time human | |
| # step: configure b7n0de/proofbundle + this workflow as a trusted publisher on pypi.org | |
| # (Settings → Publishing) BEFORE the first tag. Until then this job fails cleanly and the | |
| # Release above still succeeds. The `pypi` environment SHOULD require reviewer approval. | |
| needs: build-and-attest | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/proofbundle/ | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download the ATTESTED dist (no rebuild — published == attested) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: attested-dist | |
| path: dist/ | |
| - name: Gate — published bytes must equal the attested subjects | |
| run: | | |
| set -euo pipefail | |
| rm -f dist/SHA256SUMS # not a distributable; keep only the wheel + sdist for upload | |
| got_wheel=$(sha256sum dist/*.whl | cut -d' ' -f1) | |
| got_sdist=$(sha256sum dist/*.tar.gz | cut -d' ' -f1) | |
| echo "attested wheel: ${{ needs.build-and-attest.outputs.digest-wheel }}" | |
| echo "downloaded wheel: $got_wheel" | |
| test "$got_wheel" = "${{ needs.build-and-attest.outputs.digest-wheel }}" | |
| test "$got_sdist" = "${{ needs.build-and-attest.outputs.digest-sdist }}" | |
| echo "OK: publishing the exact bytes that were attested." | |
| - name: Publish to PyPI (Trusted Publishing, OIDC) | |
| uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 | |
| with: | |
| # PyPI recomputes and records PEP 740 attestations for these uploaded files; because | |
| # they are the attested artifacts (gate above), the PyPI attestation and the SLSA | |
| # provenance describe the same bytes. skip-existing keeps a re-tag idempotent. | |
| packages-dir: dist/ | |
| skip-existing: true |