name: Publish
on:
push:
tags: ["v*"]
workflow_dispatch:
permissions:
contents: read # Default read-only for checkout
jobs:
publish:
name: Publish Alpine with SBOM & Provenance
runs-on: ubuntu-latest
permissions:
contents: write # For uploading release assets
id-token: write # For signing attestations
packages: write # For pushing to registries
attestations: write # For attestations
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Determine image tag: use git tag ref if available, else read from pyproject.toml
- name: Determine version tag
id: version
run: |
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
else
VERSION=$(grep '^version' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
fi
# Run Dagger checks and tests first
#- name: Run checks and tests
# uses: dagger/dagger-for-github@d913e70051faf3b907d4dd96ef1161083c88c644 # v8.2.0
# with:
# verb: call
# args: checks
# cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}
# version: "latest"
# Set up Docker Buildx (required for attestations)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_SECRET }}
# Build and push with native BuildKit attestations
- name: Build and push Alpine with attestations
id: build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
context: .
file: .docker/ra-mcp.dockerfile
push: true
tags: |
riksarkivet/ra-mcp:${{ steps.version.outputs.tag }}
riksarkivet/ra-mcp:latest
build-args: |
BASE_IMAGE=python:3.13-alpine
platforms: linux/amd64,linux/arm64
sbom: true # ✅ Generate SBOM attestation
provenance: mode=max # ✅ Generate SLSA provenance (max detail)
cache-from: type=gha
cache-to: type=gha,mode=max
# Generate SBOM locally using Dagger (for Scorecard compliance)
- name: Generate standalone SBOM
if: startsWith(github.ref, 'refs/tags/v')
uses: dagger/dagger-for-github@d913e70051faf3b907d4dd96ef1161083c88c644 # v8.2.0
with:
verb: call
args: "generate-sbom --base-image python:3.13-alpine --format spdx-json --source . export --path ./sbom.spdx.json"
cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}
version: "latest"
# Extract real provenance attestation from published image using Dagger
- name: Extract provenance from image
if: startsWith(github.ref, 'refs/tags/v')
uses: dagger/dagger-for-github@d913e70051faf3b907d4dd96ef1161083c88c644 # v8.2.0
with:
verb: call
args: "extract-provenance-attestation --image-ref riksarkivet/ra-mcp:${{ steps.version.outputs.tag }} export --path ./provenance.intoto.jsonl"
cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}
version: "latest"
# Upload attestations as release assets (for OpenSSF Scorecard compliance)
- name: Upload attestations as release assets
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
with:
files: |
./sbom.spdx.json
./provenance.intoto.jsonl
# Install Cosign for signing
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
# Sign the container images with keyless signing
- name: Sign container images
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
echo "Signing riksarkivet/ra-mcp@${DIGEST}"
cosign sign --yes riksarkivet/ra-mcp@${DIGEST}
# Publish to PyPI
#- name: Publish to PyPI
# uses: dagger/dagger-for-github@d913e70051faf3b907d4dd96ef1161083c88c644 # v8.2.0
# with:
# verb: call
# args: "publish-pypi --pypi-token env:PYPI_TOKEN --source ."
# cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }}
# version: "latest"
# env:
# PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}