# Copyright 2026 Zyvor · https://zyvor.dev
# SPDX-License-Identifier: Apache-2.0

# Publishes to ghcr.io/zyvorai/kairon-{controller,node,ui} on a version tag
# push -- the piece ci.yml's own container job deliberately doesn't do (see
# its comment). Before this workflow existed, nothing in this repository's
# CI published anywhere; the images at ghcr.io/zyvorai/kairon-* were
# produced by some out-of-repo process this repo couldn't vouch for. This
# is now the one place that mints an "official" kairon-* image tag.
name: release

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

permissions:
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      # Required for cosign's keyless signing below: GitHub mints a
      # short-lived OIDC token for the job, which cosign exchanges for a
      # Fulcio-issued signing certificate tied to this exact workflow/repo/
      # ref -- no private key for this project to generate, store, or
      # rotate.
      id-token: write
    strategy:
      matrix:
        target: [controller, node, ui, csi-node, csi-controller]
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # v7
      - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069  # v4
      - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f  # v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      # cosign-installer@v3's own verification logic unconditionally
      # expects a legacy detached .sig file, which cosign itself has fully
      # stopped publishing (confirmed against v3.1.3 -- this workflow's
      # previous explicit pin -- and v3.0.6, cosign-installer@v3's own
      # default: neither ships cosign-linux-amd64.sig anymore, only the
      # newer Sigstore-bundle .sigstore.json, so every download 404'd,
      # curl exit 22 -- confirmed the first time this workflow ever
      # actually ran, on v0.5.0's tag push). cosign-installer@v4's
      # verification logic was updated for the bundle format; no explicit
      # cosign-release override needed, its own default is a real,
      # correctly-verifiable cosign release.
      # Pinned to an exact tag, not a "@v4" moving major -- unlike @v3,
      # this action doesn't publish a moving major-version tag at all
      # (confirmed via the GitHub API: only exact vX.Y.Z tags exist).
      - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6  # v4.1.2
      # scan those exact bits, and only then `docker push` them -- not a
      # second build-and-push that could, even if only in principle,
      # produce something different from what was scanned.
      - name: Build ${{ matrix.target }} image
        uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc  # v7
        with:
          context: .
          target: ${{ matrix.target }}
          push: false
          load: true
          tags: |
            ghcr.io/zyvorai/kairon-${{ matrix.target }}:${{ github.ref_name }}
            ghcr.io/zyvorai/kairon-${{ matrix.target }}:latest
      - name: Scan ${{ matrix.target }} image
        uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25  # v0.36.0
        with:
          image-ref: ghcr.io/zyvorai/kairon-${{ matrix.target }}:${{ github.ref_name }}
          severity: HIGH,CRITICAL
          exit-code: '1'
          ignore-unfixed: true
      # Only reached if the scan above passed -- a vulnerable image is
      # never pushed, matching the failurePolicy: Fail posture used
      # elsewhere in this GA pass (fail the guarantee closed, not open).
      - name: Push ${{ matrix.target }} image
        id: push
        run: |
          docker push "ghcr.io/zyvorai/kairon-${{ matrix.target }}:${{ github.ref_name }}"
          docker push "ghcr.io/zyvorai/kairon-${{ matrix.target }}:latest"
          # Sign/attest by digest, not tag -- a tag is mutable, a digest
          # isn't. `docker inspect` reads back the digest docker push just
          # recorded locally for this tag, rather than re-parsing push's
          # own stdout.
          digest="$(docker inspect --format='{{index .RepoDigests 0}}' "ghcr.io/zyvorai/kairon-${{ matrix.target }}:${{ github.ref_name }}" | cut -d'@' -f2)"
          echo "digest=$digest" >> "$GITHUB_OUTPUT"
      - name: Generate SBOM for ${{ matrix.target }}
        uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26  # v0.24.2
        with:
          image: ghcr.io/zyvorai/kairon-${{ matrix.target }}@${{ steps.push.outputs.digest }}
          format: spdx-json
          output-file: sbom-${{ matrix.target }}.spdx.json
          upload-artifact: false
      - name: Sign image and attest SBOM for ${{ matrix.target }}
        run: |
          image="ghcr.io/zyvorai/kairon-${{ matrix.target }}@${{ steps.push.outputs.digest }}"
          cosign sign --yes "$image"
          cosign attest --yes --type spdxjson --predicate "sbom-${{ matrix.target }}.spdx.json" "$image"
      - name: Upload SBOM artifact for ${{ matrix.target }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a  # v7
        with:
          name: sbom-${{ matrix.target }}-${{ github.ref_name }}
          path: sbom-${{ matrix.target }}.spdx.json

  # CLI binaries, checksums, Helm OCI chart, and GitHub Release assets.
  release-assets:
    runs-on: ubuntu-latest
    needs: publish
    permissions:
      contents: write
      packages: write
      id-token: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # v7
      - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e  # v7
        with:
          go-version: '1.27.x'
      - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f  # v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310  # v5.0.1
      - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6  # v4.1.2
      - name: Build CLI binaries
        run: |
          set -euo pipefail
          mkdir -p dist
          version="${GITHUB_REF_NAME}"
          for os_arch in linux/amd64 linux/arm64; do
            os="${os_arch%/*}"
            arch="${os_arch#*/}"
            for cmd in kaironctl kubectl-kairon; do
              out="dist/${cmd}-${os}-${arch}"
              CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" go build -trimpath \
                -ldflags "-s -w -X main.version=${version}" \
                -o "$out" "./cmd/${cmd}"
            done
          done
          (
            cd dist
            sha256sum kaironctl-* kubectl-kairon-* > SHA256SUMS
          )
      - name: Package and push Helm chart to OCI
        run: |
          set -euo pipefail
          version="${GITHUB_REF_NAME#v}"
          # Keep chart version aligned with the git tag.
          sed -i "s/^version:.*/version: ${version}/" charts/kairon/Chart.yaml
          sed -i "s/^appVersion:.*/appVersion: \"${version}\"/" charts/kairon/Chart.yaml
          helm package charts/kairon -d dist
          helm push "dist/kairon-${version}.tgz" oci://ghcr.io/zyvorai/charts
      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          version="${GITHUB_REF_NAME}"
          chart_version="${version#v}"
          notes="$(cat <<EOF_NOTES
          ## ${version}

          ### Artifacts
          - \`kaironctl\` / \`kubectl-kairon\` (linux amd64/arm64) + \`SHA256SUMS\`
          - Container images: \`ghcr.io/zyvorai/kairon-{controller,node,ui,csi-node,csi-controller}:${version}\` (cosign-signed, SBOM attested)
          - Helm chart (OCI): \`oci://ghcr.io/zyvorai/charts/kairon:${chart_version}\`

          ### Upgrade / rollback
          \`\`\`bash
          helm upgrade kairon oci://ghcr.io/zyvorai/charts/kairon --version ${chart_version} -n kairon-system
          helm rollback kairon -n kairon-system
          \`\`\`

          Compatibility evidence: see \`docs/COMPATIBILITY.md\` in this tag.
          Production installs: \`-f values-production.yaml\`.
          EOF_NOTES
          )"
          # Strip leading indentation from heredoc for GitHub.
          notes="$(printf '%s\n' "$notes" | sed 's/^          //')"
          gh release create "$version" \
            dist/kaironctl-linux-amd64 \
            dist/kaironctl-linux-arm64 \
            dist/kubectl-kairon-linux-amd64 \
            dist/kubectl-kairon-linux-arm64 \
            dist/SHA256SUMS \
            "dist/kairon-${chart_version}.tgz" \
            --title "$version" \
            --notes "$notes"
