name: Buildx SBOM Demo
on:
  workflow_dispatch:
  push:
    branches: [ demo/buildx ]

jobs:
  buildx-sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Set up .NET 10 preview
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.0.x'

      - name: Publish StellaOps BuildX generator
        run: |
          dotnet publish src/StellaOps.Scanner.Sbomer.BuildXPlugin/StellaOps.Scanner.Sbomer.BuildXPlugin.csproj \
            -c Release \
            -o out/buildx

      - name: Handshake CAS
        run: |
          dotnet out/buildx/StellaOps.Scanner.Sbomer.BuildXPlugin.dll handshake \
            --manifest out/buildx \
            --cas out/cas

      - name: Build demo container image
        run: |
          docker buildx build --load -t stellaops/buildx-demo:ci samples/ci/buildx-demo

      - name: Capture image digest
        id: digest
        run: |
          DIGEST=$(docker image inspect stellaops/buildx-demo:ci --format '{{index .RepoDigests 0}}')
          echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"

      - name: Generate SBOM from built image
        run: |
          mkdir -p out
          docker sbom stellaops/buildx-demo:ci --format cyclonedx-json > out/buildx-sbom.cdx.json

      - name: Start mock Attestor
        id: attestor
        run: |
          mkdir -p out
          cat <<'PY' > out/mock-attestor.py
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length') or 0)
        body = self.rfile.read(length)
        with open(os.path.join('out', 'provenance-request.json'), 'wb') as fp:
            fp.write(body)
        self.send_response(202)
        self.end_headers()
        self.wfile.write(b'accepted')

    def log_message(self, format, *args):
        return

if __name__ == '__main__':
    server = HTTPServer(('127.0.0.1', 8085), Handler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()
PY
          touch out/provenance-request.json
          python3 out/mock-attestor.py &
          echo $! > out/mock-attestor.pid

      - name: Emit descriptor with provenance placeholder
        env:
          IMAGE_DIGEST: ${{ steps.digest.outputs.digest }}
          # Uncomment the next line and remove the mock Attestor block to hit a real service.
          # STELLAOPS_ATTESTOR_TOKEN: ${{ secrets.STELLAOPS_ATTESTOR_TOKEN }}
        run: |
          dotnet out/buildx/StellaOps.Scanner.Sbomer.BuildXPlugin.dll descriptor \
            --manifest out/buildx \
            --image "$IMAGE_DIGEST" \
            --sbom out/buildx-sbom.cdx.json \
            --sbom-name buildx-sbom.cdx.json \
            --artifact-type application/vnd.stellaops.sbom.layer+json \
            --sbom-format cyclonedx-json \
            --sbom-kind inventory \
            --repository ${{ github.repository }} \
            --build-ref ${{ github.sha }} \
            --attestor http://127.0.0.1:8085/provenance \
            > out/buildx-descriptor.json

      - name: Stop mock Attestor
        if: always()
        run: |
          if [ -f out/mock-attestor.pid ]; then
            kill $(cat out/mock-attestor.pid)
          fi

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: stellaops-buildx-demo
          path: |
            out/buildx-descriptor.json
            out/buildx-sbom.cdx.json
            out/provenance-request.json

      - name: Show descriptor summary
        run: |
          cat out/buildx-descriptor.json
