name: Release to Docker Hub

on:
  push:
    tags:
      - "v*.*.*" # stable
      - "v*.*.*-*" # pre-release

env:
  IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}
  IS_PRERELEASE: ${{ contains(github.ref_name, '-') }}

jobs:
  build-and-push:
    name: Build & Push Docker Image
    runs-on: ubuntu-latest

    permissions:
      contents: write
      id-token: write
      attestations: write

    outputs:
      digest: ${{ steps.push.outputs.digest }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      # Extract metadata (tags, labels) for Docker
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE_NAME }}
          flavor: |
            latest=${{ env.IS_PRERELEASE == 'false' }}
            prefix=v,onlatest=false
          tags: |
            type=semver,pattern={{version}},enable=${{ env.IS_PRERELEASE == 'false' }}
            type=semver,pattern={{major}}.{{minor}},enable=${{ env.IS_PRERELEASE == 'false' }}
            type=semver,pattern={{major}},enable=${{ env.IS_PRERELEASE == 'false' }}

            type=semver,pattern={{version}},enable=${{ env.IS_PRERELEASE == 'true' }}
            type=raw,value=${{ contains(github.ref_name, '-rc') && 'rc' || contains(github.ref_name, '-beta') && 'beta' || contains(github.ref_name, '-alpha') && 'alpha' || 'pre' }},enable=${{ env.IS_PRERELEASE == 'true' }}
            type=raw,value=edge,enable=${{ env.IS_PRERELEASE == 'true' }}
          labels: |
            org.opencontainers.image.title=${{ github.event.repository.name }}
            org.opencontainers.image.description=${{ github.event.repository.description }}
            org.opencontainers.image.url=${{ github.event.repository.html_url }}
            org.opencontainers.image.source=${{ github.event.repository.clone_url }}
            org.opencontainers.image.revision=${{ github.sha }}

      # Log in to Docker Hub
      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # Set up QEMU for multi-platform builds
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      # Set up Docker Buildx for multi-platform support
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Build and push Docker image
      - name: Build and push Docker image
        id: push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            VITE_APP_VERSION=${{ github.ref_name }}
          # Generate SBOMs for each platform and attach to the image
          sbom: true
          # Also generate provenance attestation via buildkit
          provenance: mode=max
          cache-from: type=gha
          cache-to: type=gha,mode=max

      # Attest build provenance via GitHub's attestation store
      - name: Attest build provenance
        uses: actions/attest-build-provenance@v1
        with:
          subject-name: docker.io/${{ env.IMAGE_NAME }}
          subject-digest: ${{ steps.push.outputs.digest }}
          push-to-registry: true

      # Generate an SBOM with Anchore Syft and attach it as a GitHub attestation
      - name: Generate SBOM (Syft)
        uses: anchore/sbom-action@v0
        id: sbom
        with:
          image: docker.io/${{ env.IMAGE_NAME }}@${{ steps.push.outputs.digest }}
          format: spdx-json
          output-file: sbom.spdx.json
          upload-artifact: true
          upload-artifact-retention: 90

      # Attest the SBOM so consumers can verify it was produced by this workflow
      - name: Attest SBOM
        uses: actions/attest-sbom@v1
        with:
          subject-name: docker.io/${{ env.IMAGE_NAME }}
          subject-digest: ${{ steps.push.outputs.digest }}
          sbom-path: sbom.spdx.json
          push-to-registry: true

  create-github-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: build-and-push

    permissions:
      contents: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # Retrieve the SBOM artifact produced in the previous job
      - name: Download SBOM artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ github.event.repository.name }}-${{ github.ref_name }}.spdx.json
          path: release-assets/
        continue-on-error: true

      # Auto-generate a changelog from commits since the last tag
      - name: Generate changelog
        id: changelog
        uses: orhun/git-cliff-action@v4
        with:
          args: --latest --strip header
        continue-on-error: true

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.ref_name }}
          name: ${{ env.IS_PRERELEASE == 'true' && format('Pre-release {0}', github.ref_name) || format('Release {0}', github.ref_name) }}
          body: |
            ${{ env.IS_PRERELEASE == 'true' && '> ⚠️ **This is a pre-release.** It may be unstable. Do not use in production without testing.' || '' }}

            ## Docker image

            ```
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.ref_name }}
            ```

            ${{ env.IS_PRERELEASE == 'true' && format('You can also pull the rolling pre-release channel tag:\n```\ndocker pull {0}/{1}:{2}\n```', secrets.DOCKERHUB_USERNAME, github.event.repository.name, contains(github.ref_name, '-rc') && 'rc' || contains(github.ref_name, '-beta') && 'beta' || contains(github.ref_name, '-alpha') && 'alpha' || 'edge') || '' }}

            ## Supply chain security

            This release includes:
            - **Build provenance attestation** — verifiable record of how and where the image was built
            - **SBOM** (Software Bill of Materials) — full inventory of packages inside the image

            Verify with the GitHub CLI:
            ```
            gh attestation verify oci://docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.ref_name }} \
              --repo ${{ github.repository }}
            ```

            Or scan for vulnerabilities using [Grype](https://github.com/anchore/grype):
            ```
            grype ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.ref_name }}
            ```

            ${{ steps.changelog.outputs.content }}
          files: release-assets/**
          draft: false
          prerelease: ${{ env.IS_PRERELEASE == 'true' }}
          make_latest: ${{ env.IS_PRERELEASE == 'false' }}
