name: Draft release

on:
  workflow_dispatch:
    inputs:
      versionName:
        type: string
        description: "Version to use for the release (e.g. v1.2.3)"
        required: true
      ref:
        type: string
        description: "Git ref to checkout"
        required: false
      pushContainers:
        type: boolean
        description: "Push containers pinned in the cli to container registry"
        required: false
        default: false
      registry:
        description: "Container registry to use"
        type: string
        default: ghcr.io
      key:
        description: "Key to use for signing. Set to 'release' to use release key, set to 'dev' to use the dev key."
        type: string
        required: true
  workflow_call:
    inputs:
      versionName:
        type: string
        description: "Version to use for the release (e.g. v1.2.3)"
        required: true
      ref:
        type: string
        description: "Git ref to checkout"
        required: true
      pushContainers:
        type: boolean
        description: "Push containers pinned in the cli to container registry"
        required: false
        default: false
      registry:
        description: "Container registry to use"
        type: string
        default: ghcr.io
      key:
        description: "Key to use for signing. Set to 'release' to use release key, set to 'dev' to use the dev key."
        type: string
        required: true

jobs:
  build-cli:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: amd64
            os: linux

          - arch: amd64
            os: darwin

          - arch: amd64
            os: windows

          - arch: arm64
            os: linux

          - arch: arm64
            os: darwin
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Setup bazel
        uses: ./.github/actions/setup_bazel_nix
        with:
          useCache: "false"

      - name: Build CLI
        uses: ./.github/actions/build_cli
        with:
          targetOS: ${{ matrix.os }}
          targetArch: ${{ matrix.arch }}
          enterpriseCLI: true
          cosignPublicKey: ${{ inputs.key == 'release' && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}
          cosignPrivateKey: ${{ inputs.key == 'release' && secrets.COSIGN_PRIVATE_KEY || secrets.COSIGN_DEV_PRIVATE_KEY }}
          cosignPassword: ${{ inputs.key == 'release' && secrets.COSIGN_PASSWORD || secrets.COSIGN_DEV_PASSWORD }}

      - name: Upload CLI as artifact (unix)
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        if : ${{ matrix.os != 'windows' }}
        with:
          name: constellation-${{ matrix.os }}-${{ matrix.arch }}
          path: |
            build/constellation-${{ matrix.os }}-${{ matrix.arch }}
            build/constellation-${{ matrix.os }}-${{ matrix.arch }}.sig

      - name: Upload CLI as artifact (windows)
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        if : ${{ matrix.os == 'windows' }}
        with:
          name: constellation-${{ matrix.os }}-${{ matrix.arch }}
          path: |
            build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe
            build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe.sig

  build-terraform-provider:
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: amd64
            os: linux

          - arch: amd64
            os: darwin

          # No Windows release until we have a test suite for it
          #- arch: amd64
          #  os: windows

          - arch: arm64
            os: linux

          - arch: arm64
            os: darwin
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Setup bazel
        uses: ./.github/actions/setup_bazel_nix
        with:
          useCache: "false"

      - name: Build Terraform Provider Binary
        uses: ./.github/actions/build_tf_provider
        with:
          targetOS: ${{ matrix.os }}
          targetArch: ${{ matrix.arch }}

      - name: Upload Terraform Provider Binary as artifact (unix)
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        if : ${{ matrix.os != 'windows' }}
        with:
          name: terraform-provider-constellation-${{ matrix.os }}-${{ matrix.arch }}
          path: |
            build/terraform-provider-constellation-${{ matrix.os }}-${{ matrix.arch }}

      - name: Upload Terraform Provider Binary as artifact (windows)
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        if : ${{ matrix.os == 'windows' }}
        with:
          name: terraform-provider-constellation-${{ matrix.os }}-${{ matrix.arch }}
          path: |
            build/terraform-provider-constellation-${{ matrix.os }}-${{ matrix.arch }}.exe

  upload-terraform-module:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Upload Terraform infrastructure module
        uses: ./.github/actions/upload_terraform_module

  push-containers:
    runs-on: ubuntu-22.04
    if: inputs.pushContainers
    permissions:
      actions: read
      contents: write
      id-token: write
      packages: write
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Setup bazel
        uses: ./.github/actions/setup_bazel_nix
        with:
          useCache: "false"

      - name: Log in to the Container registry
        uses: ./.github/actions/container_registry_login
        with:
          registry: ${{ inputs.registry }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload referenced container images
        shell: bash
        run: bazel run //bazel/release:push

  provenance-subjects:
    runs-on: ubuntu-22.04
    needs:
      - build-cli
      - signed-sbom
      - upload-terraform-module
      - build-terraform-provider
    outputs:
      provenance-subjects: ${{ steps.provenance-subjects.outputs.provenance-subjects }}
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Download release binaries
        uses: ./.github/actions/download_release_binaries

      - name: Download CLI SBOM
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: constellation.spdx.sbom

      - name: Generate provenance subjects
        id: provenance-subjects
        run: |
          HASHES=$(sha256sum \
            constellation-darwin-amd64 \
            constellation-darwin-arm64 \
            constellation-linux-amd64 \
            constellation-linux-arm64 \
            constellation-windows-amd64.exe \
            constellation.spdx.sbom \
            terraform-module.zip \
            terraform-provider-constellation-darwin-amd64 \
            terraform-provider-constellation-darwin-arm64 \
            terraform-provider-constellation-linux-amd64 \
            terraform-provider-constellation-linux-arm64)
          # terraform-provider-constellation-windows-amd64.exe)
          HASHESB64=$(echo "${HASHES}" | base64 -w0)
          echo "${HASHES}"
          echo "${HASHESB64}"
          echo provenance-subjects="${HASHESB64}" >> "$GITHUB_OUTPUT"

  signed-sbom:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Install Cosign
        uses: sigstore/cosign-installer@c85d0e205a72a294fe064f618a87dbac13084086 # v2.8.1

      - name: Download Syft & Grype
        uses: ./.github/actions/install_syft_grype

      - name: Setup bazel
        uses: ./.github/actions/setup_bazel_nix
        with:
          useCache: "false"

      # Build one CLI since Syft's go-module catalog will default to binary parsing.
      # Binary parsing has the advantage that it will not include other dependencies from our repo not included in the CLI.
      # This seems to work fine for one OS & one arch as long as we don't have OS specific imports.
      # Luckily, so far this does not seem to be the case.
      # As of v2.4.0, all SBOMs seem to have the same packages for [linux|darwin] & [amd64|arm64].
      # If this changes, this should be split up into multiple builds & multiple SBOMs.
      - name: Build CLI (amd64, linux)
        uses: ./.github/actions/build_cli
        with:
          targetOS: "linux"
          targetArch: "amd64"
          enterpriseCLI: true

      - name: Build signed SBOM
        run: |
          syft build/constellation-linux-amd64 --catalogers go-module --file constellation.spdx.sbom -o spdx-json
          cosign sign-blob --key env://COSIGN_PRIVATE_KEY constellation.spdx.sbom > constellation.spdx.sbom.sig
          grype constellation.spdx.sbom --fail-on high --only-fixed --add-cpes-if-none
        env:
          COSIGN_EXPERIMENTAL: 1
          COSIGN_PUBLIC_KEY: ${{ inputs.key == 'release' && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}
          COSIGN_PRIVATE_KEY: ${{ inputs.key == 'release' && secrets.COSIGN_PRIVATE_KEY || secrets.COSIGN_DEV_PRIVATE_KEY }}
          COSIGN_PASSWORD: ${{ inputs.key == 'release' && secrets.COSIGN_PASSWORD || secrets.COSIGN_DEV_PASSWORD }}

      - name: Upload Constellation CLI SBOM
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        with:
          name: constellation.spdx.sbom
          path: constellation.spdx.sbom

      - name: Upload Constellation CLI SBOM's signature
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
        with:
          name: constellation.spdx.sbom.sig
          path: constellation.spdx.sbom.sig

  provenance:
    permissions:
      actions: read
      contents: write
      id-token: write
    needs:
      - provenance-subjects
    # This must not be pinned to digest. See:
    # https://github.com/slsa-framework/slsa-github-generator#referencing-slsa-builders-and-generators
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
    with:
      base64-subjects: "${{ needs.provenance-subjects.outputs.provenance-subjects }}"

  provenance-verify:
    runs-on: ubuntu-22.04
    env:
      SLSA_VERIFIER_VERSION: "2.0.1"
    needs:
      - build-cli
      - provenance
      - upload-terraform-module
      - build-terraform-provider
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Download release binaries
        uses: ./.github/actions/download_release_binaries

      - name: Download CLI SBOM
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: constellation.spdx.sbom

      - name: Download provenance
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: ${{ needs.provenance.outputs.provenance-name }}

      - name: Install slsa-verifier
        run: |
          curl -fsSLO https://github.com/slsa-framework/slsa-verifier/releases/download/v${{ env.SLSA_VERIFIER_VERSION }}/slsa-verifier-linux-amd64
          install slsa-verifier-linux-amd64 /usr/local/bin/slsa-verifier

      - name: Verify provenance
        run: |
          slsa-verifier verify-artifact constellation-darwin-amd64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact constellation-darwin-arm64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact constellation-linux-amd64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact constellation-linux-arm64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact constellation-windows-amd64.exe \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation

          slsa-verifier verify-artifact terraform-provider-constellation-darwin-amd64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact terraform-provider-constellation-darwin-arm64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact terraform-provider-constellation-linux-amd64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact terraform-provider-constellation-linux-arm64 \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          #slsa-verifier verify-artifact terraform-provider-constellation-windows-amd64.exe \
          #  --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
          #  --source-uri github.com/edgelesssys/constellation

          slsa-verifier verify-artifact constellation.spdx.sbom \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation
          slsa-verifier verify-artifact terraform-module.zip \
            --provenance-path ${{ needs.provenance.outputs.provenance-name }} \
            --source-uri github.com/edgelesssys/constellation

  release:
    permissions:
      contents: write
    runs-on: ubuntu-22.04
    needs:
      - build-cli
      - provenance
      - signed-sbom
      - upload-terraform-module
      - build-terraform-provider
    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
        with:
          ref: ${{ inputs.ref || github.head_ref }}

      - name: Write cosign public key
        run: echo "$COSIGN_PUBLIC_KEY" > cosign.pub
        env:
          COSIGN_PUBLIC_KEY: ${{ inputs.key == 'release' && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}

      - name: Download binaries
        uses: ./.github/actions/download_release_binaries

      - name: Download CLI SBOM
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: constellation.spdx.sbom

      - name: Download Constellation CLI SBOM's signature
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: constellation.spdx.sbom.sig

      - name: Download Constellation provenance
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: ${{ needs.provenance.outputs.provenance-name }}

      - name: Rename provenance file
        run: |
          mv ${{ needs.provenance.outputs.provenance-name }} constellation.intoto.jsonl

      - name: Create Terraform provider release files
        run: |
          # Remove the "v" prefix from the version as required by the Terraform registry
          version="${{ inputs.versionName }}"
          version="${version#v}"

          # Create a zip file with the Terraform provider binaries
          for file in terraform-provider-constellation-*; do
            # Special case for Windows binaries: They need to keep the .exe extension
            ext="${file##*.}"
            distribution_arch="${file#terraform-provider-constellation-}"
            distribution_arch="${distribution_arch%.exe}"
            folder_name="terraform-provider-constellation_${version}_${distribution_arch//-/_}"

            mkdir -p "${folder_name}"
            if [[ "${ext}" = "exe" ]]; then
              cp "${file}" "${folder_name}/terraform-provider-constellation_v${version}.exe"
            else
              cp "${file}" "${folder_name}/terraform-provider-constellation_v${version}"
            fi
            zip -r "${folder_name}.zip" "${folder_name}"
            rm -r "${folder_name}"
          done

          # Create a manifest file for the Terraform provider
          echo '{"version":1,"metadata":{"protocol_versions":["6.0"]}}' > "terraform-provider-constellation_${version}_manifest.json"

          # Create a SHA256SUMS file of the zip files and manifest, and sign it
          shasum -a 256 "terraform-provider-constellation_${version}"* > "terraform-provider-constellation_${version}_SHA256SUMS"
          echo "${{ secrets.TERRAFORM_GPG_SIGNING_KEY }}" | gpg --import --batch --yes
          gpg -u 3C75E56351F8F3F6 --batch --yes --detach-sign "terraform-provider-constellation_${version}_SHA256SUMS"

      - name: Create release with artifacts
        id: create-release
        # GitHub endorsed release project. See: https://github.com/actions/create-release
        uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
        with:
          draft: true
          generate_release_notes: true
          tag_name: ${{ inputs.versionName || inputs.ref || github.head_ref }}
          target_commitish: ${{ inputs.ref }}
          files: |
            constellation-*
            cosign.pub
            constellation.spdx.sbom
            constellation.spdx.sbom.sig
            constellation.intoto.jsonl
            terraform-module.zip

      - name: Create Terraform provider release with artifcats
        uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
        with:
          draft: true
          generate_release_notes: false
          body: |
            This release contains the Terraform provider binaries for Constellation ${{ inputs.versionName }}.
            Check out [the release page](https://github.com/edgelesssys/constellation/releases/tag/${{ inputs.versionName }}) for more information and a full changelog.
          token: ${{ secrets.CI_GITHUB_REPOSITORY }}
          repository: edgelesssys/terraform-provider-constellation
          tag_name: ${{ inputs.versionName || inputs.ref || github.head_ref }}
          files: |
            terraform-provider-constellation_*
