3131 run : uv run --python 3.14 mkdocs build --strict
3232 - name : Build package
3333 run : uv build
34+ - name : Download Syft
35+ id : syft
36+ uses : anchore/sbom-action/download-syft@v0
37+ - name : Generate release integrity artifacts
38+ env :
39+ SYFT : ${{ steps.syft.outputs.cmd }}
40+ run : bash scripts/generate-release-integrity-artifacts.sh dist .release-integrity.env
3441 - name : Coveralls
3542 if : github.event_name == 'push'
3643 env :
@@ -122,6 +129,8 @@ jobs:
122129 runs-on : ubuntu-latest
123130 permissions :
124131 contents : write
132+ attestations : write
133+ id-token : write
125134 steps :
126135 - uses : actions/checkout@v4
127136 with :
@@ -135,6 +144,51 @@ jobs:
135144 run : uv sync --python 3.14
136145 - name : Build package
137146 run : uv build
147+ - name : Download Syft
148+ id : syft
149+ uses : anchore/sbom-action/download-syft@v0
150+ - name : Generate release integrity artifacts
151+ env :
152+ SYFT : ${{ steps.syft.outputs.cmd }}
153+ run : bash scripts/generate-release-integrity-artifacts.sh dist .release-integrity.env
154+ - name : Load release integrity metadata
155+ id : integrity
156+ run : |
157+ source .release-integrity.env
158+ {
159+ printf 'wheel=%s\n' "$wheel"
160+ printf 'wheel_name=%s\n' "$wheel_name"
161+ printf 'wheel_sbom=%s\n' "$wheel_sbom"
162+ printf 'wheel_sbom_name=%s\n' "$wheel_sbom_name"
163+ printf 'sdist=%s\n' "$sdist"
164+ printf 'sdist_name=%s\n' "$sdist_name"
165+ printf 'sdist_sbom=%s\n' "$sdist_sbom"
166+ printf 'sdist_sbom_name=%s\n' "$sdist_sbom_name"
167+ printf 'checksums=%s\n' "$checksums"
168+ printf 'checksums_name=%s\n' "$checksums_name"
169+ } >> "$GITHUB_OUTPUT"
170+ - name : Attest build provenance
171+ id : attest-provenance
172+ uses : actions/attest-build-provenance@v3
173+ with :
174+ subject-path : |
175+ ${{ steps.integrity.outputs.wheel }}
176+ ${{ steps.integrity.outputs.sdist }}
177+ show-summary : false
178+ - name : Attest wheel SBOM
179+ id : attest-wheel-sbom
180+ uses : actions/attest-sbom@v3
181+ with :
182+ subject-path : ${{ steps.integrity.outputs.wheel }}
183+ sbom-path : ${{ steps.integrity.outputs.wheel_sbom }}
184+ show-summary : false
185+ - name : Attest source distribution SBOM
186+ id : attest-sdist-sbom
187+ uses : actions/attest-sbom@v3
188+ with :
189+ subject-path : ${{ steps.integrity.outputs.sdist }}
190+ sbom-path : ${{ steps.integrity.outputs.sdist_sbom }}
191+ show-summary : false
138192 - name : Upload workflow artifact
139193 uses : actions/upload-artifact@v4
140194 with :
@@ -144,6 +198,77 @@ jobs:
144198 env :
145199 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
146200 run : gh release upload "${{ needs.release.outputs.tag }}" dist/* --clobber
201+ - name : Publish verification summary
202+ env :
203+ RELEASE_TAG : ${{ needs.release.outputs.tag }}
204+ RELEASE_COMMIT_SHA : ${{ needs.release.outputs.commit_sha }}
205+ PROVENANCE_URL : ${{ steps.attest-provenance.outputs.attestation-url }}
206+ WHEEL_SBOM_URL : ${{ steps.attest-wheel-sbom.outputs.attestation-url }}
207+ SDIST_SBOM_URL : ${{ steps.attest-sdist-sbom.outputs.attestation-url }}
208+ run : |
209+ source .release-integrity.env
210+ wheel_sha="$(awk -v file="$wheel_name" '$2 == file { print $1 }' "$checksums")"
211+ sdist_sha="$(awk -v file="$sdist_name" '$2 == file { print $1 }' "$checksums")"
212+
213+ cat > dist/release-verification.md <<EOF
214+ <!-- release-verification:start -->
215+ ## Verification
216+
217+ Release commit: \`$RELEASE_COMMIT_SHA\`
218+
219+ | Artifact | SHA256 | SPDX SBOM | Provenance | SBOM attestation |
220+ | --- | --- | --- | --- | --- |
221+ | \`$wheel_name\` | \`$wheel_sha\` | \`$wheel_sbom_name\` | [build provenance]($PROVENANCE_URL) | [SBOM attestation]($WHEEL_SBOM_URL) |
222+ | \`$sdist_name\` | \`$sdist_sha\` | \`$sdist_sbom_name\` | [build provenance]($PROVENANCE_URL) | [SBOM attestation]($SDIST_SBOM_URL) |
223+
224+ Release assets also include \`$checksums_name\`, \`$wheel_sbom_name\`, and \`$sdist_sbom_name\`.
225+
226+ \`\`\`bash
227+ gh release verify "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY"
228+ gh release verify-asset "$RELEASE_TAG" "./$wheel_name" --repo "$GITHUB_REPOSITORY"
229+ gh release verify-asset "$RELEASE_TAG" "./$sdist_name" --repo "$GITHUB_REPOSITORY"
230+ gh attestation verify "./$wheel_name" --repo "$GITHUB_REPOSITORY" --signer-workflow .github/workflows/release.yml
231+ gh attestation verify "./$wheel_name" --repo "$GITHUB_REPOSITORY" --signer-workflow .github/workflows/release.yml --predicate-type https://spdx.dev/Document/v2.3
232+ \`\`\`
233+
234+ Install from Git with an immutable ref, not \`master\`. Example:
235+
236+ \`\`\`bash
237+ pip install "fastapi-paseto @ git+https://github.com/Raze-Systems/fastapi-paseto.git@$RELEASE_TAG"
238+ \`\`\`
239+
240+ \`pip\` and \`uv\` record VCS origin metadata in \`direct_url.json\`, but mutable branch installs cannot be strongly verified after the fact. Prefer a signed tag or commit hash and verify the matching release artifacts.
241+ <!-- release-verification:end -->
242+ EOF
243+
244+ cat dist/release-verification.md >> "$GITHUB_STEP_SUMMARY"
245+ - name : Update release notes with verification details
246+ env :
247+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
248+ RELEASE_TAG : ${{ needs.release.outputs.tag }}
249+ run : |
250+ gh release view "$RELEASE_TAG" --json body --jq .body > dist/release-body.md
251+ python - <<'PY'
252+ from pathlib import Path
253+
254+ start_marker = "<!-- release-verification:start -->"
255+ end_marker = "<!-- release-verification:end -->"
256+ body_path = Path("dist/release-body.md")
257+ section = Path("dist/release-verification.md").read_text(encoding="utf-8").strip()
258+ body = body_path.read_text(encoding="utf-8").strip()
259+
260+ if start_marker in body and end_marker in body:
261+ prefix, remainder = body.split(start_marker, 1)
262+ _, suffix = remainder.split(end_marker, 1)
263+ body = prefix.rstrip()
264+ suffix = suffix.lstrip()
265+ merged = "\n\n".join(part for part in [body, section, suffix] if part)
266+ else:
267+ merged = "\n\n".join(part for part in [body, section] if part)
268+
269+ body_path.write_text(f"{merged}\n", encoding="utf-8")
270+ PY
271+ gh release edit "$RELEASE_TAG" --notes-file dist/release-body.md
147272
148273 build-release-docs :
149274 if : needs.release.outputs.released == 'true'
0 commit comments