diff --git a/.github/workflows/dockerhub-publish.yml b/.github/workflows/dockerhub-publish.yml index 1e26bc2..689b9c3 100644 --- a/.github/workflows/dockerhub-publish.yml +++ b/.github/workflows/dockerhub-publish.yml @@ -119,6 +119,7 @@ jobs: platforms: linux/amd64 tags: ${{ steps.arch_tag.outputs.tag }} labels: ${{ needs.prepare.outputs.labels }} + provenance: mode=max build-args: | APP_VERSION=${{ needs.prepare.outputs.version }} @@ -171,6 +172,7 @@ jobs: platforms: linux/arm64 tags: ${{ steps.arch_tag.outputs.tag }} labels: ${{ needs.prepare.outputs.labels }} + provenance: mode=max build-args: | APP_VERSION=${{ needs.prepare.outputs.version }} @@ -178,6 +180,10 @@ jobs: name: Create latest multi-arch manifest runs-on: ubuntu-latest needs: [prepare, build_amd64, build_arm64] + permissions: + contents: write + id-token: write + attestations: write steps: - name: Docker Hub login uses: docker/login-action@v3.5.0 @@ -197,3 +203,38 @@ jobs: echo "Inspecting 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 + diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index e1a9fe4..a0c1207 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -56,6 +56,24 @@ jobs: - name: Checkout 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 id: notes shell: bash @@ -84,5 +102,8 @@ jobs: tag_name: ${{ steps.notes.outputs.version }} name: ${{ steps.notes.outputs.version }} body_path: ${{ steps.notes.outputs.notes_file }} + files: | + sbom-source.spdx.json + sbom-source.cyclonedx.json env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f1194e2..4da5b1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. ## [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 ### Added diff --git a/README.md b/README.md index aacd8a4..a063657 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ A web-first Commander/EDH deckbuilder with a shared core for CLI, headless, and - [Development setup](#development-setup) - [Troubleshooting](#troubleshooting) - [Contributing](#contributing) +- [Supply chain security](#supply-chain-security) - [License & attribution](#license--attribution) - [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 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). diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index cb3b04a..d63bc20 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -1,9 +1,6 @@ # MTG Python Deckbuilder ## [Unreleased] -_No unreleased changes yet._ - -## [4.5.2] - 2026-04-01 ### 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.