Skip to content

Merge #106: proofbundle 3.6.2 — security patch (never-raise/DoS, 5 it… #44

Merge #106: proofbundle 3.6.2 — security patch (never-raise/DoS, 5 it…

Merge #106: proofbundle 3.6.2 — security patch (never-raise/DoS, 5 it… #44

Workflow file for this run

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