name: Release

on:
  push:
    tags: ['v*']

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: >-
    -D warnings
    --remap-path-prefix /home/runner/work=src
    --remap-path-prefix /home/runner/.cargo/registry/src=crate
    --remap-path-prefix /home/runner/.rustup=rustup
  RUST_TOOLCHAIN: "1.93"
  SOURCE_DATE_EPOCH: "0"
  CARGO_INCREMENTAL: "0"

permissions:
  contents: read

jobs:
  build:
    name: Build Release (${{ matrix.target }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
          - target: x86_64-unknown-linux-musl
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}
      - name: Install musl-tools
        if: contains(matrix.target, 'musl')
        run: sudo apt-get update -qq && sudo apt-get install -y -qq musl-tools
      - name: Clean before release build
        run: cargo clean
      - name: Build release binaries
        run: cargo build --release --workspace --target ${{ matrix.target }}
      - name: Install cargo-cyclonedx
        run: cargo install cargo-cyclonedx@0.5.7 --locked
      - name: Generate SBOM
        run: |
          cargo cyclonedx --manifest-path crates/karapace-cli/Cargo.toml --format json --override-filename karapace_bom
          mv crates/karapace-cli/karapace_bom.json .
      - name: Compute and verify checksums
        run: |
          cd target/${{ matrix.target }}/release
          sha256sum karapace karapace-dbus > SHA256SUMS
          sha256sum -c SHA256SUMS
      - name: Validate SBOM
        run: |
          python3 -c "
          import json
          with open('karapace_bom.json') as f:
              bom = json.load(f)
          assert 'components' in bom and len(bom['components']) > 0
          print(f'SBOM valid: {len(bom["components"])} components')
          "
      - name: Verify static linking (musl only)
        if: contains(matrix.target, 'musl')
        run: |
          set -euo pipefail
          for bin in karapace karapace-dbus; do
            if ldd target/${{ matrix.target }}/release/$bin 2>&1 | grep -q 'not a dynamic executable'; then
              echo "PASS: $bin is statically linked"
            else
              echo "FAIL: $bin is NOT statically linked"
              ldd target/${{ matrix.target }}/release/$bin
              exit 1
            fi
          done
      - uses: actions/upload-artifact@v4
        with:
          name: release-${{ matrix.target }}
          path: |
            target/${{ matrix.target }}/release/karapace
            target/${{ matrix.target }}/release/karapace-dbus
            target/${{ matrix.target }}/release/SHA256SUMS

  sign:
    name: Sign Release (${{ matrix.target }})
    runs-on: ubuntu-latest
    needs: [build]
    permissions:
      id-token: write
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            suffix: gnu
          - target: x86_64-unknown-linux-musl
            suffix: musl
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: release-${{ matrix.target }}
          path: artifacts
      - uses: sigstore/cosign-installer@v3
      - name: Sign binaries
        run: |
          cosign sign-blob --yes \
            artifacts/target/${{ matrix.target }}/release/karapace \
            --output-signature artifacts/karapace-${{ matrix.suffix }}.sig \
            --output-certificate artifacts/karapace-${{ matrix.suffix }}.crt
          cosign sign-blob --yes \
            artifacts/target/${{ matrix.target }}/release/karapace-dbus \
            --output-signature artifacts/karapace-dbus-${{ matrix.suffix }}.sig \
            --output-certificate artifacts/karapace-dbus-${{ matrix.suffix }}.crt
      - name: Generate and sign provenance attestation
        run: |
          cat > artifacts/provenance-${{ matrix.suffix }}.json << EOF
          {
            "_type": "https://in-toto.io/Statement/v0.1",
            "predicateType": "https://slsa.dev/provenance/v0.2",
            "subject": [
              {
                "name": "karapace-${{ matrix.suffix }}",
                "digest": {
                  "sha256": "$(sha256sum artifacts/target/${{ matrix.target }}/release/karapace | awk '{print $1}')"
                }
              },
              {
                "name": "karapace-dbus-${{ matrix.suffix }}",
                "digest": {
                  "sha256": "$(sha256sum artifacts/target/${{ matrix.target }}/release/karapace-dbus | awk '{print $1}')"
                }
              }
            ],
            "predicate": {
              "builder": {
                "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
              },
              "buildType": "https://github.com/actions/runner",
              "invocation": {
                "configSource": {
                  "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
                  "digest": {
                    "sha1": "${{ github.sha }}"
                  },
                  "entryPoint": ".github/workflows/release.yml"
                }
              },
              "metadata": {
                "buildInvocationId": "${{ github.run_id }}",
                "completeness": {
                  "parameters": true,
                  "environment": true,
                  "materials": true
                },
                "reproducible": true
              },
              "materials": [
                {
                  "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
                  "digest": {
                    "sha1": "${{ github.sha }}"
                  }
                }
              ]
            }
          }
          EOF
          cosign sign-blob --yes \
            artifacts/provenance-${{ matrix.suffix }}.json \
            --output-signature artifacts/provenance-${{ matrix.suffix }}.json.sig \
            --output-certificate artifacts/provenance-${{ matrix.suffix }}.json.crt
      - uses: actions/upload-artifact@v4
        with:
          name: signatures-${{ matrix.suffix }}
          path: |
            artifacts/*.sig
            artifacts/*.crt
            artifacts/provenance-${{ matrix.suffix }}.json

  verify:
    name: Verify Release (${{ matrix.target }})
    runs-on: ubuntu-latest
    needs: [sign]
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            suffix: gnu
          - target: x86_64-unknown-linux-musl
            suffix: musl
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: release-${{ matrix.target }}
          path: release
      - uses: actions/download-artifact@v4
        with:
          name: signatures-${{ matrix.suffix }}
          path: sigs
      - uses: sigstore/cosign-installer@v3
      - name: Verify checksums
        run: |
          cd release/target/${{ matrix.target }}/release
          sha256sum -c SHA256SUMS
      - name: Verify binary signatures
        run: |
          cosign verify-blob release/target/${{ matrix.target }}/release/karapace \
            --signature sigs/artifacts/karapace-${{ matrix.suffix }}.sig \
            --certificate sigs/artifacts/karapace-${{ matrix.suffix }}.crt \
            --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \
            --certificate-oidc-issuer https://token.actions.githubusercontent.com
          cosign verify-blob release/target/${{ matrix.target }}/release/karapace-dbus \
            --signature sigs/artifacts/karapace-dbus-${{ matrix.suffix }}.sig \
            --certificate sigs/artifacts/karapace-dbus-${{ matrix.suffix }}.crt \
            --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \
            --certificate-oidc-issuer https://token.actions.githubusercontent.com
      - name: Verify provenance attestation
        run: |
          cosign verify-blob sigs/artifacts/provenance-${{ matrix.suffix }}.json \
            --signature sigs/artifacts/provenance-${{ matrix.suffix }}.json.sig \
            --certificate sigs/artifacts/provenance-${{ matrix.suffix }}.json.crt \
            --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \
            --certificate-oidc-issuer https://token.actions.githubusercontent.com

  publish:
    name: Publish GitHub Release
    runs-on: ubuntu-latest
    needs: [verify]
    permissions:
      contents: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: release-x86_64-unknown-linux-gnu
          path: release-gnu
      - uses: actions/download-artifact@v4
        with:
          name: release-x86_64-unknown-linux-musl
          path: release-musl
      - uses: actions/download-artifact@v4
        with:
          name: signatures-gnu
          path: sigs-gnu
      - uses: actions/download-artifact@v4
        with:
          name: signatures-musl
          path: sigs-musl
      - name: Stage release files
        run: |
          mkdir -p dist
          # GNU binaries (renamed with -gnu suffix)
          cp release-gnu/target/x86_64-unknown-linux-gnu/release/karapace dist/karapace-linux-x86_64-gnu
          cp release-gnu/target/x86_64-unknown-linux-gnu/release/karapace-dbus dist/karapace-dbus-linux-x86_64-gnu
          cp release-gnu/target/x86_64-unknown-linux-gnu/release/SHA256SUMS dist/SHA256SUMS-gnu
          # Musl binaries (renamed with -musl suffix)
          cp release-musl/target/x86_64-unknown-linux-musl/release/karapace dist/karapace-linux-x86_64-musl
          cp release-musl/target/x86_64-unknown-linux-musl/release/karapace-dbus dist/karapace-dbus-linux-x86_64-musl
          cp release-musl/target/x86_64-unknown-linux-musl/release/SHA256SUMS dist/SHA256SUMS-musl
          # Signatures
          cp sigs-gnu/artifacts/*.sig sigs-gnu/artifacts/*.crt dist/
          cp sigs-gnu/artifacts/provenance-gnu.json dist/
          cp sigs-musl/artifacts/*.sig sigs-musl/artifacts/*.crt dist/
          cp sigs-musl/artifacts/provenance-musl.json dist/
      - name: Create release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*
