feat: add SBOM generation and build provenance attestation to release workflows (#69)

* feat: add SBOM generation and build provenance attestation to release workflows

* docs: update release notes template with SBOM unreleased entry
This commit is contained in:
mwisnowski 2026-04-02 10:44:13 -07:00 committed by GitHub
parent 6d1d5a1822
commit 75184a5967
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 90 additions and 5 deletions

View file

@ -119,6 +119,7 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
tags: ${{ steps.arch_tag.outputs.tag }} tags: ${{ steps.arch_tag.outputs.tag }}
labels: ${{ needs.prepare.outputs.labels }} labels: ${{ needs.prepare.outputs.labels }}
provenance: mode=max
build-args: | build-args: |
APP_VERSION=${{ needs.prepare.outputs.version }} APP_VERSION=${{ needs.prepare.outputs.version }}
@ -171,6 +172,7 @@ jobs:
platforms: linux/arm64 platforms: linux/arm64
tags: ${{ steps.arch_tag.outputs.tag }} tags: ${{ steps.arch_tag.outputs.tag }}
labels: ${{ needs.prepare.outputs.labels }} labels: ${{ needs.prepare.outputs.labels }}
provenance: mode=max
build-args: | build-args: |
APP_VERSION=${{ needs.prepare.outputs.version }} APP_VERSION=${{ needs.prepare.outputs.version }}
@ -178,6 +180,10 @@ jobs:
name: Create latest multi-arch manifest name: Create latest multi-arch manifest
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [prepare, build_amd64, build_arm64] needs: [prepare, build_amd64, build_arm64]
permissions:
contents: write
id-token: write
attestations: write
steps: steps:
- name: Docker Hub login - name: Docker Hub login
uses: docker/login-action@v3.5.0 uses: docker/login-action@v3.5.0
@ -197,3 +203,38 @@ jobs:
echo "Inspecting latest" echo "Inspecting latest"
docker buildx imagetools inspect mwisnowski/mtg-python-deckbuilder:latest docker buildx imagetools inspect mwisnowski/mtg-python-deckbuilder:latest
- name: Extract manifest digest
id: inspect
shell: bash
run: |
DIGEST=$(docker buildx imagetools inspect mwisnowski/mtg-python-deckbuilder:latest \
| grep -E '^Digest:' | awk '{print $2}' | head -1)
echo "Manifest digest: $DIGEST"
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
- name: Attest image build provenance
uses: actions/attest-build-provenance@v2.2.3
with:
subject-name: index.docker.io/mwisnowski/mtg-python-deckbuilder
subject-digest: ${{ steps.inspect.outputs.digest }}
push-to-registry: false
- name: Generate image SBOM (CycloneDX)
uses: anchore/sbom-action@v0.24.0
with:
image: mwisnowski/mtg-python-deckbuilder:latest
format: cyclonedx-json
output-file: sbom-image-${{ needs.prepare.outputs.version }}.cyclonedx.json
upload-artifact: false
upload-release-assets: false
- name: Upload image SBOM to GitHub Release
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ needs.prepare.outputs.version }} \
sbom-image-${{ needs.prepare.outputs.version }}.cyclonedx.json \
--repo ${{ github.repository }} \
--clobber

View file

@ -56,6 +56,24 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.0
- name: Generate source SBOM (SPDX)
uses: anchore/sbom-action@v0.24.0
with:
path: .
format: spdx-json
output-file: sbom-source.spdx.json
upload-artifact: false
upload-release-assets: false
- name: Generate source SBOM (CycloneDX)
uses: anchore/sbom-action@v0.24.0
with:
path: .
format: cyclonedx-json
output-file: sbom-source.cyclonedx.json
upload-artifact: false
upload-release-assets: false
- name: Prepare release notes - name: Prepare release notes
id: notes id: notes
shell: bash shell: bash
@ -84,5 +102,8 @@ jobs:
tag_name: ${{ steps.notes.outputs.version }} tag_name: ${{ steps.notes.outputs.version }}
name: ${{ steps.notes.outputs.version }} name: ${{ steps.notes.outputs.version }}
body_path: ${{ steps.notes.outputs.notes_file }} body_path: ${{ steps.notes.outputs.notes_file }}
files: |
sbom-source.spdx.json
sbom-source.cyclonedx.json
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -8,7 +8,8 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning
- Link PRs/issues inline when helpful, e.g., (#123) or [#123]. Reference-style links at the bottom are encouraged for readability. - Link PRs/issues inline when helpful, e.g., (#123) or [#123]. Reference-style links at the bottom are encouraged for readability.
## [Unreleased] ## [Unreleased]
_No unreleased changes yet._ ### Added
- **SBOM & supply chain provenance**: Every tagged release now attaches source SBOMs (SPDX + CycloneDX JSON) for Python dependencies and a CycloneDX container image SBOM to the GitHub Release assets. Build provenance attestations (SLSA-style) are published for the multi-arch Docker image via the GitHub Attestations API. `provenance: mode=max` is enabled on all arch builds.
## [4.5.2] - 2026-04-01 ## [4.5.2] - 2026-04-01
### Added ### Added

View file

@ -34,6 +34,7 @@ A web-first Commander/EDH deckbuilder with a shared core for CLI, headless, and
- [Development setup](#development-setup) - [Development setup](#development-setup)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [Contributing](#contributing) - [Contributing](#contributing)
- [Supply chain security](#supply-chain-security)
- [License & attribution](#license--attribution) - [License & attribution](#license--attribution)
- [Further reading](#further-reading) - [Further reading](#further-reading)
@ -446,6 +447,30 @@ Pull requests are welcome—follow the conventional commit style, keep diffs foc
--- ---
## Supply chain security
Every tagged release includes SBOM (Software Bill of Materials) files attached to the GitHub Release assets:
| File | Format | Contents |
|------|--------|----------|
| `sbom-source.spdx.json` | SPDX 2.x JSON | Python dependency tree from source |
| `sbom-source.cyclonedx.json` | CycloneDX JSON | Python dependency tree from source |
| `sbom-image-vX.Y.Z.cyclonedx.json` | CycloneDX JSON | Full container image (OS + app) |
Build provenance attestations are published to the GitHub Attestations API for the multi-arch container image. To verify:
```bash
gh attestation verify oci://docker.io/mwisnowski/mtg-python-deckbuilder:latest \
--repo mwisnowski/mtg_python_deckbuilder
```
To inspect an SBOM locally (requires [Syft](https://github.com/anchore/syft)):
```bash
syft convert sbom-source.cyclonedx.json -o table
```
---
## License & attribution ## License & attribution
Licensed under the [MIT License](LICENSE). Card data and imagery are provided by [Scryfall](https://scryfall.com); please respect their [API terms](https://scryfall.com/docs/api). Licensed under the [MIT License](LICENSE). Card data and imagery are provided by [Scryfall](https://scryfall.com); please respect their [API terms](https://scryfall.com/docs/api).

View file

@ -1,9 +1,6 @@
# MTG Python Deckbuilder # MTG Python Deckbuilder
## [Unreleased] ## [Unreleased]
_No unreleased changes yet._
## [4.5.2] - 2026-04-01
### Added ### Added
- Hover-intent prefetch (`WEB_PREFETCH=1`): hovering the Open button on the Finished Decks page prefetches the deck view after 100 ms, making it load near-instantly. Off by default; Data Saver / slow connections are respected automatically. - **SBOM & supply chain provenance**: Every tagged release now attaches source SBOMs (SPDX + CycloneDX JSON) for Python dependencies and a CycloneDX container image SBOM to the GitHub Release assets. Build provenance attestations (SLSA-style) are published for the multi-arch Docker image via the GitHub Attestations API. `provenance: mode=max` is enabled on all arch builds.